From 2f1f4166d17ecabe2ae0f97588f22004b422803b Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 12:12:10 +0000 Subject: [PATCH 01/50] chore: remove unused Bitbucket.Net.snk file --- Bitbucket.Net.snk | Bin 596 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 Bitbucket.Net.snk diff --git a/Bitbucket.Net.snk b/Bitbucket.Net.snk deleted file mode 100644 index a5e5104455d746a8e0e279988c7e30e7fb821dae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096=4R3(#Q?lK~eC2q=29%X~liKK#teY-X zwDhpKVF-QYQj}B=Ksd|I_)j&dE=QseF+~TS%^7SfW%@?O^0lBvUk6ZADZ1D(G2++? zW6!+V)cASuMC|Z=`Z9xEJERT3L9LR1qMH4X6*m{?q-}CFVX1B?fLE@USlmmy*9En+ z!zk*{pw4~<&2b8Ygg0hDMPQVZ&^F;n(ZECSr-I&Og!$(c4v7u8 zc5uWw`5FFyPtfcW-7Q%2C}U?LtVWOygXEVV+3}%uw0J*@2lvnoP(A*GVzV~R!&BA` z2FV)VBi`j{wH3L-P3|WH6`p=+W3a>h0+EK)y9C9|)DJ5G&=Ns}J4J0o;&F{0nK4K45)M*@i{nFcR}@{h!KWm>XI-P= zRA=)s6z`Z?EY{q+0-DIc6S)f()vJqp6f!r3p3&}ilAlh~U9^qj=cBCGm7 zB~pU!M9;sCW*HE^xB64s8N7|Et5*0Ic`JJ&w{QVW_+eSrAo~VKY<@epS6M8@>oRm` zHQ6Rc@DMV5(g+|x;FCI5;fI`BN;OxUA;znsASt^ZovnVh6xKl&1~@7-U1D~y)cM*Q iVH~5Ktr`QRAdY>@wptF^8y1VxHYX~#+&?O_@v&NMFCa<) From d42450828ff75fbf07d68fab6bc1f81403710195 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 12:19:43 +0000 Subject: [PATCH 02/50] chore: replace .sln with .slnx and update build tasks --- .vscode/tasks.json | 6 ++--- Bitbucket.Net.sln | 62 ---------------------------------------------- Bitbucket.Net.slnx | 24 ++++++++++++++++++ 3 files changed, 27 insertions(+), 65 deletions(-) delete mode 100644 Bitbucket.Net.sln create mode 100644 Bitbucket.Net.slnx diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 53fc0fb..a054b60 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -7,7 +7,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/Bitbucket.Net.sln", + "${workspaceFolder}/Bitbucket.Net.slnx", "/property:GenerateFullPaths=true", "/consoleloggerparameters:NoSummary;ForceNoAlign" ], @@ -23,7 +23,7 @@ "type": "process", "args": [ "build", - "${workspaceFolder}/Bitbucket.Net.sln", + "${workspaceFolder}/Bitbucket.Net.slnx", "-c", "Release", "/property:GenerateFullPaths=true", @@ -131,7 +131,7 @@ "type": "process", "args": [ "clean", - "${workspaceFolder}/Bitbucket.Net.sln" + "${workspaceFolder}/Bitbucket.Net.slnx" ], "problemMatcher": "$msCompile" }, diff --git a/Bitbucket.Net.sln b/Bitbucket.Net.sln deleted file mode 100644 index 28ea8dc..0000000 --- a/Bitbucket.Net.sln +++ /dev/null @@ -1,62 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28803.156 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bitbucket.Net", "src\Bitbucket.Net\Bitbucket.Net.csproj", "{51EBF9F3-7DFA-4C72-B38D-D07B1ED7FCEE}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{CBBC7722-A73C-4504-81D7-E1FB82B851A5}" - ProjectSection(SolutionItems) = preProject - build\build.ps1 = build\build.ps1 - build\test.ps1 = build\test.ps1 - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{778D1009-F274-4E3A-AC8C-D3B357F2DBE1}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - .gitignore = .gitignore - appveyor.yml = appveyor.yml - Bitbucket.Net.snk = Bitbucket.Net.snk - global.json = global.json - LICENSE = LICENSE - README.md = README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B7E00533-033F-48D3-A01C-40BD264F245A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bitbucket.Net.Tests", "test\Bitbucket.Net.Tests\Bitbucket.Net.Tests.csproj", "{7775DD13-F980-4838-8FE4-6E8B96221298}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Benchmarks", "Benchmarks", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bitbucket.Net.Benchmarks", "benchmarks\Bitbucket.Net.Benchmarks\Bitbucket.Net.Benchmarks.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {51EBF9F3-7DFA-4C72-B38D-D07B1ED7FCEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {51EBF9F3-7DFA-4C72-B38D-D07B1ED7FCEE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {51EBF9F3-7DFA-4C72-B38D-D07B1ED7FCEE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {51EBF9F3-7DFA-4C72-B38D-D07B1ED7FCEE}.Release|Any CPU.Build.0 = Release|Any CPU - {7775DD13-F980-4838-8FE4-6E8B96221298}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7775DD13-F980-4838-8FE4-6E8B96221298}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7775DD13-F980-4838-8FE4-6E8B96221298}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7775DD13-F980-4838-8FE4-6E8B96221298}.Release|Any CPU.Build.0 = Release|Any CPU - {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Release|Any CPU - {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Release|Any CPU - {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {7775DD13-F980-4838-8FE4-6E8B96221298} = {B7E00533-033F-48D3-A01C-40BD264F245A} - {B2C3D4E5-F6A7-8901-BCDE-F12345678901} = {A1B2C3D4-E5F6-7890-ABCD-EF1234567890} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {06DABD0E-1A16-4B84-9DF3-A1B8E73D18AF} - EndGlobalSection -EndGlobal diff --git a/Bitbucket.Net.slnx b/Bitbucket.Net.slnx new file mode 100644 index 0000000..f01e175 --- /dev/null +++ b/Bitbucket.Net.slnx @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + From b1437632ba03469adc8b0c75e721280e5abba025 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 12:47:13 +0000 Subject: [PATCH 03/50] chore: update .editorconfig with default Microsoft C# coding conventions and add VSCode settings to auto-format on save --- .editorconfig | 241 +++++++++++++++++++++++++++++++++++++++++- .vscode/settings.json | 8 ++ 2 files changed, 247 insertions(+), 2 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.editorconfig b/.editorconfig index d9f8964..e808360 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,241 @@ -[*.cs] +root = true -# IDE0055: Fix formatting +# C# files +[*.cs] + +#### Core EditorConfig Options #### + +# Indentation and spacing +indent_size = 4 +indent_style = space +tab_width = 4 + +# New line preferences +end_of_line = crlf +insert_final_newline = false + +#### .NET Coding Conventions #### + +# Organize usings +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# this. and Me. preferences +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Expression-level preferences +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = false # Override due to partial classes across folders +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Field preferences +dotnet_style_readonly_field = true + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:silent + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Expression-bodied members +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Pattern matching preferences +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Null-checking preferences +csharp_style_conditional_delegate_call = true + +# Modifier preferences +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_style_namespace_declarations = file_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_top_level_statements = true + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# 'using' directive preferences +csharp_using_directive_placement = outside_namespace + +# New line preferences +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### C# Formatting Rules #### + +# New line preferences +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Wrapping preferences +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### 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, 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.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.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 + +# ========================================================================== +# Diagnostic overrides +# ========================================================================== + +# IDE0055: Fix formatting — disabled (handled by dotnet format on demand) dotnet_diagnostic.IDE0055.severity = none + +# IDE0130: Namespace does not match folder structure — this codebase uses +# partial classes across folders, so namespace != folder is expected +dotnet_diagnostic.IDE0130.severity = none diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..8f25652 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "editor.formatOnSave": true, + "editor.formatOnType": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.fixAll": "explicit" + } +} From 24ee49bb3f0ef1b1c31da1b3e8bceed115e6459f Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 13:02:22 +0000 Subject: [PATCH 04/50] chore: update .editorconfig and VSCode settings for improved code style preferences --- .editorconfig | 20 ++++++++++++++------ .vscode/settings.json | 2 +- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/.editorconfig b/.editorconfig index e808360..01f4468 100644 --- a/.editorconfig +++ b/.editorconfig @@ -108,12 +108,12 @@ csharp_style_prefer_readonly_struct = true csharp_style_prefer_readonly_struct_member = true # Code-block preferences -csharp_prefer_braces = true -csharp_prefer_simple_using_statement = true -csharp_style_namespace_declarations = file_scoped -csharp_style_prefer_method_group_conversion = true -csharp_style_prefer_primary_constructors = true -csharp_style_prefer_top_level_statements = true +csharp_prefer_braces = true:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:warning +csharp_style_prefer_method_group_conversion = true:suggestion +csharp_style_prefer_primary_constructors = true:warning +csharp_style_prefer_top_level_statements = true:silent # Expression-level preferences csharp_prefer_simple_default_expression = true @@ -239,3 +239,11 @@ dotnet_diagnostic.IDE0055.severity = none # IDE0130: Namespace does not match folder structure — this codebase uses # partial classes across folders, so namespace != folder is expected dotnet_diagnostic.IDE0130.severity = none + +# Meziantou/CA rules that have no auto code-fix — suppress so dotnet format +# does not emit "Unable to fix" warnings +dotnet_diagnostic.MA0011.severity = none +dotnet_diagnostic.MA0015.severity = none +dotnet_diagnostic.MA0016.severity = none +dotnet_diagnostic.MA0048.severity = none +dotnet_diagnostic.CA2024.severity = none diff --git a/.vscode/settings.json b/.vscode/settings.json index 8f25652..02ea242 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "editor.formatOnSave": true, - "editor.formatOnType": true, + "editor.formatOnType": false, "editor.codeActionsOnSave": { "source.organizeImports": "explicit", "source.fixAll": "explicit" From 04e5e15a31a059038fa0b8a2b74d7669110b1934 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 13:18:27 +0000 Subject: [PATCH 05/50] chore: update .editorconfig and VSCode settings for code style preferences --- .editorconfig | 2 +- .vscode/settings.json | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.editorconfig b/.editorconfig index 01f4468..7c5f337 100644 --- a/.editorconfig +++ b/.editorconfig @@ -108,7 +108,7 @@ csharp_style_prefer_readonly_struct = true csharp_style_prefer_readonly_struct_member = true # Code-block preferences -csharp_prefer_braces = true:suggestion +csharp_prefer_braces = false:suggestion csharp_prefer_simple_using_statement = true:suggestion csharp_style_namespace_declarations = file_scoped:warning csharp_style_prefer_method_group_conversion = true:suggestion diff --git a/.vscode/settings.json b/.vscode/settings.json index 02ea242..8983790 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,8 +1,9 @@ { "editor.formatOnSave": true, + "editor.formatOnSaveMode": "modificationsIfAvailable", "editor.formatOnType": false, "editor.codeActionsOnSave": { - "source.organizeImports": "explicit", - "source.fixAll": "explicit" + "source.organizeImports": "always", + "source.fixAll": "always" } } From 547e1172acb7a964304bc911128a7d6013836402 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 13:19:04 +0000 Subject: [PATCH 06/50] chore: apply dotnet format to the codebase --- .../Config/BenchmarkConfig.cs | 10 +- .../Bitbucket.Net.Benchmarks/Program.cs | 2 +- .../Response/ResponseHandlingBenchmarks.cs | 10 +- .../Serialization/ColdStartBenchmarks.cs | 8 +- .../JsonSerializationBenchmarks.cs | 6 +- .../Serialization/SourceGenBenchmarks.cs | 8 +- .../Streaming/StreamingBenchmarks.cs | 14 +- .../ZeroCopy/ZeroCopyBenchmarks.cs | 8 +- src/Bitbucket.Net/Audit/BitbucketClient.cs | 101 +- src/Bitbucket.Net/BitbucketClient.cs | 499 +- src/Bitbucket.Net/Branches/BitbucketClient.cs | 120 +- src/Bitbucket.Net/Builds/BitbucketClient.cs | 102 +- .../CommentLikes/BitbucketClient.cs | 149 +- src/Bitbucket.Net/Common/BitbucketHelpers.cs | 1147 ++-- .../BlockerCommentStateConverter.cs | 27 +- .../Converters/CommentSeverityConverter.cs | 27 +- .../Common/Converters/FileTypesConverter.cs | 21 +- .../Common/Converters/HookTypesConverter.cs | 21 +- .../Common/Converters/JsonEnumConverter.cs | 147 +- .../Common/Converters/LineTypesConverter.cs | 21 +- .../Converters/ParticipantStatusConverter.cs | 21 +- .../Common/Converters/PermissionsConverter.cs | 21 +- .../Converters/PullRequestStatesConverter.cs | 21 +- .../RefRestrictionTypesConverter.cs | 21 +- .../Common/Converters/RolesConverter.cs | 21 +- .../Common/Converters/ScopeTypesConverter.cs | 21 +- .../Converters/SynchronizeActionsConverter.cs | 21 +- .../Converters/UnixDateTimeOffsetConverter.cs | 81 +- .../Converters/WebHookOutcomesConverter.cs | 21 +- .../Common/DynamicMultipartFormDataContent.cs | 43 +- .../Exceptions/BitbucketApiException.cs | 185 +- .../BitbucketAuthenticationException.cs | 53 +- .../BitbucketBadRequestException.cs | 53 +- .../Exceptions/BitbucketConflictException.cs | 53 +- .../Exceptions/BitbucketForbiddenException.cs | 53 +- .../Exceptions/BitbucketNotFoundException.cs | 53 +- .../Exceptions/BitbucketRateLimitException.cs | 53 +- .../Exceptions/BitbucketServerException.cs | 57 +- .../BitbucketValidationException.cs | 53 +- .../Common/FlurlRequestExtensions.cs | 25 +- .../Common/Mcp/DiffStreamingExtensions.cs | 552 +- src/Bitbucket.Net/Common/Mcp/McpExtensions.cs | 384 +- src/Bitbucket.Net/Common/Models/Error.cs | 35 +- .../Common/Models/ErrorResponse.cs | 19 +- .../Common/Models/PagedResults.cs | 35 +- .../Common/Models/PagedResultsBase.cs | 13 +- src/Bitbucket.Net/Common/TypeExtensions.cs | 11 +- .../Common/UnixDateTimeExtensions.cs | 24 +- .../Core/Admin/BitbucketClient.cs | 938 ++-- .../ApplicationProperties/BitbucketClient.cs | 27 +- .../Core/Dashboard/BitbucketClient.cs | 113 +- .../Core/Groups/BitbucketClient.cs | 52 +- .../Core/Hooks/BitbucketClient.cs | 29 +- .../Core/Inbox/BitbucketClient.cs | 86 +- .../Core/Logs/BitbucketClient.cs | 95 +- .../Core/Markup/BitbucketClient.cs | 66 +- .../Core/Profile/BitbucketClient.cs | 58 +- .../Core/Projects/BitbucketClient.cs | 4805 +++++++++-------- .../Core/Repos/BitbucketClient.cs | 123 +- .../Core/Tasks/BitbucketClient.cs | 83 +- .../Core/Users/BitbucketClient.cs | 176 +- .../Core/WhoAmI/BitbucketClient.cs | 117 +- .../DefaultReviewers/BitbucketClient.cs | 188 +- src/Bitbucket.Net/Git/BitbucketClient.cs | 85 +- src/Bitbucket.Net/Jira/BitbucketClient.cs | 96 +- src/Bitbucket.Net/Models/Audit/AuditEvent.cs | 17 +- .../Models/Branches/BranchModel.cs | 19 +- .../Models/Branches/BranchModelType.cs | 15 +- src/Bitbucket.Net/Models/Builds/BuildStats.cs | 15 +- .../Models/Builds/BuildStatus.cs | 17 +- src/Bitbucket.Net/Models/Builds/KeyedUrl.cs | 11 +- .../Models/Core/Admin/Address.cs | 11 +- .../Models/Core/Admin/Cluster.cs | 15 +- .../Models/Core/Admin/DeletableGroupOrUser.cs | 11 +- .../Models/Core/Admin/GroupPermission.cs | 17 +- .../Models/Core/Admin/GroupUsers.cs | 13 +- .../Models/Core/Admin/LicenseDetails.cs | 47 +- .../Models/Core/Admin/LicenseInfo.cs | 9 +- .../Models/Core/Admin/LicenseStatus.cs | 11 +- .../Core/Admin/MailServerConfiguration.cs | 24 +- .../Models/Core/Admin/MergeStrategies.cs | 15 +- .../Models/Core/Admin/MergeStrategy.cs | 17 +- src/Bitbucket.Net/Models/Core/Admin/Node.cs | 15 +- .../Models/Core/Admin/PasswordBasic.cs | 13 +- .../Models/Core/Admin/PasswordChange.cs | 11 +- .../Models/Core/Admin/Permissions.cs | 31 +- .../Models/Core/Admin/UserGroups.cs | 13 +- .../Models/Core/Admin/UserInfo.cs | 20 +- .../Models/Core/Admin/UserPermission.cs | 17 +- .../Models/Core/Admin/UserRename.cs | 13 +- .../Models/Core/Logs/LogLevels.cs | 19 +- .../Core/Projects/AheadBehindMetaData.cs | 13 +- .../Models/Core/Projects/AnchorStates.cs | 15 +- .../Models/Core/Projects/ArchiveFormats.cs | 17 +- .../Models/Core/Projects/Author.cs | 11 +- .../Models/Core/Projects/BlockerComment.cs | 163 +- .../Core/Projects/BlockerCommentState.cs | 27 +- .../Models/Core/Projects/Branch.cs | 101 +- .../Models/Core/Projects/BranchBase.cs | 13 +- .../Models/Core/Projects/BranchInfo.cs | 13 +- .../Models/Core/Projects/BranchMetaData.cs | 21 +- .../Models/Core/Projects/BranchOrderBy.cs | 11 +- .../Models/Core/Projects/BranchRef.cs | 9 +- .../Models/Core/Projects/BrowseItem.cs | 15 +- .../Models/Core/Projects/BrowsePathItem.cs | 15 +- .../Core/Projects/BuildStatusMetadata.cs | 13 +- .../Models/Core/Projects/Change.cs | 29 +- .../Models/Core/Projects/ChangeScopes.cs | 15 +- .../Models/Core/Projects/CloneLink.cs | 11 +- .../Models/Core/Projects/CloneLinks.cs | 11 +- .../Models/Core/Projects/Comment.cs | 83 +- .../Models/Core/Projects/CommentAnchor.cs | 25 +- .../Models/Core/Projects/CommentId.cs | 9 +- .../Models/Core/Projects/CommentInfo.cs | 15 +- .../Models/Core/Projects/CommentRef.cs | 35 +- .../Models/Core/Projects/CommentSeverity.cs | 27 +- .../Models/Core/Projects/CommentText.cs | 13 +- .../Models/Core/Projects/Commit.cs | 31 +- .../Models/Core/Projects/CommitParent.cs | 11 +- .../Models/Core/Projects/ContentItem.cs | 15 +- .../Models/Core/Projects/Diff.cs | 13 +- .../Models/Core/Projects/DiffHunk.cs | 19 +- .../Models/Core/Projects/DiffInfo.cs | 25 +- .../Models/Core/Projects/DiffTypes.cs | 15 +- .../Models/Core/Projects/Differences.cs | 11 +- .../Models/Core/Projects/FileTypes.cs | 13 +- .../Models/Core/Projects/FromToRef.cs | 51 +- .../Models/Core/Projects/Hook.cs | 17 +- .../Models/Core/Projects/HookDetails.cs | 25 +- .../Models/Core/Projects/HookScope.cs | 13 +- .../Models/Core/Projects/HookTypes.cs | 15 +- .../Models/Core/Projects/LastModified.cs | 13 +- .../Models/Core/Projects/LicensedUser.cs | 13 +- .../Models/Core/Projects/Line.cs | 9 +- .../Models/Core/Projects/LineRef.cs | 15 +- .../Models/Core/Projects/LineTypes.cs | 15 +- .../Models/Core/Projects/Link.cs | 11 +- .../Models/Core/Projects/Links.cs | 9 +- .../Core/Projects/MergeCheckRequiredBuilds.cs | 11 +- .../Models/Core/Projects/MergeCommits.cs | 15 +- .../Projects/MergeHookRequiredApprovers.cs | 11 +- .../Models/Core/Projects/Participant.cs | 23 +- .../Models/Core/Projects/ParticipantStatus.cs | 15 +- .../Models/Core/Projects/Path.cs | 101 +- .../Core/Projects/Permittedoperations.cs | 13 +- .../Models/Core/Projects/Project.cs | 19 +- .../Models/Core/Projects/ProjectDefinition.cs | 15 +- .../Models/Core/Projects/ProjectRef.cs | 11 +- .../Models/Core/Projects/Properties.cs | 9 +- .../Models/Core/Projects/PullRequest.cs | 31 +- .../Core/Projects/PullRequestActivity.cs | 29 +- .../Core/Projects/PullRequestDirections.cs | 11 +- .../Core/Projects/PullRequestFromTypes.cs | 13 +- .../Models/Core/Projects/PullRequestInfo.cs | 31 +- .../Core/Projects/PullRequestMergeState.cs | 17 +- .../Core/Projects/PullRequestMetadata.cs | 9 +- .../Models/Core/Projects/PullRequestOrders.cs | 15 +- .../Core/Projects/PullRequestSettings.cs | 27 +- .../Models/Core/Projects/PullRequestStates.cs | 15 +- .../Core/Projects/PullRequestSuggestion.cs | 23 +- .../Models/Core/Projects/PullRequestUpdate.cs | 19 +- src/Bitbucket.Net/Models/Core/Projects/Ref.cs | 13 +- .../Models/Core/Projects/RefChange.cs | 17 +- .../Models/Core/Projects/Repository.cs | 25 +- .../Models/Core/Projects/RepositoryFork.cs | 11 +- .../Models/Core/Projects/RepositoryOrigin.cs | 27 +- .../Models/Core/Projects/RepositoryRef.cs | 15 +- .../Models/Core/Projects/Reviewer.cs | 9 +- .../Models/Core/Projects/Roles.cs | 15 +- .../Models/Core/Projects/ScopeTypes.cs | 13 +- .../Models/Core/Projects/Segment.cs | 13 +- src/Bitbucket.Net/Models/Core/Projects/Tag.cs | 21 +- .../Models/Core/Projects/TimeWindow.cs | 15 +- .../Models/Core/Projects/VersionInfo.cs | 11 +- .../Models/Core/Projects/Veto.cs | 11 +- .../Models/Core/Projects/WebHook.cs | 31 +- .../Models/Core/Projects/WebHookInvocation.cs | 27 +- .../Models/Core/Projects/WebHookOutcomes.cs | 15 +- .../Models/Core/Projects/WebHookRequest.cs | 13 +- .../Models/Core/Projects/WebHookResult.cs | 15 +- .../Models/Core/Projects/WebHookStatistics.cs | 11 +- .../Core/Projects/WebHookStatisticsCounts.cs | 15 +- .../Core/Projects/WebHookStatisticsSummary.cs | 17 +- .../Core/Projects/WebHookTestRequest.cs | 11 +- .../Projects/WebHookTestRequestResponse.cs | 13 +- .../Core/Projects/WebHookTestResponse.cs | 13 +- .../Models/Core/Projects/WithId.cs | 11 +- .../Models/Core/Tasks/BitbucketTask.cs | 13 +- .../Models/Core/Tasks/BitbucketTaskCount.cs | 14 +- .../Models/Core/Tasks/TaskAnchor.cs | 21 +- .../Models/Core/Tasks/TaskBasicAnchor.cs | 11 +- .../Models/Core/Tasks/TaskInfo.cs | 13 +- .../Models/Core/Tasks/TaskRef.cs | 23 +- .../Models/Core/Users/Identity.cs | 9 +- src/Bitbucket.Net/Models/Core/Users/Named.cs | 13 +- .../Models/Core/Users/PasswordChange.cs | 11 +- src/Bitbucket.Net/Models/Core/Users/User.cs | 21 +- .../DefaultReviewerPullRequestCondition.cs | 25 +- ...efaultReviewerPullRequestConditionScope.cs | 11 +- ...DefaultReviewerPullRequestConditionType.cs | 11 +- .../Models/DefaultReviewers/RefMatcher.cs | 15 +- .../Models/Git/RebasePullRequestCondition.cs | 15 +- src/Bitbucket.Net/Models/Git/TagTypes.cs | 13 +- src/Bitbucket.Net/Models/Git/Veto.cs | 11 +- src/Bitbucket.Net/Models/Jira/ChangeSet.cs | 19 +- src/Bitbucket.Net/Models/Jira/Changes.cs | 21 +- src/Bitbucket.Net/Models/Jira/JiraIssue.cs | 13 +- .../PersonalAccessTokens/AccessToken.cs | 27 +- .../PersonalAccessTokens/AccessTokenCreate.cs | 15 +- .../PersonalAccessTokens/FullAccessToken.cs | 9 +- .../Models/RefRestrictions/AccessKey.cs | 9 +- .../Models/RefRestrictions/Key.cs | 13 +- .../Models/RefRestrictions/RefMatcherTypes.cs | 17 +- .../Models/RefRestrictions/RefRestriction.cs | 21 +- .../RefRestrictions/RefRestrictionBase.cs | 17 +- .../RefRestrictions/RefRestrictionCreate.cs | 13 +- .../RefRestrictions/RefRestrictionTypes.cs | 17 +- src/Bitbucket.Net/Models/RefSync/FullRef.cs | 11 +- .../RepositorySynchronizationStatus.cs | 25 +- .../Models/RefSync/Synchronize.cs | 17 +- .../Models/RefSync/SynchronizeActions.cs | 13 +- .../Models/RefSync/SynchronizeContext.cs | 9 +- src/Bitbucket.Net/Models/Ssh/Accesskeys.cs | 9 +- src/Bitbucket.Net/Models/Ssh/Fingerprint.cs | 11 +- src/Bitbucket.Net/Models/Ssh/KeyBase.cs | 13 +- src/Bitbucket.Net/Models/Ssh/ProjectKey.cs | 11 +- src/Bitbucket.Net/Models/Ssh/RepositoryKey.cs | 9 +- src/Bitbucket.Net/Models/Ssh/SshSettings.cs | 19 +- .../PersonalAccessTokens/BitbucketClient.cs | 114 +- .../RefRestrictions/BitbucketClient.cs | 269 +- src/Bitbucket.Net/RefSync/BitbucketClient.cs | 69 +- .../Serialization/BitbucketJsonContext.cs | 22 +- src/Bitbucket.Net/Ssh/BitbucketClient.cs | 470 +- .../Mcp/DiffStreamingExtensionsTests.cs | 431 +- .../Common/Mcp/McpExtensionsTests.cs | 517 +- .../Infrastructure/BitbucketMockFixture.cs | 2 +- .../Infrastructure/MockSetupExtensions.cs | 9 +- .../Infrastructure/TestConstants.cs | 2 +- .../MockTests/AdminExtendedMockTests.cs | 554 +- .../MockTests/AdminMockTests.cs | 300 +- .../MockTests/AdminPermissionsMockTests.cs | 98 +- .../ApplicationPropertiesMockTests.cs | 42 +- .../MockTests/AuditMockTests.cs | 78 +- .../MockTests/BranchExtendedMockTests.cs | 136 +- .../MockTests/BranchMockTests.cs | 158 +- .../MockTests/BuildExtendedMockTests.cs | 80 +- .../MockTests/BuildMockTests.cs | 104 +- .../MockTests/ChangesAndFilesMockTests.cs | 204 +- .../MockTests/CommentLikesMockTests.cs | 180 +- .../MockTests/CommitMockTests.cs | 92 +- .../MockTests/CoreExtendedMockTests.cs | 442 +- .../MockTests/DashboardMockTests.cs | 80 +- .../DefaultReviewersExtendedMockTests.cs | 258 +- .../MockTests/DefaultReviewersMockTests.cs | 98 +- .../MockTests/DiffAndTagMockTests.cs | 152 +- .../MockTests/ErrorHandlingMockTests.cs | 204 +- .../MockTests/GitAndTagMockTests.cs | 140 +- .../MockTests/GitMockTests.cs | 106 +- .../MockTests/GroupsMockTests.cs | 44 +- .../MockTests/HooksMockTests.cs | 66 +- .../MockTests/InboxMockTests.cs | 68 +- .../MockTests/JiraMockTests.cs | 104 +- .../MockTests/LogsMockTests.cs | 88 +- .../MockTests/MarkupMockTests.cs | 36 +- .../PersonalAccessTokensMockTests.cs | 148 +- .../MockTests/ProfileMockTests.cs | 42 +- .../MockTests/ProjectCrudMockTests.cs | 94 +- .../MockTests/ProjectMockTests.cs | 120 +- .../MockTests/ProjectPermissionsMockTests.cs | 320 +- .../MockTests/ProjectSettingsMockTests.cs | 232 +- .../MockTests/PullRequestActionsMockTests.cs | 146 +- .../MockTests/PullRequestBlockerMockTests.cs | 262 +- .../MockTests/PullRequestCommentMockTests.cs | 164 +- .../MockTests/PullRequestCrudMockTests.cs | 122 +- .../MockTests/PullRequestExtendedMockTests.cs | 384 +- .../MockTests/PullRequestMockTests.cs | 154 +- .../PullRequestParticipantsMockTests.cs | 164 +- .../MockTests/PullRequestWatchMockTests.cs | 208 +- .../MockTests/RefRestrictionsMockTests.cs | 332 +- .../MockTests/RefSyncMockTests.cs | 106 +- .../MockTests/RepositoryCrudMockTests.cs | 196 +- .../MockTests/RepositoryMockTests.cs | 116 +- .../RepositoryOperationsMockTests.cs | 308 +- .../RepositoryPermissionsMockTests.cs | 280 +- .../MockTests/SshKeyMockTests.cs | 522 +- .../MockTests/TasksMockTests.cs | 118 +- .../MockTests/UserMockTests.cs | 154 +- .../MockTests/UsersMockTests.cs | 182 +- .../MockTests/WebhookAndCompareMockTests.cs | 118 +- .../MockTests/WebhookMockTests.cs | 106 +- .../MockTests/WhoAmIMockTests.cs | 52 +- .../BitbucketClientConstructorTests.cs | 4 +- .../UnitTests/BitbucketHelpersTests.cs | 4 +- .../UnitTests/CommonModelTests.cs | 10 +- .../UnitTests/DiffStreamingExtensionsTests.cs | 6 +- .../UnitTests/ExceptionTests.cs | 22 +- .../UnitTests/JsonConverterTests.cs | 6 +- .../UnitTests/McpExtensionsTests.cs | 46 +- .../UnitTests/ModelSerializationTests.cs | 12 +- 299 files changed, 12616 insertions(+), 13073 deletions(-) diff --git a/benchmarks/Bitbucket.Net.Benchmarks/Config/BenchmarkConfig.cs b/benchmarks/Bitbucket.Net.Benchmarks/Config/BenchmarkConfig.cs index ed0699c..cac839f 100644 --- a/benchmarks/Bitbucket.Net.Benchmarks/Config/BenchmarkConfig.cs +++ b/benchmarks/Bitbucket.Net.Benchmarks/Config/BenchmarkConfig.cs @@ -72,20 +72,20 @@ public FullBenchmarkConfig() { AddJob(Job.Default); AddDiagnoser(MemoryDiagnoser.Default); - + AddColumn(StatisticColumn.Mean); AddColumn(StatisticColumn.StdErr); AddColumn(StatisticColumn.StdDev); AddColumn(StatisticColumn.Median); AddColumn(StatisticColumn.P95); AddColumn(StatisticColumn.OperationsPerSecond); - + AddExporter(MarkdownExporter.GitHub); AddExporter(HtmlExporter.Default); AddExporter(CsvExporter.Default); - + AddLogger(ConsoleLogger.Default); - + WithSummaryStyle(SummaryStyle.Default.WithRatioStyle(RatioStyle.Trend)); } -} +} \ No newline at end of file diff --git a/benchmarks/Bitbucket.Net.Benchmarks/Program.cs b/benchmarks/Bitbucket.Net.Benchmarks/Program.cs index 2483486..483cf4e 100644 --- a/benchmarks/Bitbucket.Net.Benchmarks/Program.cs +++ b/benchmarks/Bitbucket.Net.Benchmarks/Program.cs @@ -66,4 +66,4 @@ private static void RunAllBenchmarks() Console.WriteLine("All benchmarks completed!"); Console.WriteLine("Results are available in the BenchmarkDotNet.Artifacts folder."); } -} +} \ No newline at end of file diff --git a/benchmarks/Bitbucket.Net.Benchmarks/Response/ResponseHandlingBenchmarks.cs b/benchmarks/Bitbucket.Net.Benchmarks/Response/ResponseHandlingBenchmarks.cs index 9d0359e..e12f6d4 100644 --- a/benchmarks/Bitbucket.Net.Benchmarks/Response/ResponseHandlingBenchmarks.cs +++ b/benchmarks/Bitbucket.Net.Benchmarks/Response/ResponseHandlingBenchmarks.cs @@ -1,6 +1,6 @@ -using System.Text; using BenchmarkDotNet.Attributes; using Bitbucket.Net.Benchmarks.Config; +using System.Text; namespace Bitbucket.Net.Benchmarks.Response; @@ -57,7 +57,7 @@ public int ProcessLargeDiff() [Benchmark(Description = "Buffered diff processing (1000 lines)")] public List BufferedDiffProcessing() { - return _largeDiff.Split('\n').ToList(); + return [.. _largeDiff.Split('\n')]; } /// @@ -205,14 +205,14 @@ private static IEnumerable EnumerateLines(string content) { if (content[i] == '\n') { - yield return content.Substring(start, i - start); + yield return content[start..i]; start = i + 1; } } if (start < content.Length) { - yield return content.Substring(start); + yield return content[start..]; } } @@ -282,4 +282,4 @@ private static string GenerateFileContent(int lines) } #endregion -} +} \ No newline at end of file diff --git a/benchmarks/Bitbucket.Net.Benchmarks/Serialization/ColdStartBenchmarks.cs b/benchmarks/Bitbucket.Net.Benchmarks/Serialization/ColdStartBenchmarks.cs index b68fe68..18a64af 100644 --- a/benchmarks/Bitbucket.Net.Benchmarks/Serialization/ColdStartBenchmarks.cs +++ b/benchmarks/Bitbucket.Net.Benchmarks/Serialization/ColdStartBenchmarks.cs @@ -1,11 +1,11 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Engines; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; namespace Bitbucket.Net.Benchmarks.Serialization; @@ -164,4 +164,4 @@ private static PagedResults CreatePagedProjectsObject() Start = 0 }; } -} +} \ No newline at end of file diff --git a/benchmarks/Bitbucket.Net.Benchmarks/Serialization/JsonSerializationBenchmarks.cs b/benchmarks/Bitbucket.Net.Benchmarks/Serialization/JsonSerializationBenchmarks.cs index 66e37af..6259330 100644 --- a/benchmarks/Bitbucket.Net.Benchmarks/Serialization/JsonSerializationBenchmarks.cs +++ b/benchmarks/Bitbucket.Net.Benchmarks/Serialization/JsonSerializationBenchmarks.cs @@ -1,9 +1,9 @@ -using System.Text.Json; -using System.Text.Json.Serialization; using BenchmarkDotNet.Attributes; using Bitbucket.Net.Benchmarks.Config; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Projects; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Bitbucket.Net.Benchmarks.Serialization; @@ -307,4 +307,4 @@ private static string CreatePagedCommitsJson(int count) } #endregion -} +} \ No newline at end of file diff --git a/benchmarks/Bitbucket.Net.Benchmarks/Serialization/SourceGenBenchmarks.cs b/benchmarks/Bitbucket.Net.Benchmarks/Serialization/SourceGenBenchmarks.cs index bee70c1..7dd09ad 100644 --- a/benchmarks/Bitbucket.Net.Benchmarks/Serialization/SourceGenBenchmarks.cs +++ b/benchmarks/Bitbucket.Net.Benchmarks/Serialization/SourceGenBenchmarks.cs @@ -1,12 +1,12 @@ -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; using BenchmarkDotNet.Attributes; using Bitbucket.Net.Benchmarks.Config; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; namespace Bitbucket.Net.Benchmarks.Serialization; @@ -605,4 +605,4 @@ private static string CreatePagedCommitsJson(int count) } #endregion -} +} \ No newline at end of file diff --git a/benchmarks/Bitbucket.Net.Benchmarks/Streaming/StreamingBenchmarks.cs b/benchmarks/Bitbucket.Net.Benchmarks/Streaming/StreamingBenchmarks.cs index dde68df..a0c86c3 100644 --- a/benchmarks/Bitbucket.Net.Benchmarks/Streaming/StreamingBenchmarks.cs +++ b/benchmarks/Bitbucket.Net.Benchmarks/Streaming/StreamingBenchmarks.cs @@ -1,10 +1,10 @@ -using System.Runtime.CompilerServices; -using System.Text.Json; -using System.Text.Json.Serialization; using BenchmarkDotNet.Attributes; using Bitbucket.Net.Benchmarks.Config; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Projects; +using System.Runtime.CompilerServices; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Bitbucket.Net.Benchmarks.Streaming; @@ -33,9 +33,7 @@ public class StreamingBenchmarks [GlobalSetup] public void Setup() { - _pagedResponses = Enumerable.Range(0, PageCount) - .Select(pageIndex => CreatePagedRepositoriesJson(ItemsPerPage, pageIndex, pageIndex < PageCount - 1)) - .ToList(); + _pagedResponses = [.. Enumerable.Range(0, PageCount).Select(pageIndex => CreatePagedRepositoriesJson(ItemsPerPage, pageIndex, pageIndex < PageCount - 1))]; } /// @@ -129,7 +127,7 @@ public async Task> StreamingEarlyTermination() public async Task> BufferedEarlyTermination() { var allResults = await BufferedApproach().ConfigureAwait(false); - return allResults.Take(10).ToList(); + return [.. allResults.Take(10)]; } private async IAsyncEnumerable StreamItemsAsync([EnumeratorCancellation] CancellationToken cancellationToken = default) @@ -194,4 +192,4 @@ private static string CreatePagedRepositoriesJson(int count, int pageIndex, bool } """; } -} +} \ No newline at end of file diff --git a/benchmarks/Bitbucket.Net.Benchmarks/ZeroCopy/ZeroCopyBenchmarks.cs b/benchmarks/Bitbucket.Net.Benchmarks/ZeroCopy/ZeroCopyBenchmarks.cs index 667a704..81f3809 100644 --- a/benchmarks/Bitbucket.Net.Benchmarks/ZeroCopy/ZeroCopyBenchmarks.cs +++ b/benchmarks/Bitbucket.Net.Benchmarks/ZeroCopy/ZeroCopyBenchmarks.cs @@ -1,9 +1,9 @@ +using BenchmarkDotNet.Attributes; +using Bitbucket.Net.Benchmarks.Config; using System.Buffers; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; -using BenchmarkDotNet.Attributes; -using Bitbucket.Net.Benchmarks.Config; namespace Bitbucket.Net.Benchmarks.ZeroCopy; @@ -375,7 +375,7 @@ public sealed class BenchmarkPath public string? Parent { get; set; } public string? Name { get; set; } public string? Extension { get; set; } - + [JsonPropertyName("toString")] public string? PathString { get; set; } } @@ -388,4 +388,4 @@ public sealed class BenchmarkDiffHunk public int DestinationSpan { get; set; } public List? Segments { get; set; } public bool Truncated { get; set; } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Audit/BitbucketClient.cs b/src/Bitbucket.Net/Audit/BitbucketClient.cs index 830f27a..4fa1be0 100644 --- a/src/Bitbucket.Net/Audit/BitbucketClient.cs +++ b/src/Bitbucket.Net/Audit/BitbucketClient.cs @@ -1,63 +1,64 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Audit; using Flurl.Http; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetAuditUrl() => GetBaseUrl("/audit"); + private IFlurlRequest GetAuditUrl() => GetBaseUrl("/audit"); - private IFlurlRequest GetAuditUrl(string path) => GetAuditUrl() - .AppendPathSegment(path); + private IFlurlRequest GetAuditUrl(string path) => GetAuditUrl() + .AppendPathSegment(path); - [System.Diagnostics.CodeAnalysis.SuppressMessage("AsyncUsage", "AsyncFixer01:Unnecessary async/await usage", Justification = "")] - public async Task> GetProjectAuditEventsAsync(string projectKey, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) + [System.Diagnostics.CodeAnalysis.SuppressMessage("AsyncUsage", "AsyncFixer01:Unnecessary async/await usage", Justification = "")] + public async Task> GetProjectAuditEventsAsync(string projectKey, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize - }; + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAuditUrl($"/projects/{projectKey}/events") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAuditUrl($"/projects/{projectKey}/events") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - [System.Diagnostics.CodeAnalysis.SuppressMessage("AsyncUsage", "AsyncFixer01:Unnecessary async/await usage", Justification = "")] - public async Task> GetProjectRepoAuditEventsAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) + [System.Diagnostics.CodeAnalysis.SuppressMessage("AsyncUsage", "AsyncFixer01:Unnecessary async/await usage", Justification = "")] + public async Task> GetProjectRepoAuditEventsAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize - }; + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAuditUrl($"/projects/{projectKey}/repos/{repositorySlug}/events") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAuditUrl($"/projects/{projectKey}/repos/{repositorySlug}/events") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/BitbucketClient.cs b/src/Bitbucket.Net/BitbucketClient.cs index f9a1328..6237952 100644 --- a/src/Bitbucket.Net/BitbucketClient.cs +++ b/src/Bitbucket.Net/BitbucketClient.cs @@ -1,3 +1,11 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Converters; +using Bitbucket.Net.Common.Exceptions; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Serialization; +using Flurl; +using Flurl.Http; +using Flurl.Http.Configuration; using System; using System.Collections.Generic; using System.Linq; @@ -8,302 +16,293 @@ using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common; -using Bitbucket.Net.Common.Converters; -using Bitbucket.Net.Common.Exceptions; -using Bitbucket.Net.Common.Models; -using Bitbucket.Net.Serialization; -using Flurl; -using Flurl.Http; -using Flurl.Http.Configuration; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient + private static readonly JsonSerializerOptions s_jsonOptions = new() { - private static readonly JsonSerializerOptions s_jsonOptions = new() + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + // Source-generated context with reflection fallback for edge cases + TypeInfoResolver = JsonTypeInfoResolver.Combine( + BitbucketJsonContext.Default, // Source-generated (fast path) + new DefaultJsonTypeInfoResolver() // Reflection fallback for unregistered types + ), + Converters = { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - // Source-generated context with reflection fallback for edge cases - TypeInfoResolver = JsonTypeInfoResolver.Combine( - BitbucketJsonContext.Default, // Source-generated (fast path) - new DefaultJsonTypeInfoResolver() // Reflection fallback for unregistered types - ), - Converters = - { - new UnixDateTimeOffsetConverter(), - new NullableUnixDateTimeOffsetConverter(), - new PermissionsConverter(), - new RolesConverter(), - new FileTypesConverter(), - new LineTypesConverter(), - new ParticipantStatusConverter(), - new PullRequestStatesConverter(), - new HookTypesConverter(), - new ScopeTypesConverter(), - new WebHookOutcomesConverter(), - new RefRestrictionTypesConverter(), - new SynchronizeActionsConverter(), - new BlockerCommentStateConverter(), - new CommentSeverityConverter() - } - }; - - private static readonly ISerializer s_serializer = new DefaultJsonSerializer(s_jsonOptions); + new UnixDateTimeOffsetConverter(), + new NullableUnixDateTimeOffsetConverter(), + new PermissionsConverter(), + new RolesConverter(), + new FileTypesConverter(), + new LineTypesConverter(), + new ParticipantStatusConverter(), + new PullRequestStatesConverter(), + new HookTypesConverter(), + new ScopeTypesConverter(), + new WebHookOutcomesConverter(), + new RefRestrictionTypesConverter(), + new SynchronizeActionsConverter(), + new BlockerCommentStateConverter(), + new CommentSeverityConverter() + }, + }; + + private static readonly ISerializer s_serializer = new DefaultJsonSerializer(s_jsonOptions); + + static BitbucketClient() + { + // Configure Flurl to use System.Text.Json globally + FlurlHttp.Clients.WithDefaults(builder => + builder.WithSettings(settings => + settings.JsonSerializer = s_serializer)); + } - static BitbucketClient() - { - // Configure Flurl to use System.Text.Json globally - FlurlHttp.Clients.WithDefaults(builder => - builder.WithSettings(settings => - settings.JsonSerializer = s_serializer)); - } + private readonly Url _url; + private readonly Func? _getToken; + private readonly string? _userName; + private readonly string? _password; + private readonly IFlurlClient? _injectedClient; - private readonly Url _url; - private readonly Func? _getToken; - private readonly string? _userName; - private readonly string? _password; - private readonly IFlurlClient? _injectedClient; + private BitbucketClient(string url) + { + _url = url; + } - private BitbucketClient(string url) - { - _url = url; - } + /// + /// Creates a BitbucketClient with basic authentication. + /// + /// The base URL of the Bitbucket Server instance. + /// The username for basic authentication. + /// The password for basic authentication. + public BitbucketClient(string url, string userName, string password) + : this(url) + { + _userName = userName; + _password = password; + } - /// - /// Creates a BitbucketClient with basic authentication. - /// - /// The base URL of the Bitbucket Server instance. - /// The username for basic authentication. - /// The password for basic authentication. - public BitbucketClient(string url, string userName, string password) - : this(url) - { - _userName = userName; - _password = password; - } + /// + /// Creates a BitbucketClient with token-based authentication. + /// + /// The base URL of the Bitbucket Server instance. + /// A function that returns the bearer token. + public BitbucketClient(string url, Func getToken) + : this(url) + { + _getToken = getToken; + } - /// - /// Creates a BitbucketClient with token-based authentication. - /// - /// The base URL of the Bitbucket Server instance. - /// A function that returns the bearer token. - public BitbucketClient(string url, Func getToken) - : this(url) - { - _getToken = getToken; - } + /// + /// Creates a BitbucketClient using an externally managed HttpClient. + /// This constructor is designed for dependency injection scenarios where consumers + /// want to configure the HttpClient with IHttpClientFactory, Polly resilience policies, + /// custom timeouts, or other middleware. + /// + /// The externally managed HttpClient instance. The client should be configured with any desired resilience policies, timeouts, etc. + /// The base URL of the Bitbucket Server instance. + /// Optional: A function that returns the bearer token for authentication. + /// + /// + /// When using this constructor, authentication should typically be handled by configuring + /// the HttpClient with appropriate headers via IHttpClientFactory or DelegatingHandlers. + /// If getToken is provided, it will add the Authorization header to each request. + /// + /// + /// Example DI registration: + /// + /// services.AddHttpClient<BitbucketClient>() + /// .AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(1))) + /// .ConfigureHttpClient(c => c.Timeout = TimeSpan.FromMinutes(2)); + /// + /// services.AddSingleton<BitbucketClient>(sp => { + /// var httpClient = sp.GetRequiredService<IHttpClientFactory>().CreateClient(nameof(BitbucketClient)); + /// return new BitbucketClient(httpClient, "https://bitbucket.example.com", () => GetToken()); + /// }); + /// + /// + /// + public BitbucketClient(HttpClient httpClient, string baseUrl, Func? getToken = null) + { + ArgumentNullException.ThrowIfNull(httpClient); + if (string.IsNullOrWhiteSpace(baseUrl)) throw new ArgumentNullException(nameof(baseUrl)); - /// - /// Creates a BitbucketClient using an externally managed HttpClient. - /// This constructor is designed for dependency injection scenarios where consumers - /// want to configure the HttpClient with IHttpClientFactory, Polly resilience policies, - /// custom timeouts, or other middleware. - /// - /// The externally managed HttpClient instance. The client should be configured with any desired resilience policies, timeouts, etc. - /// The base URL of the Bitbucket Server instance. - /// Optional: A function that returns the bearer token for authentication. - /// - /// - /// When using this constructor, authentication should typically be handled by configuring - /// the HttpClient with appropriate headers via IHttpClientFactory or DelegatingHandlers. - /// If getToken is provided, it will add the Authorization header to each request. - /// - /// - /// Example DI registration: - /// - /// services.AddHttpClient<BitbucketClient>() - /// .AddTransientHttpErrorPolicy(p => p.WaitAndRetryAsync(3, _ => TimeSpan.FromSeconds(1))) - /// .ConfigureHttpClient(c => c.Timeout = TimeSpan.FromMinutes(2)); - /// - /// services.AddSingleton<BitbucketClient>(sp => { - /// var httpClient = sp.GetRequiredService<IHttpClientFactory>().CreateClient(nameof(BitbucketClient)); - /// return new BitbucketClient(httpClient, "https://bitbucket.example.com", () => GetToken()); - /// }); - /// - /// - /// - public BitbucketClient(HttpClient httpClient, string baseUrl, Func? getToken = null) - { - if (httpClient == null) throw new ArgumentNullException(nameof(httpClient)); - if (string.IsNullOrWhiteSpace(baseUrl)) throw new ArgumentNullException(nameof(baseUrl)); + _url = baseUrl; + _getToken = getToken; + _injectedClient = new FlurlClient(httpClient, baseUrl) + .WithSettings(settings => settings.JsonSerializer = s_serializer); + } - _url = baseUrl; - _getToken = getToken; - _injectedClient = new FlurlClient(httpClient, baseUrl) - .WithSettings(settings => settings.JsonSerializer = s_serializer); - } + /// + /// Creates a BitbucketClient using an externally managed IFlurlClient. + /// This constructor provides maximum control over the Flurl client configuration. + /// + /// The pre-configured IFlurlClient instance. + /// Optional: A function that returns the bearer token for authentication. + /// + /// Use this constructor when you need fine-grained control over Flurl's configuration, + /// such as custom event handlers, advanced settings, or when using IFlurlClientCache. + /// + public BitbucketClient(IFlurlClient flurlClient, Func? getToken = null) + { + _injectedClient = flurlClient ?? throw new ArgumentNullException(nameof(flurlClient)); + _url = flurlClient.BaseUrl ?? throw new ArgumentException("FlurlClient must have a BaseUrl configured.", nameof(flurlClient)); + _getToken = getToken; + } - /// - /// Creates a BitbucketClient using an externally managed IFlurlClient. - /// This constructor provides maximum control over the Flurl client configuration. - /// - /// The pre-configured IFlurlClient instance. - /// Optional: A function that returns the bearer token for authentication. - /// - /// Use this constructor when you need fine-grained control over Flurl's configuration, - /// such as custom event handlers, advanced settings, or when using IFlurlClientCache. - /// - public BitbucketClient(IFlurlClient flurlClient, Func? getToken = null) + private IFlurlRequest GetBaseUrl(string root = "/api", string version = "1.0") + { + // If using injected client, use it directly + if (_injectedClient != null) { - _injectedClient = flurlClient ?? throw new ArgumentNullException(nameof(flurlClient)); - _url = flurlClient.BaseUrl ?? throw new ArgumentException("FlurlClient must have a BaseUrl configured.", nameof(flurlClient)); - _getToken = getToken; - } + var request = _injectedClient + .Request() + .AppendPathSegment($"/rest{root}/{version}") + .WithSettings(settings => settings.JsonSerializer = s_serializer); - private IFlurlRequest GetBaseUrl(string root = "/api", string version = "1.0") - { - // If using injected client, use it directly - if (_injectedClient != null) + // Apply token authentication if provided + if (_getToken != null) { - var request = _injectedClient - .Request() - .AppendPathSegment($"/rest{root}/{version}") - .WithSettings(settings => settings.JsonSerializer = s_serializer); - - // Apply token authentication if provided - if (_getToken != null) - { - request = request.WithOAuthBearerToken(_getToken()); - } - - return request; + request = request.WithOAuthBearerToken(_getToken()); } - // Original behavior for non-DI scenarios - return new Url(_url) - .AppendPathSegment($"/rest{root}/{version}") - .WithSettings(settings => settings.JsonSerializer = s_serializer) - .WithAuthentication(_getToken, _userName, _password); + return request; } - private async Task ReadResponseContentAsync(IFlurlResponse response, Func? contentHandler = null, CancellationToken cancellationToken = default) - { - string content = await response.GetStringAsync().ConfigureAwait(false); - return contentHandler != null - ? contentHandler(content) - : JsonSerializer.Deserialize(content, s_jsonOptions)!; - } + // Original behavior for non-DI scenarios + return new Url(_url) + .AppendPathSegment($"/rest{root}/{version}") + .WithSettings(settings => settings.JsonSerializer = s_serializer) + .WithAuthentication(_getToken, _userName, _password); + } - private async Task ReadResponseContentAsync(IFlurlResponse response, CancellationToken cancellationToken = default) - { - string content = await response.GetStringAsync().ConfigureAwait(false); - return content == ""; - } + private static async Task ReadResponseContentAsync(IFlurlResponse response, Func? contentHandler = null, CancellationToken cancellationToken = default) + { + string content = await response.GetStringAsync().ConfigureAwait(false); + return contentHandler != null + ? contentHandler(content) + : JsonSerializer.Deserialize(content, s_jsonOptions)!; + } + + private static async Task ReadResponseContentAsync(IFlurlResponse response, CancellationToken cancellationToken = default) + { + string content = await response.GetStringAsync().ConfigureAwait(false); + return content == ""; + } - private async Task HandleErrorsAsync(IFlurlResponse response, CancellationToken cancellationToken = default) + private static async Task HandleErrorsAsync(IFlurlResponse response, CancellationToken cancellationToken = default) + { + if (response.StatusCode >= 400) { - if (response.StatusCode >= 400) + var errors = Array.Empty(); + string? requestUrl = response.ResponseMessage?.RequestMessage?.RequestUri?.ToString(); + string? rawResponseBody = null; + + try { - var errors = Array.Empty(); - string? requestUrl = response.ResponseMessage?.RequestMessage?.RequestUri?.ToString(); - string? rawResponseBody = null; + // Read the response body first so we can include it in the error if parsing fails + rawResponseBody = await response.GetStringAsync().ConfigureAwait(false); - try + if (!string.IsNullOrWhiteSpace(rawResponseBody)) { - // Read the response body first so we can include it in the error if parsing fails - rawResponseBody = await response.GetStringAsync().ConfigureAwait(false); - - if (!string.IsNullOrWhiteSpace(rawResponseBody)) + var errorResponse = JsonSerializer.Deserialize(rawResponseBody, s_jsonOptions); + if (errorResponse?.Errors != null && errorResponse.Errors.Any()) { - var errorResponse = JsonSerializer.Deserialize(rawResponseBody, s_jsonOptions); - if (errorResponse?.Errors != null && errorResponse.Errors.Any()) - { - errors = errorResponse.Errors.ToArray(); - } + errors = [.. errorResponse.Errors]; } } - catch + } + catch + { + // If we can't parse the error response as JSON, create a synthetic error with the raw body + if (!string.IsNullOrWhiteSpace(rawResponseBody)) { - // If we can't parse the error response as JSON, create a synthetic error with the raw body - if (!string.IsNullOrWhiteSpace(rawResponseBody)) - { - // Truncate very long responses - var truncatedBody = rawResponseBody.Length > 500 - ? rawResponseBody.Substring(0, 500) + "..." - : rawResponseBody; - errors = new[] { new Error { Message = truncatedBody } }; - } + // Truncate very long responses + var truncatedBody = rawResponseBody.Length > 500 + ? rawResponseBody[..500] + "..." + : rawResponseBody; + errors = [new Error { Message = truncatedBody }]; } - - throw BitbucketApiException.Create(response.StatusCode, errors, requestUrl); } - } - private async Task HandleResponseAsync(IFlurlResponse response, Func? contentHandler = null, CancellationToken cancellationToken = default) - { - await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); - return await ReadResponseContentAsync(response, contentHandler, cancellationToken).ConfigureAwait(false); + throw BitbucketApiException.Create(response.StatusCode, errors, requestUrl); } + } - private async Task HandleResponseAsync(IFlurlResponse response, CancellationToken cancellationToken = default) - { - await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); - return await ReadResponseContentAsync(response, cancellationToken).ConfigureAwait(false); - } + private static async Task HandleResponseAsync(IFlurlResponse response, Func? contentHandler = null, CancellationToken cancellationToken = default) + { + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + return await ReadResponseContentAsync(response, contentHandler, cancellationToken).ConfigureAwait(false); + } - private async Task> GetPagedResultsAsync(int? maxPages, IDictionary queryParamValues, Func, CancellationToken, Task>> selector, CancellationToken cancellationToken = default) - { - var results = new List(); - bool isLastPage = false; - int numPages = 0; + private static async Task HandleResponseAsync(IFlurlResponse response, CancellationToken cancellationToken = default) + { + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + return await ReadResponseContentAsync(response, cancellationToken).ConfigureAwait(false); + } - while (!isLastPage && (maxPages == null || numPages < maxPages)) - { - cancellationToken.ThrowIfCancellationRequested(); - var selectorResults = await selector(queryParamValues, cancellationToken).ConfigureAwait(false); - results.AddRange(selectorResults.Values); + private static async Task> GetPagedResultsAsync(int? maxPages, IDictionary queryParamValues, Func, CancellationToken, Task>> selector, CancellationToken cancellationToken = default) + { + var results = new List(); + bool isLastPage = false; + int numPages = 0; - isLastPage = selectorResults.IsLastPage; - if (!isLastPage && selectorResults.NextPageStart.HasValue) - { - queryParamValues["start"] = selectorResults.NextPageStart.Value; - } + while (!isLastPage && (maxPages == null || numPages < maxPages)) + { + cancellationToken.ThrowIfCancellationRequested(); + var selectorResults = await selector(queryParamValues, cancellationToken).ConfigureAwait(false); + results.AddRange(selectorResults.Values); - numPages++; + isLastPage = selectorResults.IsLastPage; + if (!isLastPage && selectorResults.NextPageStart.HasValue) + { + queryParamValues["start"] = selectorResults.NextPageStart.Value; } - return results; + numPages++; } - /// - /// Streams paged results as an IAsyncEnumerable, yielding items as they are retrieved. - /// This is more memory-efficient for large result sets and provides faster time-to-first-result. - /// - /// The type of items in the paged results. - /// Optional maximum number of pages to retrieve. - /// Query parameters for the API request. - /// Function to retrieve a page of results. - /// Cancellation token. - /// An async enumerable that yields items as they are retrieved. - private async IAsyncEnumerable GetPagedResultsStreamAsync( - int? maxPages, - IDictionary queryParamValues, - Func, CancellationToken, Task>> selector, - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - bool isLastPage = false; - int numPages = 0; + return results; + } - while (!isLastPage && (maxPages == null || numPages < maxPages)) - { - cancellationToken.ThrowIfCancellationRequested(); - var selectorResults = await selector(queryParamValues, cancellationToken).ConfigureAwait(false); + /// + /// Streams paged results as an IAsyncEnumerable, yielding items as they are retrieved. + /// This is more memory-efficient for large result sets and provides faster time-to-first-result. + /// + /// The type of items in the paged results. + /// Optional maximum number of pages to retrieve. + /// Query parameters for the API request. + /// Function to retrieve a page of results. + /// Cancellation token. + /// An async enumerable that yields items as they are retrieved. + private static async IAsyncEnumerable GetPagedResultsStreamAsync( + int? maxPages, + IDictionary queryParamValues, + Func, CancellationToken, Task>> selector, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + bool isLastPage = false; + int numPages = 0; - foreach (var item in selectorResults.Values) - { - yield return item; - } + while (!isLastPage && (maxPages == null || numPages < maxPages)) + { + cancellationToken.ThrowIfCancellationRequested(); + var selectorResults = await selector(queryParamValues, cancellationToken).ConfigureAwait(false); - isLastPage = selectorResults.IsLastPage; - if (!isLastPage && selectorResults.NextPageStart.HasValue) - { - queryParamValues["start"] = selectorResults.NextPageStart.Value; - } + foreach (var item in selectorResults.Values) + { + yield return item; + } - numPages++; + isLastPage = selectorResults.IsLastPage; + if (!isLastPage && selectorResults.NextPageStart.HasValue) + { + queryParamValues["start"] = selectorResults.NextPageStart.Value; } + + numPages++; } } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Branches/BitbucketClient.cs b/src/Bitbucket.Net/Branches/BitbucketClient.cs index 4275f62..aae10e6 100644 --- a/src/Bitbucket.Net/Branches/BitbucketClient.cs +++ b/src/Bitbucket.Net/Branches/BitbucketClient.cs @@ -1,82 +1,82 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Branches; +using Bitbucket.Net.Models.Core.Projects; +using Flurl.Http; using System.Collections.Generic; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common; -using Bitbucket.Net.Common.Models; -using Bitbucket.Net.Models.Branches; -using Bitbucket.Net.Models.Core.Projects; -using Flurl.Http; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetBranchUrl() => GetBaseUrl("/branch-utils"); + private IFlurlRequest GetBranchUrl() => GetBaseUrl("/branch-utils"); - private IFlurlRequest GetBranchUrl(string path) => GetBranchUrl() - .AppendPathSegment(path); + private IFlurlRequest GetBranchUrl(string path) => GetBranchUrl() + .AppendPathSegment(path); - public async Task> GetCommitBranchInfoAsync(string projectKey, string repositorySlug, string fullSha, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetCommitBranchInfoAsync(string projectKey, string repositorySlug, string fullSha, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start - }; + ["limit"] = limit, + ["start"] = start, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches/info/{fullSha}") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches/info/{fullSha}") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task GetRepoBranchModelAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) - { - return await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branchmodel") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetRepoBranchModelAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + return await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branchmodel") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task CreateRepoBranchAsync(string projectKey, string repositorySlug, string branchName, string startPoint, CancellationToken cancellationToken = default) + public async Task CreateRepoBranchAsync(string projectKey, string repositorySlug, string branchName, string startPoint, CancellationToken cancellationToken = default) + { + var data = new { - var data = new - { - name = branchName, - startPoint - }; + name = branchName, + startPoint, + }; - var response = await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteRepoBranchAsync(string projectKey, string repositorySlug, string branchName, bool dryRun, string? endPoint = null, CancellationToken cancellationToken = default) + public async Task DeleteRepoBranchAsync(string projectKey, string repositorySlug, string branchName, bool dryRun, string? endPoint = null, CancellationToken cancellationToken = default) + { + var data = new { - var data = new - { - name = branchName, - dryRun = BitbucketHelpers.BoolToString(dryRun), - endPoint - }; + name = branchName, + dryRun = BitbucketHelpers.BoolToString(dryRun), + endPoint, + }; - var json = JsonSerializer.Serialize(data, s_jsonOptions); - var response = await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches") - .WithHeader("Content-Type", "application/json") - .SendAsync(HttpMethod.Delete, new StringContent(json, Encoding.UTF8, "application/json"), cancellationToken: cancellationToken) - .ConfigureAwait(false); + var json = JsonSerializer.Serialize(data, s_jsonOptions); + var response = await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches") + .WithHeader("Content-Type", "application/json") + .SendAsync(HttpMethod.Delete, new StringContent(json, Encoding.UTF8, "application/json"), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Builds/BitbucketClient.cs b/src/Bitbucket.Net/Builds/BitbucketClient.cs index 09bd59c..719f62c 100644 --- a/src/Bitbucket.Net/Builds/BitbucketClient.cs +++ b/src/Bitbucket.Net/Builds/BitbucketClient.cs @@ -1,68 +1,68 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Builds; using Flurl.Http; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetBuildsUrl() => GetBaseUrl("/build-status"); + private IFlurlRequest GetBuildsUrl() => GetBaseUrl("/build-status"); - private IFlurlRequest GetBuildsUrl(string path) => GetBuildsUrl() - .AppendPathSegment(path); + private IFlurlRequest GetBuildsUrl(string path) => GetBuildsUrl() + .AppendPathSegment(path); - public async Task GetBuildStatsForCommitAsync(string commitId, bool includeUnique = false, CancellationToken cancellationToken = default) - { - return await GetBuildsUrl($"/commits/stats/{commitId}") - .SetQueryParam("includeUnique", BitbucketHelpers.BoolToString(includeUnique)) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetBuildStatsForCommitAsync(string commitId, bool includeUnique = false, CancellationToken cancellationToken = default) + { + return await GetBuildsUrl($"/commits/stats/{commitId}") + .SetQueryParam("includeUnique", BitbucketHelpers.BoolToString(includeUnique)) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetBuildStatsForCommitsAsync(CancellationToken cancellationToken, params string[] commitIds) - { - var response = await GetBuildsUrl("/commits/stats") - .PostJsonAsync(commitIds, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetBuildStatsForCommitsAsync(CancellationToken cancellationToken, params string[] commitIds) + { + var response = await GetBuildsUrl("/commits/stats") + .PostJsonAsync(commitIds, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task> GetBuildStatsForCommitsAsync(params string[] commitIds) - { - return await GetBuildStatsForCommitsAsync(default, commitIds).ConfigureAwait(false); - } + public async Task> GetBuildStatsForCommitsAsync(params string[] commitIds) + { + return await GetBuildStatsForCommitsAsync(default, commitIds).ConfigureAwait(false); + } - public async Task> GetBuildStatusForCommitAsync(string commitId, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetBuildStatusForCommitAsync(string commitId, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start - }; + ["limit"] = limit, + ["start"] = start, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetBuildsUrl($"/commits/{commitId}") - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetBuildsUrl($"/commits/{commitId}") + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task AssociateBuildStatusWithCommitAsync(string commitId, BuildStatus buildStatus, CancellationToken cancellationToken = default) - { - var response = await GetBuildsUrl($"/commits/{commitId}") - .PostJsonAsync(buildStatus, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task AssociateBuildStatusWithCommitAsync(string commitId, BuildStatus buildStatus, CancellationToken cancellationToken = default) + { + var response = await GetBuildsUrl($"/commits/{commitId}") + .PostJsonAsync(buildStatus, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs index aaf9778..346122a 100644 --- a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs +++ b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs @@ -1,96 +1,97 @@ +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Users; +using Flurl.Http; using System.Collections.Generic; using System.Net.Http; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common.Models; -using Bitbucket.Net.Models.Core.Users; -using Flurl.Http; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetCommentLikesUrl() => GetBaseUrl("/comment-likes"); + private IFlurlRequest GetCommentLikesUrl() => GetBaseUrl("/comment-likes"); - private IFlurlRequest GetCommentLikesUrl(string path) => GetCommentLikesUrl() - .AppendPathSegment(path); + private IFlurlRequest GetCommentLikesUrl(string path) => GetCommentLikesUrl() + .AppendPathSegment(path); - public async Task> GetCommitCommentLikesAsync(string projectKey, string repositorySlug, string commitId, string commentId, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) + public async Task> GetCommitCommentLikesAsync(string projectKey, string repositorySlug, string commitId, string commentId, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize - }; + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task LikeCommitCommentAsync(string projectKey, string repositorySlug, string commitId, string commentId, CancellationToken cancellationToken = default) - { - var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task LikeCommitCommentAsync(string projectKey, string repositorySlug, string commitId, string commentId, CancellationToken cancellationToken = default) + { + var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task UnlikeCommitCommentAsync(string projectKey, string repositorySlug, string commitId, string commentId, CancellationToken cancellationToken = default) - { - var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UnlikeCommitCommentAsync(string projectKey, string repositorySlug, string commitId, string commentId, CancellationToken cancellationToken = default) + { + var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task> GetPullRequestCommentLikesAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetPullRequestCommentLikesAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start - }; + ["limit"] = limit, + ["start"] = start, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task LikePullRequestCommentAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, CancellationToken cancellationToken = default) - { - var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task LikePullRequestCommentAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, CancellationToken cancellationToken = default) + { + var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task UnlikePullRequestCommentAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, CancellationToken cancellationToken = default) - { - var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UnlikePullRequestCommentAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, CancellationToken cancellationToken = default) + { + var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/BitbucketHelpers.cs b/src/Bitbucket.Net/Common/BitbucketHelpers.cs index 599ad1a..9a26f06 100644 --- a/src/Bitbucket.Net/Common/BitbucketHelpers.cs +++ b/src/Bitbucket.Net/Common/BitbucketHelpers.cs @@ -1,793 +1,792 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Logs; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Git; using Bitbucket.Net.Models.RefRestrictions; using Bitbucket.Net.Models.RefSync; +using System; +using System.Collections.Generic; +using System.Linq; -namespace Bitbucket.Net.Common +namespace Bitbucket.Net.Common; + +public static class BitbucketHelpers { - public static class BitbucketHelpers - { - #region Bool + #region Bool - public static string BoolToString(bool value) => value - ? "true" - : "false"; + public static string BoolToString(bool value) => value + ? "true" + : "false"; - public static string? BoolToString(bool? value) => value.HasValue - ? BoolToString(value.Value) - : null; + public static string? BoolToString(bool? value) => value.HasValue + ? BoolToString(value.Value) + : null; - public static bool StringToBool(string value) => value.Equals("true", StringComparison.OrdinalIgnoreCase); + public static bool StringToBool(string value) => value.Equals("true", StringComparison.OrdinalIgnoreCase); - #endregion + #endregion - #region BranchOrderBy + #region BranchOrderBy - private static readonly Dictionary s_stringByBranchOrderBy = new Dictionary - { - [BranchOrderBy.Alphabetical] = "ALPHABETICAL", - [BranchOrderBy.Modification] = "MODIFICATION" - }; + private static readonly Dictionary s_stringByBranchOrderBy = new() + { + [BranchOrderBy.Alphabetical] = "ALPHABETICAL", + [BranchOrderBy.Modification] = "MODIFICATION", + }; - public static string BranchOrderByToString(BranchOrderBy orderBy) + public static string BranchOrderByToString(BranchOrderBy orderBy) + { + if (!s_stringByBranchOrderBy.TryGetValue(orderBy, out string? result)) { - if (!s_stringByBranchOrderBy.TryGetValue(orderBy, out string? result)) - { - throw new ArgumentException($"Unknown branch order by: {orderBy}"); - } - - return result; + throw new ArgumentException($"Unknown branch order by: {orderBy}"); } - #endregion + return result; + } - #region PullRequestDirections + #endregion - private static readonly Dictionary s_stringByPullRequestDirection = new Dictionary - { - [PullRequestDirections.Incoming] = "INCOMING", - [PullRequestDirections.Outgoing] = "OUTGOING" - }; + #region PullRequestDirections - public static string PullRequestDirectionToString(PullRequestDirections direction) - { - if (!s_stringByPullRequestDirection.TryGetValue(direction, out string? result)) - { - throw new ArgumentException($"Unknown pull request direction: {direction}"); - } + private static readonly Dictionary s_stringByPullRequestDirection = new() + { + [PullRequestDirections.Incoming] = "INCOMING", + [PullRequestDirections.Outgoing] = "OUTGOING", + }; - return result; + public static string PullRequestDirectionToString(PullRequestDirections direction) + { + if (!s_stringByPullRequestDirection.TryGetValue(direction, out string? result)) + { + throw new ArgumentException($"Unknown pull request direction: {direction}"); } - #endregion + return result; + } - #region PullRequestStates + #endregion - private static readonly Dictionary s_stringByPullRequestState = new Dictionary - { - [PullRequestStates.Open] = "OPEN", - [PullRequestStates.Declined] = "DECLINED", - [PullRequestStates.Merged] = "MERGED", - [PullRequestStates.All] = "ALL" - }; + #region PullRequestStates - public static string PullRequestStateToString(PullRequestStates state) - { - if (!s_stringByPullRequestState.TryGetValue(state, out string? result)) - { - throw new ArgumentException($"Unknown pull request state: {state}"); - } + private static readonly Dictionary s_stringByPullRequestState = new() + { + [PullRequestStates.Open] = "OPEN", + [PullRequestStates.Declined] = "DECLINED", + [PullRequestStates.Merged] = "MERGED", + [PullRequestStates.All] = "ALL", + }; - return result; + public static string PullRequestStateToString(PullRequestStates state) + { + if (!s_stringByPullRequestState.TryGetValue(state, out string? result)) + { + throw new ArgumentException($"Unknown pull request state: {state}"); } - public static string? PullRequestStateToString(PullRequestStates? state) => state.HasValue - ? PullRequestStateToString(state.Value) - : null; + return result; + } - public static PullRequestStates StringToPullRequestState(string s) - { - var pair = s_stringByPullRequestState.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown pull request state: {s}"); - } + public static string? PullRequestStateToString(PullRequestStates? state) => state.HasValue + ? PullRequestStateToString(state.Value) + : null; - return pair.Key; + public static PullRequestStates StringToPullRequestState(string s) + { + var pair = s_stringByPullRequestState.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown pull request state: {s}"); } - #endregion + return pair.Key; + } - #region PullRequestOrders + #endregion - private static readonly Dictionary s_stringByPullRequestOrder = new Dictionary - { - [PullRequestOrders.Newest] = "NEWEST", - [PullRequestOrders.Oldest] = "OLDEST" - }; + #region PullRequestOrders - public static string PullRequestOrderToString(PullRequestOrders order) - { - if (!s_stringByPullRequestOrder.TryGetValue(order, out string? result)) - { - throw new ArgumentException($"Unknown pull request order: {order}"); - } + private static readonly Dictionary s_stringByPullRequestOrder = new() + { + [PullRequestOrders.Newest] = "NEWEST", + [PullRequestOrders.Oldest] = "OLDEST", + }; - return result; + public static string PullRequestOrderToString(PullRequestOrders order) + { + if (!s_stringByPullRequestOrder.TryGetValue(order, out string? result)) + { + throw new ArgumentException($"Unknown pull request order: {order}"); } - public static string? PullRequestOrderToString(PullRequestOrders? order) => order.HasValue - ? PullRequestOrderToString(order.Value) - : null; + return result; + } - #endregion + public static string? PullRequestOrderToString(PullRequestOrders? order) => order.HasValue + ? PullRequestOrderToString(order.Value) + : null; - #region PullRequestFromTypes + #endregion - private static readonly Dictionary s_stringByPullRequestFromType = new Dictionary - { - [PullRequestFromTypes.Comment] = "COMMENT", - [PullRequestFromTypes.Activity] = "ACTIVITY" - }; + #region PullRequestFromTypes - private static string PullRequestFromTypeToString(PullRequestFromTypes fromType) - { - if (!s_stringByPullRequestFromType.TryGetValue(fromType, out string? result)) - { - throw new ArgumentException($"Unknown pull request from type: {fromType}"); - } + private static readonly Dictionary s_stringByPullRequestFromType = new() + { + [PullRequestFromTypes.Comment] = "COMMENT", + [PullRequestFromTypes.Activity] = "ACTIVITY", + }; - return result; + private static string PullRequestFromTypeToString(PullRequestFromTypes fromType) + { + if (!s_stringByPullRequestFromType.TryGetValue(fromType, out string? result)) + { + throw new ArgumentException($"Unknown pull request from type: {fromType}"); } - public static string? PullRequestFromTypeToString(PullRequestFromTypes? fromType) => fromType.HasValue - ? PullRequestFromTypeToString(fromType.Value) - : null; + return result; + } - #endregion + public static string? PullRequestFromTypeToString(PullRequestFromTypes? fromType) => fromType.HasValue + ? PullRequestFromTypeToString(fromType.Value) + : null; - #region Permissions + #endregion - private static readonly Dictionary s_stringByPermissions = new Dictionary - { - [Permissions.Admin] = "ADMIN", - [Permissions.LicensedUser] = "LICENSED_USER", - [Permissions.ProjectAdmin] = "PROJECT_ADMIN", - [Permissions.ProjectCreate] = "PROJECT_CREATE", - [Permissions.ProjectRead] = "PROJECT_READ", - [Permissions.ProjectView] = "PROJECT_VIEW", - [Permissions.ProjectWrite] = "PROJECT_WRITE", - [Permissions.RepoAdmin] = "REPO_ADMIN", - [Permissions.RepoRead] = "REPO_READ", - [Permissions.RepoWrite] = "REPO_WRITE", - [Permissions.SysAdmin] = "SYS_ADMIN" - }; + #region Permissions - public static string PermissionToString(Permissions permission) + private static readonly Dictionary s_stringByPermissions = new() + { + [Permissions.Admin] = "ADMIN", + [Permissions.LicensedUser] = "LICENSED_USER", + [Permissions.ProjectAdmin] = "PROJECT_ADMIN", + [Permissions.ProjectCreate] = "PROJECT_CREATE", + [Permissions.ProjectRead] = "PROJECT_READ", + [Permissions.ProjectView] = "PROJECT_VIEW", + [Permissions.ProjectWrite] = "PROJECT_WRITE", + [Permissions.RepoAdmin] = "REPO_ADMIN", + [Permissions.RepoRead] = "REPO_READ", + [Permissions.RepoWrite] = "REPO_WRITE", + [Permissions.SysAdmin] = "SYS_ADMIN", + }; + + public static string PermissionToString(Permissions permission) + { + if (!s_stringByPermissions.TryGetValue(permission, out string? result)) { - if (!s_stringByPermissions.TryGetValue(permission, out string? result)) - { - throw new ArgumentException($"Unknown permission: {permission}"); - } - - return result; + throw new ArgumentException($"Unknown permission: {permission}"); } - public static string? PermissionToString(Permissions? permission) => permission.HasValue - ? PermissionToString(permission.Value) - : null; + return result; + } - public static Permissions StringToPermission(string s) - { - var pair = s_stringByPermissions.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown permission: {s}"); - } + public static string? PermissionToString(Permissions? permission) => permission.HasValue + ? PermissionToString(permission.Value) + : null; - return pair.Key; + public static Permissions StringToPermission(string s) + { + var pair = s_stringByPermissions.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown permission: {s}"); } - #endregion + return pair.Key; + } - #region MergeCommits + #endregion - private static readonly Dictionary s_stringByMergeCommits = new Dictionary - { - [MergeCommits.Exclude] = "exclude", - [MergeCommits.Include] = "include", - [MergeCommits.Only] = "only" - }; + #region MergeCommits - public static string MergeCommitsToString(MergeCommits mergeCommits) - { - if (!s_stringByMergeCommits.TryGetValue(mergeCommits, out string? result)) - { - throw new ArgumentException($"Unknown merge commit: {mergeCommits}"); - } + private static readonly Dictionary s_stringByMergeCommits = new() + { + [MergeCommits.Exclude] = "exclude", + [MergeCommits.Include] = "include", + [MergeCommits.Only] = "only", + }; - return result; + public static string MergeCommitsToString(MergeCommits mergeCommits) + { + if (!s_stringByMergeCommits.TryGetValue(mergeCommits, out string? result)) + { + throw new ArgumentException($"Unknown merge commit: {mergeCommits}"); } - #endregion + return result; + } + + #endregion - #region Roles + #region Roles - private static readonly Dictionary s_stringByRoles = new Dictionary - { - [Roles.Author] = "AUTHOR", - [Roles.Reviewer] = "REVIEWER", - [Roles.Participant] = "PARTICIPANT" - }; + private static readonly Dictionary s_stringByRoles = new() + { + [Roles.Author] = "AUTHOR", + [Roles.Reviewer] = "REVIEWER", + [Roles.Participant] = "PARTICIPANT", + }; - public static string RoleToString(Roles role) + public static string RoleToString(Roles role) + { + if (!s_stringByRoles.TryGetValue(role, out string? result)) { - if (!s_stringByRoles.TryGetValue(role, out string? result)) - { - throw new ArgumentException($"Unknown role: {role}"); - } - - return result; + throw new ArgumentException($"Unknown role: {role}"); } - public static string? RoleToString(Roles? role) => role.HasValue - ? RoleToString(role.Value) - : null; + return result; + } - public static Roles StringToRole(string s) - { - var pair = s_stringByRoles.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown role: {s}"); - } + public static string? RoleToString(Roles? role) => role.HasValue + ? RoleToString(role.Value) + : null; - return pair.Key; + public static Roles StringToRole(string s) + { + var pair = s_stringByRoles.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown role: {s}"); } - #endregion + return pair.Key; + } - #region LineTypes + #endregion - private static readonly Dictionary s_stringByLineTypes = new Dictionary - { - [LineTypes.Added] = "ADDED", - [LineTypes.Removed] = "REMOVED", - [LineTypes.Context] = "CONTEXT" - }; + #region LineTypes - public static string LineTypeToString(LineTypes lineType) - { - if (!s_stringByLineTypes.TryGetValue(lineType, out string? result)) - { - throw new ArgumentException($"Unknown line type: {lineType}"); - } + private static readonly Dictionary s_stringByLineTypes = new() + { + [LineTypes.Added] = "ADDED", + [LineTypes.Removed] = "REMOVED", + [LineTypes.Context] = "CONTEXT", + }; - return result; + public static string LineTypeToString(LineTypes lineType) + { + if (!s_stringByLineTypes.TryGetValue(lineType, out string? result)) + { + throw new ArgumentException($"Unknown line type: {lineType}"); } - public static string? LineTypeToString(LineTypes? lineType) + return result; + } + + public static string? LineTypeToString(LineTypes? lineType) + { + return lineType.HasValue + ? LineTypeToString(lineType.Value) + : null; + } + + public static LineTypes StringToLineType(string s) + { + var pair = s_stringByLineTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) { - return lineType.HasValue - ? LineTypeToString(lineType.Value) - : null; + throw new ArgumentException($"Unknown line type: {s}"); } - public static LineTypes StringToLineType(string s) - { - var pair = s_stringByLineTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown line type: {s}"); - } + return pair.Key; + } - return pair.Key; - } + #endregion - #endregion + #region FileTypes - #region FileTypes + private static readonly Dictionary s_stringByFileTypes = new() + { + [FileTypes.From] = "FROM", + [FileTypes.To] = "TO", + }; - private static readonly Dictionary s_stringByFileTypes = new Dictionary + public static string FileTypeToString(FileTypes fileType) + { + if (!s_stringByFileTypes.TryGetValue(fileType, out string? result)) { - [FileTypes.From] = "FROM", - [FileTypes.To] = "TO" - }; + throw new ArgumentException($"Unknown file type: {fileType}"); + } - public static string FileTypeToString(FileTypes fileType) - { - if (!s_stringByFileTypes.TryGetValue(fileType, out string? result)) - { - throw new ArgumentException($"Unknown file type: {fileType}"); - } + return result; + } - return result; - } + public static string? FileTypeToString(FileTypes? fileType) + { + return fileType.HasValue + ? FileTypeToString(fileType.Value) + : null; + } - public static string? FileTypeToString(FileTypes? fileType) + public static FileTypes StringToFileType(string s) + { + var pair = s_stringByFileTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) { - return fileType.HasValue - ? FileTypeToString(fileType.Value) - : null; + throw new ArgumentException($"Unknown file type: {s}"); } - public static FileTypes StringToFileType(string s) - { - var pair = s_stringByFileTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown file type: {s}"); - } - - return pair.Key; - } + return pair.Key; + } - #endregion + #endregion - #region ChangeScopes + #region ChangeScopes - private static readonly Dictionary s_stringByChangeScopes = new Dictionary - { - [ChangeScopes.All] = "ALL", - [ChangeScopes.Unreviewed] = "UNREVIEWED", - [ChangeScopes.Range] = "RANGE" - }; + private static readonly Dictionary s_stringByChangeScopes = new() + { + [ChangeScopes.All] = "ALL", + [ChangeScopes.Unreviewed] = "UNREVIEWED", + [ChangeScopes.Range] = "RANGE", + }; - public static string ChangeScopeToString(ChangeScopes changeScope) + public static string ChangeScopeToString(ChangeScopes changeScope) + { + if (!s_stringByChangeScopes.TryGetValue(changeScope, out string? result)) { - if (!s_stringByChangeScopes.TryGetValue(changeScope, out string? result)) - { - throw new ArgumentException($"Unknown change scope: {changeScope}"); - } - - return result; + throw new ArgumentException($"Unknown change scope: {changeScope}"); } - #endregion + return result; + } - #region LogLevels + #endregion - private static readonly Dictionary s_stringByLogLevels = new Dictionary - { - [LogLevels.Trace] = "TRACE", - [LogLevels.Debug] = "DEBUG", - [LogLevels.Info] = "INFO", - [LogLevels.Warn] = "WARN", - [LogLevels.Error] = "ERROR" - }; + #region LogLevels - public static string LogLevelToString(LogLevels logLevel) + private static readonly Dictionary s_stringByLogLevels = new() + { + [LogLevels.Trace] = "TRACE", + [LogLevels.Debug] = "DEBUG", + [LogLevels.Info] = "INFO", + [LogLevels.Warn] = "WARN", + [LogLevels.Error] = "ERROR", + }; + + public static string LogLevelToString(LogLevels logLevel) + { + if (!s_stringByLogLevels.TryGetValue(logLevel, out string? result)) { - if (!s_stringByLogLevels.TryGetValue(logLevel, out string? result)) - { - throw new ArgumentException($"Unknown log level: {logLevel}"); - } - - return result; + throw new ArgumentException($"Unknown log level: {logLevel}"); } - public static LogLevels StringToLogLevel(string s) - { - var pair = s_stringByLogLevels.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown log level: {s}"); - } + return result; + } - return pair.Key; + public static LogLevels StringToLogLevel(string s) + { + var pair = s_stringByLogLevels.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown log level: {s}"); } - #endregion + return pair.Key; + } - #region ParticipantStatus + #endregion - private static readonly Dictionary s_stringByParticipantStatus = new Dictionary - { - [ParticipantStatus.Approved] = "APPROVED", - [ParticipantStatus.NeedsWork] = "NEEDS_WORK", - [ParticipantStatus.Unapproved] = "UNAPPROVED" - }; + #region ParticipantStatus - public static string ParticipantStatusToString(ParticipantStatus participantStatus) - { - if (!s_stringByParticipantStatus.TryGetValue(participantStatus, out string? result)) - { - throw new ArgumentException($"Unknown participant status: {participantStatus}"); - } + private static readonly Dictionary s_stringByParticipantStatus = new() + { + [ParticipantStatus.Approved] = "APPROVED", + [ParticipantStatus.NeedsWork] = "NEEDS_WORK", + [ParticipantStatus.Unapproved] = "UNAPPROVED", + }; - return result; + public static string ParticipantStatusToString(ParticipantStatus participantStatus) + { + if (!s_stringByParticipantStatus.TryGetValue(participantStatus, out string? result)) + { + throw new ArgumentException($"Unknown participant status: {participantStatus}"); } - public static ParticipantStatus StringToParticipantStatus(string s) - { - var pair = s_stringByParticipantStatus.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown participant status: {s}"); - } + return result; + } - return pair.Key; + public static ParticipantStatus StringToParticipantStatus(string s) + { + var pair = s_stringByParticipantStatus.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown participant status: {s}"); } - #endregion + return pair.Key; + } - #region HookTypes + #endregion - private static readonly Dictionary s_stringByHookTypes = new Dictionary - { - [HookTypes.PreReceive] = "PRE_RECEIVE", - [HookTypes.PostReceive] = "POST_RECEIVE", - [HookTypes.PrePullRequestMerge] = "PRE_PULL_REQUEST_MERGE" - }; + #region HookTypes - public static string HookTypeToString(HookTypes hookType) - { - if (!s_stringByHookTypes.TryGetValue(hookType, out string? result)) - { - throw new ArgumentException($"Unknown hook type: {hookType}"); - } + private static readonly Dictionary s_stringByHookTypes = new() + { + [HookTypes.PreReceive] = "PRE_RECEIVE", + [HookTypes.PostReceive] = "POST_RECEIVE", + [HookTypes.PrePullRequestMerge] = "PRE_PULL_REQUEST_MERGE", + }; - return result; + public static string HookTypeToString(HookTypes hookType) + { + if (!s_stringByHookTypes.TryGetValue(hookType, out string? result)) + { + throw new ArgumentException($"Unknown hook type: {hookType}"); } - public static HookTypes StringToHookType(string s) - { - var pair = s_stringByHookTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown hook type: {s}"); - } + return result; + } - return pair.Key; + public static HookTypes StringToHookType(string s) + { + var pair = s_stringByHookTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown hook type: {s}"); } - #endregion + return pair.Key; + } - #region ScopeTypes + #endregion - private static readonly Dictionary s_stringByScopeTypes = new Dictionary - { - [ScopeTypes.Project] = "PROJECT", - [ScopeTypes.Repository] = "REPOSITORY" - }; + #region ScopeTypes - public static string ScopeTypeToString(ScopeTypes scopeType) - { - if (!s_stringByScopeTypes.TryGetValue(scopeType, out string? result)) - { - throw new ArgumentException($"Unknown scope type: {scopeType}"); - } + private static readonly Dictionary s_stringByScopeTypes = new() + { + [ScopeTypes.Project] = "PROJECT", + [ScopeTypes.Repository] = "REPOSITORY", + }; - return result; + public static string ScopeTypeToString(ScopeTypes scopeType) + { + if (!s_stringByScopeTypes.TryGetValue(scopeType, out string? result)) + { + throw new ArgumentException($"Unknown scope type: {scopeType}"); } - public static ScopeTypes StringToScopeType(string s) - { - var pair = s_stringByScopeTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown scope type: {s}"); - } + return result; + } - return pair.Key; + public static ScopeTypes StringToScopeType(string s) + { + var pair = s_stringByScopeTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown scope type: {s}"); } - #endregion + return pair.Key; + } - #region ArchiveFormats + #endregion - private static readonly Dictionary s_stringByArchiveFormats = new Dictionary - { - [ArchiveFormats.Zip] = "zip", - [ArchiveFormats.Tar] = "tar", - [ArchiveFormats.TarGz] = "tar.gz", - [ArchiveFormats.Tgz] = "tgz" - }; + #region ArchiveFormats - public static string ArchiveFormatToString(ArchiveFormats archiveFormat) - { - if (!s_stringByArchiveFormats.TryGetValue(archiveFormat, out string? result)) - { - throw new ArgumentException($"Unknown archive format: {archiveFormat}"); - } + private static readonly Dictionary s_stringByArchiveFormats = new() + { + [ArchiveFormats.Zip] = "zip", + [ArchiveFormats.Tar] = "tar", + [ArchiveFormats.TarGz] = "tar.gz", + [ArchiveFormats.Tgz] = "tgz", + }; - return result; + public static string ArchiveFormatToString(ArchiveFormats archiveFormat) + { + if (!s_stringByArchiveFormats.TryGetValue(archiveFormat, out string? result)) + { + throw new ArgumentException($"Unknown archive format: {archiveFormat}"); } - #endregion + return result; + } - #region WebHookOutcomes + #endregion - private static readonly Dictionary s_stringByWebHookOutcomes = new Dictionary - { - [WebHookOutcomes.Success] = "SUCCESS", - [WebHookOutcomes.Failure] = "FAILURE", - [WebHookOutcomes.Error] = "ERROR" - }; + #region WebHookOutcomes - public static string WebHookOutcomeToString(WebHookOutcomes webHookOutcome) - { - if (!s_stringByWebHookOutcomes.TryGetValue(webHookOutcome, out string? result)) - { - throw new ArgumentException($"Unknown web hook outcome: {webHookOutcome}"); - } + private static readonly Dictionary s_stringByWebHookOutcomes = new() + { + [WebHookOutcomes.Success] = "SUCCESS", + [WebHookOutcomes.Failure] = "FAILURE", + [WebHookOutcomes.Error] = "ERROR", + }; - return result; + public static string WebHookOutcomeToString(WebHookOutcomes webHookOutcome) + { + if (!s_stringByWebHookOutcomes.TryGetValue(webHookOutcome, out string? result)) + { + throw new ArgumentException($"Unknown web hook outcome: {webHookOutcome}"); } - public static string? WebHookOutcomeToString(WebHookOutcomes? webHookOutcome) => webHookOutcome.HasValue - ? WebHookOutcomeToString(webHookOutcome.Value) - : null; + return result; + } - public static WebHookOutcomes StringToWebHookOutcome(string s) - { - var pair = s_stringByWebHookOutcomes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown web hook outcome: {s}"); - } + public static string? WebHookOutcomeToString(WebHookOutcomes? webHookOutcome) => webHookOutcome.HasValue + ? WebHookOutcomeToString(webHookOutcome.Value) + : null; - return pair.Key; + public static WebHookOutcomes StringToWebHookOutcome(string s) + { + var pair = s_stringByWebHookOutcomes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown web hook outcome: {s}"); } - #endregion + return pair.Key; + } - #region AnchorStates + #endregion - private static readonly Dictionary s_stringByAnchorStates = new Dictionary - { - [AnchorStates.Active] = "ACTIVE", - [AnchorStates.Orphaned] = "ORPHANED", - [AnchorStates.All] = "ALL" - }; + #region AnchorStates - public static string AnchorStateToString(AnchorStates anchorState) - { - if (!s_stringByAnchorStates.TryGetValue(anchorState, out string? result)) - { - throw new ArgumentException($"Unknown anchor state: {anchorState}"); - } + private static readonly Dictionary s_stringByAnchorStates = new() + { + [AnchorStates.Active] = "ACTIVE", + [AnchorStates.Orphaned] = "ORPHANED", + [AnchorStates.All] = "ALL", + }; - return result; + public static string AnchorStateToString(AnchorStates anchorState) + { + if (!s_stringByAnchorStates.TryGetValue(anchorState, out string? result)) + { + throw new ArgumentException($"Unknown anchor state: {anchorState}"); } - #endregion - - #region DiffTypes + return result; + } - private static readonly Dictionary s_stringByDiffTypes = new Dictionary - { - [DiffTypes.Effective] = "EFFECTIVE", - [DiffTypes.Range] = "RANGE", - [DiffTypes.Commit] = "COMMIT" - }; + #endregion - public static string DiffTypeToString(DiffTypes diffType) - { - if (!s_stringByDiffTypes.TryGetValue(diffType, out string? result)) - { - throw new ArgumentException($"Unknown diff type: {diffType}"); - } + #region DiffTypes - return result; - } + private static readonly Dictionary s_stringByDiffTypes = new() + { + [DiffTypes.Effective] = "EFFECTIVE", + [DiffTypes.Range] = "RANGE", + [DiffTypes.Commit] = "COMMIT", + }; - public static string? DiffTypeToString(DiffTypes? diffType) + public static string DiffTypeToString(DiffTypes diffType) + { + if (!s_stringByDiffTypes.TryGetValue(diffType, out string? result)) { - return diffType.HasValue - ? DiffTypeToString(diffType.Value) - : null; + throw new ArgumentException($"Unknown diff type: {diffType}"); } - #endregion + return result; + } - #region TagTypes + public static string? DiffTypeToString(DiffTypes? diffType) + { + return diffType.HasValue + ? DiffTypeToString(diffType.Value) + : null; + } - private static readonly Dictionary s_stringByTagTypes = new Dictionary - { - [TagTypes.LightWeight] = "LIGHTWEIGHT", - [TagTypes.Annotated] = "ANNOTATED" - }; + #endregion - public static string TagTypeToString(TagTypes tagType) - { - if (!s_stringByTagTypes.TryGetValue(tagType, out string? result)) - { - throw new ArgumentException($"Unknown tag type: {tagType}"); - } + #region TagTypes - return result; - } + private static readonly Dictionary s_stringByTagTypes = new() + { + [TagTypes.LightWeight] = "LIGHTWEIGHT", + [TagTypes.Annotated] = "ANNOTATED", + }; - #endregion + public static string TagTypeToString(TagTypes tagType) + { + if (!s_stringByTagTypes.TryGetValue(tagType, out string? result)) + { + throw new ArgumentException($"Unknown tag type: {tagType}"); + } - #region RefRestrictionTypes + return result; + } - private static readonly Dictionary s_stringByRefRestrictionTypes = new Dictionary - { - [RefRestrictionTypes.AllChanges] = "read-only", - [RefRestrictionTypes.RewritingHistory] = "fast-forward-only", - [RefRestrictionTypes.Deletion] = "no-deletes", - [RefRestrictionTypes.ChangesWithoutPullRequest] = "pull-request-only" - }; + #endregion - public static string RefRestrictionTypeToString(RefRestrictionTypes refRestrictionType) - { - if (!s_stringByRefRestrictionTypes.TryGetValue(refRestrictionType, out string? result)) - { - throw new ArgumentException($"Unknown ref restriction type: {refRestrictionType}"); - } + #region RefRestrictionTypes - return result; - } + private static readonly Dictionary s_stringByRefRestrictionTypes = new() + { + [RefRestrictionTypes.AllChanges] = "read-only", + [RefRestrictionTypes.RewritingHistory] = "fast-forward-only", + [RefRestrictionTypes.Deletion] = "no-deletes", + [RefRestrictionTypes.ChangesWithoutPullRequest] = "pull-request-only", + }; - public static string? RefRestrictionTypeToString(RefRestrictionTypes? refRestrictionType) + public static string RefRestrictionTypeToString(RefRestrictionTypes refRestrictionType) + { + if (!s_stringByRefRestrictionTypes.TryGetValue(refRestrictionType, out string? result)) { - return refRestrictionType.HasValue - ? RefRestrictionTypeToString(refRestrictionType.Value) - : null; + throw new ArgumentException($"Unknown ref restriction type: {refRestrictionType}"); } - public static RefRestrictionTypes StringToRefRestrictionType(string s) - { - var pair = s_stringByRefRestrictionTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown ref restriction type: {s}"); - } + return result; + } - return pair.Key; - } + public static string? RefRestrictionTypeToString(RefRestrictionTypes? refRestrictionType) + { + return refRestrictionType.HasValue + ? RefRestrictionTypeToString(refRestrictionType.Value) + : null; + } - #endregion + public static RefRestrictionTypes StringToRefRestrictionType(string s) + { + var pair = s_stringByRefRestrictionTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown ref restriction type: {s}"); + } - #region RefMatcherTypes + return pair.Key; + } - private static readonly Dictionary s_stringByRefMatcherTypes = new Dictionary - { - [RefMatcherTypes.Branch] = "BRANCH", - [RefMatcherTypes.Pattern] = "PATTERN", - [RefMatcherTypes.ModelCategory] = "MODEL_CATEGORY", - [RefMatcherTypes.ModelBranch] = "MODEL_BRANCH" - }; + #endregion - private static string RefMatcherTypeToString(RefMatcherTypes refMatcherType) - { - if (!s_stringByRefMatcherTypes.TryGetValue(refMatcherType, out string? result)) - { - throw new ArgumentException($"Unknown ref matcher type: {refMatcherType}"); - } + #region RefMatcherTypes - return result; - } + private static readonly Dictionary s_stringByRefMatcherTypes = new() + { + [RefMatcherTypes.Branch] = "BRANCH", + [RefMatcherTypes.Pattern] = "PATTERN", + [RefMatcherTypes.ModelCategory] = "MODEL_CATEGORY", + [RefMatcherTypes.ModelBranch] = "MODEL_BRANCH", + }; - public static string? RefMatcherTypeToString(RefMatcherTypes? refMatcherType) + private static string RefMatcherTypeToString(RefMatcherTypes refMatcherType) + { + if (!s_stringByRefMatcherTypes.TryGetValue(refMatcherType, out string? result)) { - return refMatcherType.HasValue - ? RefMatcherTypeToString(refMatcherType.Value) - : null; + throw new ArgumentException($"Unknown ref matcher type: {refMatcherType}"); } - #endregion + return result; + } - #region SynchronizeActions + public static string? RefMatcherTypeToString(RefMatcherTypes? refMatcherType) + { + return refMatcherType.HasValue + ? RefMatcherTypeToString(refMatcherType.Value) + : null; + } - private static readonly Dictionary s_stringBySynchronizeActions = new Dictionary - { - [SynchronizeActions.Merge] = "MERGE", - [SynchronizeActions.Discard] = "DISCARD" - }; + #endregion - public static string SynchronizeActionToString(SynchronizeActions synchronizeAction) - { - if (!s_stringBySynchronizeActions.TryGetValue(synchronizeAction, out string? result)) - { - throw new ArgumentException($"Unknown synchronize action: {synchronizeAction}"); - } + #region SynchronizeActions - return result; - } + private static readonly Dictionary s_stringBySynchronizeActions = new() + { + [SynchronizeActions.Merge] = "MERGE", + [SynchronizeActions.Discard] = "DISCARD", + }; - public static SynchronizeActions StringToSynchronizeAction(string s) + public static string SynchronizeActionToString(SynchronizeActions synchronizeAction) + { + if (!s_stringBySynchronizeActions.TryGetValue(synchronizeAction, out string? result)) { - var pair = s_stringBySynchronizeActions.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown synchronize action: {s}"); - } + throw new ArgumentException($"Unknown synchronize action: {synchronizeAction}"); + } + + return result; + } - return pair.Key; + public static SynchronizeActions StringToSynchronizeAction(string s) + { + var pair = s_stringBySynchronizeActions.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown synchronize action: {s}"); } - #endregion + return pair.Key; + } - #region BlockerCommentState + #endregion - private static readonly Dictionary s_stringByBlockerCommentState = new Dictionary - { - [BlockerCommentState.Open] = "OPEN", - [BlockerCommentState.Resolved] = "RESOLVED" - }; + #region BlockerCommentState - public static string BlockerCommentStateToString(BlockerCommentState state) - { - if (!s_stringByBlockerCommentState.TryGetValue(state, out string? result)) - { - throw new ArgumentException($"Unknown blocker comment state: {state}"); - } + private static readonly Dictionary s_stringByBlockerCommentState = new() + { + [BlockerCommentState.Open] = "OPEN", + [BlockerCommentState.Resolved] = "RESOLVED", + }; - return result; + public static string BlockerCommentStateToString(BlockerCommentState state) + { + if (!s_stringByBlockerCommentState.TryGetValue(state, out string? result)) + { + throw new ArgumentException($"Unknown blocker comment state: {state}"); } - public static string? BlockerCommentStateToString(BlockerCommentState? state) => state.HasValue - ? BlockerCommentStateToString(state.Value) - : null; + return result; + } - public static BlockerCommentState StringToBlockerCommentState(string s) - { - var pair = s_stringByBlockerCommentState.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown blocker comment state: {s}"); - } + public static string? BlockerCommentStateToString(BlockerCommentState? state) => state.HasValue + ? BlockerCommentStateToString(state.Value) + : null; - return pair.Key; + public static BlockerCommentState StringToBlockerCommentState(string s) + { + var pair = s_stringByBlockerCommentState.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown blocker comment state: {s}"); } - #endregion + return pair.Key; + } - #region CommentSeverity + #endregion - private static readonly Dictionary s_stringByCommentSeverity = new Dictionary - { - [CommentSeverity.Normal] = "NORMAL", - [CommentSeverity.Blocker] = "BLOCKER" - }; + #region CommentSeverity - public static string CommentSeverityToString(CommentSeverity severity) - { - if (!s_stringByCommentSeverity.TryGetValue(severity, out string? result)) - { - throw new ArgumentException($"Unknown comment severity: {severity}"); - } + private static readonly Dictionary s_stringByCommentSeverity = new() + { + [CommentSeverity.Normal] = "NORMAL", + [CommentSeverity.Blocker] = "BLOCKER", + }; - return result; + public static string CommentSeverityToString(CommentSeverity severity) + { + if (!s_stringByCommentSeverity.TryGetValue(severity, out string? result)) + { + throw new ArgumentException($"Unknown comment severity: {severity}"); } - public static string? CommentSeverityToString(CommentSeverity? severity) => severity.HasValue - ? CommentSeverityToString(severity.Value) - : null; + return result; + } - public static CommentSeverity StringToCommentSeverity(string s) - { - var pair = s_stringByCommentSeverity.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); - // ReSharper disable once SuspiciousTypeConversion.Global - if (EqualityComparer>.Default.Equals(pair)) - { - throw new ArgumentException($"Unknown comment severity: {s}"); - } + public static string? CommentSeverityToString(CommentSeverity? severity) => severity.HasValue + ? CommentSeverityToString(severity.Value) + : null; - return pair.Key; + public static CommentSeverity StringToCommentSeverity(string s) + { + var pair = s_stringByCommentSeverity.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); + // ReSharper disable once SuspiciousTypeConversion.Global + if (EqualityComparer>.Default.Equals(pair)) + { + throw new ArgumentException($"Unknown comment severity: {s}"); } - #endregion + return pair.Key; } -} + + #endregion +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/BlockerCommentStateConverter.cs b/src/Bitbucket.Net/Common/Converters/BlockerCommentStateConverter.cs index 51a090b..caf0338 100644 --- a/src/Bitbucket.Net/Common/Converters/BlockerCommentStateConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/BlockerCommentStateConverter.cs @@ -1,20 +1,19 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +/// +/// JSON converter for enum values. +/// +public class BlockerCommentStateConverter : JsonEnumConverter { - /// - /// JSON converter for enum values. - /// - public class BlockerCommentStateConverter : JsonEnumConverter + protected override string ConvertToString(BlockerCommentState value) { - protected override string ConvertToString(BlockerCommentState value) - { - return BitbucketHelpers.BlockerCommentStateToString(value); - } + return BitbucketHelpers.BlockerCommentStateToString(value); + } - protected override BlockerCommentState ConvertFromString(string s) - { - return BitbucketHelpers.StringToBlockerCommentState(s); - } + protected override BlockerCommentState ConvertFromString(string s) + { + return BitbucketHelpers.StringToBlockerCommentState(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/CommentSeverityConverter.cs b/src/Bitbucket.Net/Common/Converters/CommentSeverityConverter.cs index 57e59f6..b189903 100644 --- a/src/Bitbucket.Net/Common/Converters/CommentSeverityConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/CommentSeverityConverter.cs @@ -1,20 +1,19 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +/// +/// JSON converter for enum values. +/// +public class CommentSeverityConverter : JsonEnumConverter { - /// - /// JSON converter for enum values. - /// - public class CommentSeverityConverter : JsonEnumConverter + protected override string ConvertToString(CommentSeverity value) { - protected override string ConvertToString(CommentSeverity value) - { - return BitbucketHelpers.CommentSeverityToString(value); - } + return BitbucketHelpers.CommentSeverityToString(value); + } - protected override CommentSeverity ConvertFromString(string s) - { - return BitbucketHelpers.StringToCommentSeverity(s); - } + protected override CommentSeverity ConvertFromString(string s) + { + return BitbucketHelpers.StringToCommentSeverity(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/FileTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/FileTypesConverter.cs index dfd3d12..5e297bb 100644 --- a/src/Bitbucket.Net/Common/Converters/FileTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/FileTypesConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class FileTypesConverter : JsonEnumConverter { - public class FileTypesConverter : JsonEnumConverter + protected override string ConvertToString(FileTypes value) { - protected override string ConvertToString(FileTypes value) - { - return BitbucketHelpers.FileTypeToString(value); - } + return BitbucketHelpers.FileTypeToString(value); + } - protected override FileTypes ConvertFromString(string s) - { - return BitbucketHelpers.StringToFileType(s); - } + protected override FileTypes ConvertFromString(string s) + { + return BitbucketHelpers.StringToFileType(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/HookTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/HookTypesConverter.cs index 9935739..a6bd45e 100644 --- a/src/Bitbucket.Net/Common/Converters/HookTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/HookTypesConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class HookTypesConverter : JsonEnumConverter { - public class HookTypesConverter : JsonEnumConverter + protected override string ConvertToString(HookTypes value) { - protected override string ConvertToString(HookTypes value) - { - return BitbucketHelpers.HookTypeToString(value); - } + return BitbucketHelpers.HookTypeToString(value); + } - protected override HookTypes ConvertFromString(string s) - { - return BitbucketHelpers.StringToHookType(s); - } + protected override HookTypes ConvertFromString(string s) + { + return BitbucketHelpers.StringToHookType(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs b/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs index c4306d4..a95bdcc 100644 --- a/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs @@ -3,107 +3,106 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +/// +/// Abstract base class for custom enum converters that convert between enum values and their string representations. +/// +/// The enum type to convert. +public abstract class JsonEnumConverter : JsonConverter + where TEnum : struct, Enum { /// - /// Abstract base class for custom enum converters that convert between enum values and their string representations. + /// Converts an enum value to its string representation. /// - /// The enum type to convert. - public abstract class JsonEnumConverter : JsonConverter - where TEnum : struct, Enum - { - /// - /// Converts an enum value to its string representation. - /// - protected abstract string ConvertToString(TEnum value); + protected abstract string ConvertToString(TEnum value); - /// - /// Converts a string representation to its enum value. - /// - protected abstract TEnum ConvertFromString(string s); + /// + /// Converts a string representation to its enum value. + /// + protected abstract TEnum ConvertFromString(string s); - public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.Null) { - if (reader.TokenType == JsonTokenType.Null) - { - return default; - } - - if (reader.TokenType == JsonTokenType.String) - { - string value = reader.GetString()!; - return ConvertFromString(value); - } - - throw new JsonException($"Unexpected token {reader.TokenType} when parsing enum {typeof(TEnum).Name}."); + return default; } - public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + if (reader.TokenType == JsonTokenType.String) { - writer.WriteStringValue(ConvertToString(value)); + string value = reader.GetString()!; + return ConvertFromString(value); } + + throw new JsonException($"Unexpected token {reader.TokenType} when parsing enum {typeof(TEnum).Name}."); + } + + public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) + { + writer.WriteStringValue(ConvertToString(value)); } +} +/// +/// Abstract base class for custom enum list converters that convert between lists of enum values and their JSON array representations. +/// +/// The enum type to convert. +public abstract class JsonEnumListConverter : JsonConverter?> + where TEnum : struct, Enum +{ /// - /// Abstract base class for custom enum list converters that convert between lists of enum values and their JSON array representations. + /// Converts an enum value to its string representation. /// - /// The enum type to convert. - public abstract class JsonEnumListConverter : JsonConverter?> - where TEnum : struct, Enum + protected abstract string ConvertToString(TEnum value); + + /// + /// Converts a string representation to its enum value. + /// + protected abstract TEnum ConvertFromString(string s); + + public override List? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - /// - /// Converts an enum value to its string representation. - /// - protected abstract string ConvertToString(TEnum value); + if (reader.TokenType == JsonTokenType.Null) + { + return null; + } - /// - /// Converts a string representation to its enum value. - /// - protected abstract TEnum ConvertFromString(string s); + if (reader.TokenType != JsonTokenType.StartArray) + { + throw new JsonException($"Expected StartArray token, got {reader.TokenType}."); + } - public override List? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + var items = new List(); + while (reader.Read()) { - if (reader.TokenType == JsonTokenType.Null) + if (reader.TokenType == JsonTokenType.EndArray) { - return null; + return items; } - if (reader.TokenType != JsonTokenType.StartArray) + if (reader.TokenType == JsonTokenType.String) { - throw new JsonException($"Expected StartArray token, got {reader.TokenType}."); + items.Add(ConvertFromString(reader.GetString()!)); } + } - var items = new List(); - while (reader.Read()) - { - if (reader.TokenType == JsonTokenType.EndArray) - { - return items; - } - - if (reader.TokenType == JsonTokenType.String) - { - items.Add(ConvertFromString(reader.GetString()!)); - } - } + throw new JsonException("Unexpected end of JSON while reading array."); + } - throw new JsonException("Unexpected end of JSON while reading array."); + public override void Write(Utf8JsonWriter writer, List? value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + return; } - public override void Write(Utf8JsonWriter writer, List? value, JsonSerializerOptions options) + writer.WriteStartArray(); + foreach (var item in value) { - if (value is null) - { - writer.WriteNullValue(); - return; - } - - writer.WriteStartArray(); - foreach (var item in value) - { - writer.WriteStringValue(ConvertToString(item)); - } - writer.WriteEndArray(); + writer.WriteStringValue(ConvertToString(item)); } + writer.WriteEndArray(); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/LineTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/LineTypesConverter.cs index e6809e4..90dd770 100644 --- a/src/Bitbucket.Net/Common/Converters/LineTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/LineTypesConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class LineTypesConverter : JsonEnumConverter { - public class LineTypesConverter : JsonEnumConverter + protected override string ConvertToString(LineTypes value) { - protected override string ConvertToString(LineTypes value) - { - return BitbucketHelpers.LineTypeToString(value); - } + return BitbucketHelpers.LineTypeToString(value); + } - protected override LineTypes ConvertFromString(string s) - { - return BitbucketHelpers.StringToLineType(s); - } + protected override LineTypes ConvertFromString(string s) + { + return BitbucketHelpers.StringToLineType(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/ParticipantStatusConverter.cs b/src/Bitbucket.Net/Common/Converters/ParticipantStatusConverter.cs index ea94e57..b0d4f7f 100644 --- a/src/Bitbucket.Net/Common/Converters/ParticipantStatusConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/ParticipantStatusConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class ParticipantStatusConverter : JsonEnumConverter { - public class ParticipantStatusConverter : JsonEnumConverter + protected override string ConvertToString(ParticipantStatus value) { - protected override string ConvertToString(ParticipantStatus value) - { - return BitbucketHelpers.ParticipantStatusToString(value); - } + return BitbucketHelpers.ParticipantStatusToString(value); + } - protected override ParticipantStatus ConvertFromString(string s) - { - return BitbucketHelpers.StringToParticipantStatus(s); - } + protected override ParticipantStatus ConvertFromString(string s) + { + return BitbucketHelpers.StringToParticipantStatus(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs b/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs index e74e09a..c2c33f5 100644 --- a/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.Core.Admin; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class PermissionsConverter : JsonEnumConverter { - public class PermissionsConverter : JsonEnumConverter + protected override string ConvertToString(Permissions value) { - protected override string ConvertToString(Permissions value) - { - return BitbucketHelpers.PermissionToString(value); - } + return BitbucketHelpers.PermissionToString(value); + } - protected override Permissions ConvertFromString(string s) - { - return BitbucketHelpers.StringToPermission(s); - } + protected override Permissions ConvertFromString(string s) + { + return BitbucketHelpers.StringToPermission(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/PullRequestStatesConverter.cs b/src/Bitbucket.Net/Common/Converters/PullRequestStatesConverter.cs index 59ca6de..1e4a0f8 100644 --- a/src/Bitbucket.Net/Common/Converters/PullRequestStatesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/PullRequestStatesConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class PullRequestStatesConverter : JsonEnumConverter { - public class PullRequestStatesConverter : JsonEnumConverter + protected override string ConvertToString(PullRequestStates value) { - protected override string ConvertToString(PullRequestStates value) - { - return BitbucketHelpers.PullRequestStateToString(value); - } + return BitbucketHelpers.PullRequestStateToString(value); + } - protected override PullRequestStates ConvertFromString(string s) - { - return BitbucketHelpers.StringToPullRequestState(s); - } + protected override PullRequestStates ConvertFromString(string s) + { + return BitbucketHelpers.StringToPullRequestState(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/RefRestrictionTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/RefRestrictionTypesConverter.cs index e8bda96..8ffb269 100644 --- a/src/Bitbucket.Net/Common/Converters/RefRestrictionTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/RefRestrictionTypesConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.RefRestrictions; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class RefRestrictionTypesConverter : JsonEnumConverter { - public class RefRestrictionTypesConverter : JsonEnumConverter + protected override string ConvertToString(RefRestrictionTypes value) { - protected override string ConvertToString(RefRestrictionTypes value) - { - return BitbucketHelpers.RefRestrictionTypeToString(value); - } + return BitbucketHelpers.RefRestrictionTypeToString(value); + } - protected override RefRestrictionTypes ConvertFromString(string s) - { - return BitbucketHelpers.StringToRefRestrictionType(s); - } + protected override RefRestrictionTypes ConvertFromString(string s) + { + return BitbucketHelpers.StringToRefRestrictionType(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/RolesConverter.cs b/src/Bitbucket.Net/Common/Converters/RolesConverter.cs index 4eaeb61..5a10b18 100644 --- a/src/Bitbucket.Net/Common/Converters/RolesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/RolesConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class RolesConverter : JsonEnumConverter { - public class RolesConverter : JsonEnumConverter + protected override string ConvertToString(Roles value) { - protected override string ConvertToString(Roles value) - { - return BitbucketHelpers.RoleToString(value); - } + return BitbucketHelpers.RoleToString(value); + } - protected override Roles ConvertFromString(string s) - { - return BitbucketHelpers.StringToRole(s); - } + protected override Roles ConvertFromString(string s) + { + return BitbucketHelpers.StringToRole(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/ScopeTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/ScopeTypesConverter.cs index f71e90a..462082e 100644 --- a/src/Bitbucket.Net/Common/Converters/ScopeTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/ScopeTypesConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class ScopeTypesConverter : JsonEnumConverter { - public class ScopeTypesConverter : JsonEnumConverter + protected override string ConvertToString(ScopeTypes value) { - protected override string ConvertToString(ScopeTypes value) - { - return BitbucketHelpers.ScopeTypeToString(value); - } + return BitbucketHelpers.ScopeTypeToString(value); + } - protected override ScopeTypes ConvertFromString(string s) - { - return BitbucketHelpers.StringToScopeType(s); - } + protected override ScopeTypes ConvertFromString(string s) + { + return BitbucketHelpers.StringToScopeType(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/SynchronizeActionsConverter.cs b/src/Bitbucket.Net/Common/Converters/SynchronizeActionsConverter.cs index b1cdca3..0e38a17 100644 --- a/src/Bitbucket.Net/Common/Converters/SynchronizeActionsConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/SynchronizeActionsConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.RefSync; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class SynchronizeActionsConverter : JsonEnumConverter { - public class SynchronizeActionsConverter : JsonEnumConverter + protected override string ConvertToString(SynchronizeActions value) { - protected override string ConvertToString(SynchronizeActions value) - { - return BitbucketHelpers.SynchronizeActionToString(value); - } + return BitbucketHelpers.SynchronizeActionToString(value); + } - protected override SynchronizeActions ConvertFromString(string s) - { - return BitbucketHelpers.StringToSynchronizeAction(s); - } + protected override SynchronizeActions ConvertFromString(string s) + { + return BitbucketHelpers.StringToSynchronizeAction(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs b/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs index 5cb074e..79c6bf2 100644 --- a/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs @@ -2,57 +2,56 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +/// +/// Converts Unix timestamps (seconds since epoch) to/from DateTimeOffset. +/// +public sealed class UnixDateTimeOffsetConverter : JsonConverter { - /// - /// Converts Unix timestamps (seconds since epoch) to/from DateTimeOffset. - /// - public sealed class UnixDateTimeOffsetConverter : JsonConverter + public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + return reader.TokenType switch { - return reader.TokenType switch - { - JsonTokenType.Null => default, - JsonTokenType.Number when reader.TryGetInt64(out long unixTime) => unixTime.FromUnixTimeSeconds(), - JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) => unixTime.FromUnixTimeSeconds(), - _ => throw new JsonException($"Cannot convert {reader.TokenType} to {nameof(DateTimeOffset)}.") - }; - } + JsonTokenType.Null => default, + JsonTokenType.Number when reader.TryGetInt64(out long unixTime) => unixTime.FromUnixTimeSeconds(), + JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) => unixTime.FromUnixTimeSeconds(), + _ => throw new JsonException($"Cannot convert {reader.TokenType} to {nameof(DateTimeOffset)}."), + }; + } + + public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) + { + writer.WriteNumberValue(value.ToUnixTimeSeconds()); + } +} - public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) +/// +/// Converts Unix timestamps (seconds since epoch) to/from nullable DateTimeOffset. +/// +public sealed class NullableUnixDateTimeOffsetConverter : JsonConverter +{ + public override DateTimeOffset? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + return reader.TokenType switch { - writer.WriteNumberValue(value.ToUnixTimeSeconds()); - } + JsonTokenType.Null => null, + JsonTokenType.Number when reader.TryGetInt64(out long unixTime) => unixTime.FromUnixTimeSeconds(), + JsonTokenType.String when string.IsNullOrEmpty(reader.GetString()) => null, + JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) => unixTime.FromUnixTimeSeconds(), + _ => throw new JsonException($"Cannot convert {reader.TokenType} to nullable {nameof(DateTimeOffset)}."), + }; } - /// - /// Converts Unix timestamps (seconds since epoch) to/from nullable DateTimeOffset. - /// - public sealed class NullableUnixDateTimeOffsetConverter : JsonConverter + public override void Write(Utf8JsonWriter writer, DateTimeOffset? value, JsonSerializerOptions options) { - public override DateTimeOffset? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + if (value.HasValue) { - return reader.TokenType switch - { - JsonTokenType.Null => null, - JsonTokenType.Number when reader.TryGetInt64(out long unixTime) => unixTime.FromUnixTimeSeconds(), - JsonTokenType.String when string.IsNullOrEmpty(reader.GetString()) => null, - JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) => unixTime.FromUnixTimeSeconds(), - _ => throw new JsonException($"Cannot convert {reader.TokenType} to nullable {nameof(DateTimeOffset)}.") - }; + writer.WriteNumberValue(value.Value.ToUnixTimeSeconds()); } - - public override void Write(Utf8JsonWriter writer, DateTimeOffset? value, JsonSerializerOptions options) + else { - if (value.HasValue) - { - writer.WriteNumberValue(value.Value.ToUnixTimeSeconds()); - } - else - { - writer.WriteNullValue(); - } + writer.WriteNullValue(); } } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Converters/WebHookOutcomesConverter.cs b/src/Bitbucket.Net/Common/Converters/WebHookOutcomesConverter.cs index a7496bc..d906cc6 100644 --- a/src/Bitbucket.Net/Common/Converters/WebHookOutcomesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/WebHookOutcomesConverter.cs @@ -1,17 +1,16 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Converters +namespace Bitbucket.Net.Common.Converters; + +public class WebHookOutcomesConverter : JsonEnumConverter { - public class WebHookOutcomesConverter : JsonEnumConverter + protected override string ConvertToString(WebHookOutcomes value) { - protected override string ConvertToString(WebHookOutcomes value) - { - return BitbucketHelpers.WebHookOutcomeToString(value); - } + return BitbucketHelpers.WebHookOutcomeToString(value); + } - protected override WebHookOutcomes ConvertFromString(string s) - { - return BitbucketHelpers.StringToWebHookOutcome(s); - } + protected override WebHookOutcomes ConvertFromString(string s) + { + return BitbucketHelpers.StringToWebHookOutcome(s); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs b/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs index 40c47de..ba01a54 100644 --- a/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs +++ b/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs @@ -2,35 +2,34 @@ using System.Collections.Generic; using System.Net.Http; -namespace Bitbucket.Net.Common +namespace Bitbucket.Net.Common; + +public class DynamicMultipartFormDataContent : IEnumerable { - public class DynamicMultipartFormDataContent : IEnumerable + private readonly MultipartFormDataContent _multipartFormDataContent = []; + + public void Add(HttpContent value, string key) { - private readonly MultipartFormDataContent _multipartFormDataContent = new MultipartFormDataContent(); + _multipartFormDataContent.Add(value, key); + } - public void Add(HttpContent value, string key) + public void Add(T t, HttpContent? value, string key) + { + if (!EqualityComparer.Default.Equals(t, default) && value is not null) { _multipartFormDataContent.Add(value, key); } + } - public void Add(T t, HttpContent? value, string key) - { - if (!EqualityComparer.Default.Equals(t, default(T)) && value is not null) - { - _multipartFormDataContent.Add(value, key); - } - } - - public MultipartFormDataContent ToMultipartFormDataContent() => _multipartFormDataContent; + public MultipartFormDataContent ToMultipartFormDataContent() => _multipartFormDataContent; - public IEnumerator GetEnumerator() - { - return _multipartFormDataContent.GetEnumerator(); - } + public IEnumerator GetEnumerator() + { + return _multipartFormDataContent.GetEnumerator(); + } - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketApiException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketApiException.cs index 87ba60c..a12fd73 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketApiException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketApiException.cs @@ -1,117 +1,116 @@ +using Bitbucket.Net.Common.Models; using System; using System.Collections.Generic; using System.Net; -using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Common.Exceptions +namespace Bitbucket.Net.Common.Exceptions; + +/// +/// Base exception for all Bitbucket API errors. Contains detailed error information +/// from the Bitbucket Server response. +/// +public class BitbucketApiException : Exception { /// - /// Base exception for all Bitbucket API errors. Contains detailed error information - /// from the Bitbucket Server response. + /// Gets the HTTP status code returned by the Bitbucket Server. /// - public class BitbucketApiException : Exception - { - /// - /// Gets the HTTP status code returned by the Bitbucket Server. - /// - public HttpStatusCode StatusCode { get; } + public HttpStatusCode StatusCode { get; } - /// - /// Gets the context information from the first error, if available. - /// This typically contains the field or resource that caused the error. - /// - public string? Context { get; } + /// + /// Gets the context information from the first error, if available. + /// This typically contains the field or resource that caused the error. + /// + public string? Context { get; } - /// - /// Gets the collection of errors returned by the Bitbucket Server. - /// - public IReadOnlyList Errors { get; } + /// + /// Gets the collection of errors returned by the Bitbucket Server. + /// + public IReadOnlyList Errors { get; } - /// - /// Gets the request URL that caused the error, if available. - /// - public string? RequestUrl { get; } + /// + /// Gets the request URL that caused the error, if available. + /// + public string? RequestUrl { get; } - /// - /// Initializes a new instance of the class. - /// - /// The error message. - /// The HTTP status code. - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - public BitbucketApiException(string message, HttpStatusCode statusCode, IReadOnlyList errors, string? requestUrl = null) - : base(message) - { - StatusCode = statusCode; - Errors = errors ?? Array.Empty(); - Context = errors?.Count > 0 ? errors[0].Context : null; - RequestUrl = requestUrl; - } + /// + /// Initializes a new instance of the class. + /// + /// The error message. + /// The HTTP status code. + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + public BitbucketApiException(string message, HttpStatusCode statusCode, IReadOnlyList errors, string? requestUrl = null) + : base(message) + { + StatusCode = statusCode; + Errors = errors ?? []; + Context = errors?.Count > 0 ? errors[0].Context : null; + RequestUrl = requestUrl; + } - /// - /// Initializes a new instance of the class with an inner exception. - /// - /// The error message. - /// The HTTP status code. - /// The collection of errors from the Bitbucket response. - /// The inner exception. - /// The request URL that caused the error. - public BitbucketApiException(string message, HttpStatusCode statusCode, IReadOnlyList errors, Exception innerException, string? requestUrl = null) - : base(message, innerException) - { - StatusCode = statusCode; - Errors = errors ?? Array.Empty(); - Context = errors?.Count > 0 ? errors[0].Context : null; - RequestUrl = requestUrl; - } + /// + /// Initializes a new instance of the class with an inner exception. + /// + /// The error message. + /// The HTTP status code. + /// The collection of errors from the Bitbucket response. + /// The inner exception. + /// The request URL that caused the error. + public BitbucketApiException(string message, HttpStatusCode statusCode, IReadOnlyList errors, Exception innerException, string? requestUrl = null) + : base(message, innerException) + { + StatusCode = statusCode; + Errors = errors ?? []; + Context = errors?.Count > 0 ? errors[0].Context : null; + RequestUrl = requestUrl; + } + + /// + /// Creates the appropriate exception type based on the HTTP status code. + /// + /// The HTTP status code. + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + /// A typed exception matching the HTTP status code. + public static BitbucketApiException Create(int statusCode, IReadOnlyList errors, string? requestUrl = null) + { + var httpStatusCode = (HttpStatusCode)statusCode; + string message = BuildErrorMessage(httpStatusCode, errors); - /// - /// Creates the appropriate exception type based on the HTTP status code. - /// - /// The HTTP status code. - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - /// A typed exception matching the HTTP status code. - public static BitbucketApiException Create(int statusCode, IReadOnlyList errors, string? requestUrl = null) + return statusCode switch { - var httpStatusCode = (HttpStatusCode)statusCode; - string message = BuildErrorMessage(httpStatusCode, errors); + 400 => new BitbucketBadRequestException(message, errors, requestUrl), + 401 => new BitbucketAuthenticationException(message, errors, requestUrl), + 403 => new BitbucketForbiddenException(message, errors, requestUrl), + 404 => new BitbucketNotFoundException(message, errors, requestUrl), + 409 => new BitbucketConflictException(message, errors, requestUrl), + 422 => new BitbucketValidationException(message, errors, requestUrl), + 429 => new BitbucketRateLimitException(message, errors, requestUrl), + >= 500 and < 600 => new BitbucketServerException(message, httpStatusCode, errors, requestUrl), + _ => new BitbucketApiException(message, httpStatusCode, errors, requestUrl), + }; + } - return statusCode switch - { - 400 => new BitbucketBadRequestException(message, errors, requestUrl), - 401 => new BitbucketAuthenticationException(message, errors, requestUrl), - 403 => new BitbucketForbiddenException(message, errors, requestUrl), - 404 => new BitbucketNotFoundException(message, errors, requestUrl), - 409 => new BitbucketConflictException(message, errors, requestUrl), - 422 => new BitbucketValidationException(message, errors, requestUrl), - 429 => new BitbucketRateLimitException(message, errors, requestUrl), - >= 500 and < 600 => new BitbucketServerException(message, httpStatusCode, errors, requestUrl), - _ => new BitbucketApiException(message, httpStatusCode, errors, requestUrl) - }; + private static string BuildErrorMessage(HttpStatusCode statusCode, IReadOnlyList errors) + { + if (errors == null || errors.Count == 0) + { + return $"Bitbucket API request failed with status {(int)statusCode} ({statusCode})"; } - private static string BuildErrorMessage(HttpStatusCode statusCode, IReadOnlyList errors) + var messages = new List(errors.Count); + foreach (var error in errors) { - if (errors == null || errors.Count == 0) + if (!string.IsNullOrEmpty(error.Context)) { - return $"Bitbucket API request failed with status {(int)statusCode} ({statusCode})"; + messages.Add($"[{error.Context}] {error.Message}"); } - - var messages = new List(errors.Count); - foreach (var error in errors) + else { - if (!string.IsNullOrEmpty(error.Context)) - { - messages.Add($"[{error.Context}] {error.Message}"); - } - else - { - messages.Add(error.Message); - } + messages.Add(error.Message); } - - return $"Bitbucket API request failed with status {(int)statusCode} ({statusCode}): {string.Join("; ", messages)}"; } + + return $"Bitbucket API request failed with status {(int)statusCode} ({statusCode}): {string.Join("; ", messages)}"; } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketAuthenticationException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketAuthenticationException.cs index e87adc7..31a3a8d 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketAuthenticationException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketAuthenticationException.cs @@ -1,37 +1,36 @@ +using Bitbucket.Net.Common.Models; using System; using System.Collections.Generic; using System.Net; -using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Common.Exceptions +namespace Bitbucket.Net.Common.Exceptions; + +/// +/// Exception thrown when authentication fails (HTTP 401 Unauthorized). +/// This typically indicates invalid or missing credentials. +/// +public class BitbucketAuthenticationException : BitbucketApiException { /// - /// Exception thrown when authentication fails (HTTP 401 Unauthorized). - /// This typically indicates invalid or missing credentials. + /// Initializes a new instance of the class. /// - public class BitbucketAuthenticationException : BitbucketApiException + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + public BitbucketAuthenticationException(string message, IReadOnlyList errors, string? requestUrl = null) + : base(message, HttpStatusCode.Unauthorized, errors, requestUrl) { - /// - /// Initializes a new instance of the class. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - public BitbucketAuthenticationException(string message, IReadOnlyList errors, string? requestUrl = null) - : base(message, HttpStatusCode.Unauthorized, errors, requestUrl) - { - } + } - /// - /// Initializes a new instance of the class with an inner exception. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The inner exception. - /// The request URL that caused the error. - public BitbucketAuthenticationException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) - : base(message, HttpStatusCode.Unauthorized, errors, innerException, requestUrl) - { - } + /// + /// Initializes a new instance of the class with an inner exception. + /// + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The inner exception. + /// The request URL that caused the error. + public BitbucketAuthenticationException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) + : base(message, HttpStatusCode.Unauthorized, errors, innerException, requestUrl) + { } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketBadRequestException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketBadRequestException.cs index 631df4d..8350993 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketBadRequestException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketBadRequestException.cs @@ -1,37 +1,36 @@ +using Bitbucket.Net.Common.Models; using System; using System.Collections.Generic; using System.Net; -using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Common.Exceptions +namespace Bitbucket.Net.Common.Exceptions; + +/// +/// Exception thrown when the request is malformed (HTTP 400 Bad Request). +/// This indicates invalid parameters, malformed JSON, or other request-level issues. +/// +public class BitbucketBadRequestException : BitbucketApiException { /// - /// Exception thrown when the request is malformed (HTTP 400 Bad Request). - /// This indicates invalid parameters, malformed JSON, or other request-level issues. + /// Initializes a new instance of the class. /// - public class BitbucketBadRequestException : BitbucketApiException + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + public BitbucketBadRequestException(string message, IReadOnlyList errors, string? requestUrl = null) + : base(message, HttpStatusCode.BadRequest, errors, requestUrl) { - /// - /// Initializes a new instance of the class. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - public BitbucketBadRequestException(string message, IReadOnlyList errors, string? requestUrl = null) - : base(message, HttpStatusCode.BadRequest, errors, requestUrl) - { - } + } - /// - /// Initializes a new instance of the class with an inner exception. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The inner exception. - /// The request URL that caused the error. - public BitbucketBadRequestException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) - : base(message, HttpStatusCode.BadRequest, errors, innerException, requestUrl) - { - } + /// + /// Initializes a new instance of the class with an inner exception. + /// + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The inner exception. + /// The request URL that caused the error. + public BitbucketBadRequestException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) + : base(message, HttpStatusCode.BadRequest, errors, innerException, requestUrl) + { } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketConflictException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketConflictException.cs index f80d3e1..e863348 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketConflictException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketConflictException.cs @@ -1,37 +1,36 @@ +using Bitbucket.Net.Common.Models; using System; using System.Collections.Generic; using System.Net; -using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Common.Exceptions +namespace Bitbucket.Net.Common.Exceptions; + +/// +/// Exception thrown when there is a resource conflict (HTTP 409 Conflict). +/// This typically indicates a merge conflict, duplicate resource, or state conflict. +/// +public class BitbucketConflictException : BitbucketApiException { /// - /// Exception thrown when there is a resource conflict (HTTP 409 Conflict). - /// This typically indicates a merge conflict, duplicate resource, or state conflict. + /// Initializes a new instance of the class. /// - public class BitbucketConflictException : BitbucketApiException + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + public BitbucketConflictException(string message, IReadOnlyList errors, string? requestUrl = null) + : base(message, HttpStatusCode.Conflict, errors, requestUrl) { - /// - /// Initializes a new instance of the class. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - public BitbucketConflictException(string message, IReadOnlyList errors, string? requestUrl = null) - : base(message, HttpStatusCode.Conflict, errors, requestUrl) - { - } + } - /// - /// Initializes a new instance of the class with an inner exception. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The inner exception. - /// The request URL that caused the error. - public BitbucketConflictException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) - : base(message, HttpStatusCode.Conflict, errors, innerException, requestUrl) - { - } + /// + /// Initializes a new instance of the class with an inner exception. + /// + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The inner exception. + /// The request URL that caused the error. + public BitbucketConflictException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) + : base(message, HttpStatusCode.Conflict, errors, innerException, requestUrl) + { } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketForbiddenException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketForbiddenException.cs index e63fc6e..6daf64f 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketForbiddenException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketForbiddenException.cs @@ -1,37 +1,36 @@ +using Bitbucket.Net.Common.Models; using System; using System.Collections.Generic; using System.Net; -using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Common.Exceptions +namespace Bitbucket.Net.Common.Exceptions; + +/// +/// Exception thrown when access is forbidden (HTTP 403 Forbidden). +/// This indicates the user is authenticated but lacks permission for the requested operation. +/// +public class BitbucketForbiddenException : BitbucketApiException { /// - /// Exception thrown when access is forbidden (HTTP 403 Forbidden). - /// This indicates the user is authenticated but lacks permission for the requested operation. + /// Initializes a new instance of the class. /// - public class BitbucketForbiddenException : BitbucketApiException + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + public BitbucketForbiddenException(string message, IReadOnlyList errors, string? requestUrl = null) + : base(message, HttpStatusCode.Forbidden, errors, requestUrl) { - /// - /// Initializes a new instance of the class. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - public BitbucketForbiddenException(string message, IReadOnlyList errors, string? requestUrl = null) - : base(message, HttpStatusCode.Forbidden, errors, requestUrl) - { - } + } - /// - /// Initializes a new instance of the class with an inner exception. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The inner exception. - /// The request URL that caused the error. - public BitbucketForbiddenException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) - : base(message, HttpStatusCode.Forbidden, errors, innerException, requestUrl) - { - } + /// + /// Initializes a new instance of the class with an inner exception. + /// + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The inner exception. + /// The request URL that caused the error. + public BitbucketForbiddenException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) + : base(message, HttpStatusCode.Forbidden, errors, innerException, requestUrl) + { } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketNotFoundException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketNotFoundException.cs index 1d1c609..f141c9d 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketNotFoundException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketNotFoundException.cs @@ -1,37 +1,36 @@ +using Bitbucket.Net.Common.Models; using System; using System.Collections.Generic; using System.Net; -using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Common.Exceptions +namespace Bitbucket.Net.Common.Exceptions; + +/// +/// Exception thrown when a requested resource is not found (HTTP 404 Not Found). +/// This typically indicates the project, repository, branch, or other resource does not exist. +/// +public class BitbucketNotFoundException : BitbucketApiException { /// - /// Exception thrown when a requested resource is not found (HTTP 404 Not Found). - /// This typically indicates the project, repository, branch, or other resource does not exist. + /// Initializes a new instance of the class. /// - public class BitbucketNotFoundException : BitbucketApiException + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + public BitbucketNotFoundException(string message, IReadOnlyList errors, string? requestUrl = null) + : base(message, HttpStatusCode.NotFound, errors, requestUrl) { - /// - /// Initializes a new instance of the class. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - public BitbucketNotFoundException(string message, IReadOnlyList errors, string? requestUrl = null) - : base(message, HttpStatusCode.NotFound, errors, requestUrl) - { - } + } - /// - /// Initializes a new instance of the class with an inner exception. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The inner exception. - /// The request URL that caused the error. - public BitbucketNotFoundException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) - : base(message, HttpStatusCode.NotFound, errors, innerException, requestUrl) - { - } + /// + /// Initializes a new instance of the class with an inner exception. + /// + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The inner exception. + /// The request URL that caused the error. + public BitbucketNotFoundException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) + : base(message, HttpStatusCode.NotFound, errors, innerException, requestUrl) + { } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketRateLimitException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketRateLimitException.cs index d09d959..48b2757 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketRateLimitException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketRateLimitException.cs @@ -1,37 +1,36 @@ +using Bitbucket.Net.Common.Models; using System; using System.Collections.Generic; using System.Net; -using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Common.Exceptions +namespace Bitbucket.Net.Common.Exceptions; + +/// +/// Exception thrown when rate limiting is applied (HTTP 429 Too Many Requests). +/// This indicates too many requests have been made in a given time period. +/// +public class BitbucketRateLimitException : BitbucketApiException { /// - /// Exception thrown when rate limiting is applied (HTTP 429 Too Many Requests). - /// This indicates too many requests have been made in a given time period. + /// Initializes a new instance of the class. /// - public class BitbucketRateLimitException : BitbucketApiException + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + public BitbucketRateLimitException(string message, IReadOnlyList errors, string? requestUrl = null) + : base(message, HttpStatusCode.TooManyRequests, errors, requestUrl) { - /// - /// Initializes a new instance of the class. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - public BitbucketRateLimitException(string message, IReadOnlyList errors, string? requestUrl = null) - : base(message, HttpStatusCode.TooManyRequests, errors, requestUrl) - { - } + } - /// - /// Initializes a new instance of the class with an inner exception. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The inner exception. - /// The request URL that caused the error. - public BitbucketRateLimitException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) - : base(message, HttpStatusCode.TooManyRequests, errors, innerException, requestUrl) - { - } + /// + /// Initializes a new instance of the class with an inner exception. + /// + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The inner exception. + /// The request URL that caused the error. + public BitbucketRateLimitException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) + : base(message, HttpStatusCode.TooManyRequests, errors, innerException, requestUrl) + { } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketServerException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketServerException.cs index 318c15d..8a6cf03 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketServerException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketServerException.cs @@ -1,39 +1,38 @@ +using Bitbucket.Net.Common.Models; using System; using System.Collections.Generic; using System.Net; -using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Common.Exceptions +namespace Bitbucket.Net.Common.Exceptions; + +/// +/// Exception thrown when a server error occurs (HTTP 5xx). +/// This indicates an internal server error on the Bitbucket Server side. +/// +public class BitbucketServerException : BitbucketApiException { /// - /// Exception thrown when a server error occurs (HTTP 5xx). - /// This indicates an internal server error on the Bitbucket Server side. + /// Initializes a new instance of the class. /// - public class BitbucketServerException : BitbucketApiException + /// The error message. + /// The HTTP status code (must be 5xx). + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + public BitbucketServerException(string message, HttpStatusCode statusCode, IReadOnlyList errors, string? requestUrl = null) + : base(message, statusCode, errors, requestUrl) { - /// - /// Initializes a new instance of the class. - /// - /// The error message. - /// The HTTP status code (must be 5xx). - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - public BitbucketServerException(string message, HttpStatusCode statusCode, IReadOnlyList errors, string? requestUrl = null) - : base(message, statusCode, errors, requestUrl) - { - } + } - /// - /// Initializes a new instance of the class with an inner exception. - /// - /// The error message. - /// The HTTP status code (must be 5xx). - /// The collection of errors from the Bitbucket response. - /// The inner exception. - /// The request URL that caused the error. - public BitbucketServerException(string message, HttpStatusCode statusCode, IReadOnlyList errors, Exception innerException, string? requestUrl = null) - : base(message, statusCode, errors, innerException, requestUrl) - { - } + /// + /// Initializes a new instance of the class with an inner exception. + /// + /// The error message. + /// The HTTP status code (must be 5xx). + /// The collection of errors from the Bitbucket response. + /// The inner exception. + /// The request URL that caused the error. + public BitbucketServerException(string message, HttpStatusCode statusCode, IReadOnlyList errors, Exception innerException, string? requestUrl = null) + : base(message, statusCode, errors, innerException, requestUrl) + { } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketValidationException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketValidationException.cs index 9338584..59b68c2 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketValidationException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketValidationException.cs @@ -1,37 +1,36 @@ +using Bitbucket.Net.Common.Models; using System; using System.Collections.Generic; using System.Net; -using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Common.Exceptions +namespace Bitbucket.Net.Common.Exceptions; + +/// +/// Exception thrown when validation fails (HTTP 422 Unprocessable Entity). +/// This indicates the request was well-formed but contained semantic errors. +/// +public class BitbucketValidationException : BitbucketApiException { /// - /// Exception thrown when validation fails (HTTP 422 Unprocessable Entity). - /// This indicates the request was well-formed but contained semantic errors. + /// Initializes a new instance of the class. /// - public class BitbucketValidationException : BitbucketApiException + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The request URL that caused the error. + public BitbucketValidationException(string message, IReadOnlyList errors, string? requestUrl = null) + : base(message, HttpStatusCode.UnprocessableEntity, errors, requestUrl) { - /// - /// Initializes a new instance of the class. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The request URL that caused the error. - public BitbucketValidationException(string message, IReadOnlyList errors, string? requestUrl = null) - : base(message, HttpStatusCode.UnprocessableEntity, errors, requestUrl) - { - } + } - /// - /// Initializes a new instance of the class with an inner exception. - /// - /// The error message. - /// The collection of errors from the Bitbucket response. - /// The inner exception. - /// The request URL that caused the error. - public BitbucketValidationException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) - : base(message, HttpStatusCode.UnprocessableEntity, errors, innerException, requestUrl) - { - } + /// + /// Initializes a new instance of the class with an inner exception. + /// + /// The error message. + /// The collection of errors from the Bitbucket response. + /// The inner exception. + /// The request URL that caused the error. + public BitbucketValidationException(string message, IReadOnlyList errors, Exception innerException, string? requestUrl = null) + : base(message, HttpStatusCode.UnprocessableEntity, errors, innerException, requestUrl) + { } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs b/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs index 8b89ceb..49557d4 100644 --- a/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs +++ b/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs @@ -1,19 +1,18 @@ -using System; -using Flurl.Http; +using Flurl.Http; +using System; -namespace Bitbucket.Net.Common +namespace Bitbucket.Net.Common; + +public static class FlurlRequestExtensions { - public static class FlurlRequestExtensions + public static IFlurlRequest WithAuthentication(this IFlurlRequest request, Func? getToken, string? userName, string? password) { - public static IFlurlRequest WithAuthentication(this IFlurlRequest request, Func? getToken, string? userName, string? password) + if (getToken != null) { - if (getToken != null) - { - string token = getToken(); - return request.WithOAuthBearerToken(token); - } - - return request.WithBasicAuth(userName, password); + string token = getToken(); + return request.WithOAuthBearerToken(token); } + + return request.WithBasicAuth(userName, password); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Mcp/DiffStreamingExtensions.cs b/src/Bitbucket.Net/Common/Mcp/DiffStreamingExtensions.cs index 7a44541..422f478 100644 --- a/src/Bitbucket.Net/Common/Mcp/DiffStreamingExtensions.cs +++ b/src/Bitbucket.Net/Common/Mcp/DiffStreamingExtensions.cs @@ -1,359 +1,349 @@ +using Bitbucket.Net.Models.Core.Projects; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Common.Mcp +namespace Bitbucket.Net.Common.Mcp; + +/// +/// MCP-optimized diff streaming extensions for context window management. +/// Diffs are typically the largest response payloads in MCP usage (100KB-10MB+). +/// These extensions provide line-count-aware streaming with early termination. +/// +public static class DiffStreamingExtensions { /// - /// MCP-optimized diff streaming extensions for context window management. - /// Diffs are typically the largest response payloads in MCP usage (100KB-10MB+). - /// These extensions provide line-count-aware streaming with early termination. + /// Streams diff hunks from an async enumerable of diffs with line and file limits. + /// Enables early termination when MCP context window limits are reached. /// - public static class DiffStreamingExtensions + /// The async enumerable of diffs to process. + /// Maximum total lines to yield across all diffs. Null for unlimited. + /// Maximum number of files to process. Null for unlimited. + /// Cancellation token. + /// An async enumerable of diff results with truncation metadata. + public static async IAsyncEnumerable StreamDiffsWithLimitsAsync( + this IAsyncEnumerable diffs, + int? maxLines = null, + int? maxFiles = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) { - /// - /// Streams diff hunks from an async enumerable of diffs with line and file limits. - /// Enables early termination when MCP context window limits are reached. - /// - /// The async enumerable of diffs to process. - /// Maximum total lines to yield across all diffs. Null for unlimited. - /// Maximum number of files to process. Null for unlimited. - /// Cancellation token. - /// An async enumerable of diff results with truncation metadata. - public static async IAsyncEnumerable StreamDiffsWithLimitsAsync( - this IAsyncEnumerable diffs, - int? maxLines = null, - int? maxFiles = null, - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - int totalLines = 0; - int totalFiles = 0; + int totalLines = 0; + int totalFiles = 0; - await foreach (var diff in diffs.WithCancellation(cancellationToken).ConfigureAwait(false)) + await foreach (var diff in diffs.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + // Check file limit + if (maxFiles.HasValue && totalFiles >= maxFiles.Value) { - // Check file limit - if (maxFiles.HasValue && totalFiles >= maxFiles.Value) - { - yield return DiffStreamResult.CreateTruncated(totalLines, totalFiles, "max_files_reached"); - yield break; - } - - int diffLineCount = CountDiffLines(diff); - - // Check if this diff would exceed line limit - if (maxLines.HasValue && totalLines + diffLineCount > maxLines.Value) - { - // Calculate how many lines we can still include - int remainingLines = maxLines.Value - totalLines; - - if (remainingLines > 0) - { - // Truncate this diff and yield partial result - var truncatedDiff = TruncateDiff(diff, remainingLines); - yield return DiffStreamResult.CreatePartial(truncatedDiff, totalLines + remainingLines, totalFiles + 1); - } - - yield return DiffStreamResult.CreateTruncated(totalLines + remainingLines, totalFiles + 1, "max_lines_reached"); - yield break; - } - - totalLines += diffLineCount; - totalFiles++; - - yield return DiffStreamResult.Create(diff, totalLines, totalFiles); + yield return DiffStreamResult.CreateTruncated(totalLines, totalFiles, "max_files_reached"); + yield break; } - } - /// - /// Takes diffs up to specified line and file limits, returning pagination metadata. - /// - /// The async enumerable of diffs to process. - /// Maximum total lines. Null for unlimited. - /// Maximum number of files. Null for unlimited. - /// Cancellation token. - /// A result containing the diffs and truncation metadata. - public static async Task TakeDiffsWithLimitsAsync( - this IAsyncEnumerable diffs, - int? maxLines = null, - int? maxFiles = null, - CancellationToken cancellationToken = default) - { - var collectedDiffs = new List(); - int totalLines = 0; - int totalFiles = 0; - bool wasTruncated = false; - string? truncationReason = null; + int diffLineCount = CountDiffLines(diff); - await foreach (var diff in diffs.WithCancellation(cancellationToken).ConfigureAwait(false)) + // Check if this diff would exceed line limit + if (maxLines.HasValue && totalLines + diffLineCount > maxLines.Value) { - // Check file limit - if (maxFiles.HasValue && totalFiles >= maxFiles.Value) - { - wasTruncated = true; - truncationReason = "max_files_reached"; - break; - } + // Calculate how many lines we can still include + int remainingLines = maxLines.Value - totalLines; - int diffLineCount = CountDiffLines(diff); - - // Check if this diff would exceed line limit - if (maxLines.HasValue && totalLines + diffLineCount > maxLines.Value) + if (remainingLines > 0) { - int remainingLines = maxLines.Value - totalLines; - - if (remainingLines > 0) - { - var truncatedDiff = TruncateDiff(diff, remainingLines); - collectedDiffs.Add(truncatedDiff); - totalLines += remainingLines; - totalFiles++; - } - - wasTruncated = true; - truncationReason = "max_lines_reached"; - break; + // Truncate this diff and yield partial result + var truncatedDiff = TruncateDiff(diff, remainingLines); + yield return DiffStreamResult.CreatePartial(truncatedDiff, totalLines + remainingLines, totalFiles + 1); } - collectedDiffs.Add(diff); - totalLines += diffLineCount; - totalFiles++; + yield return DiffStreamResult.CreateTruncated(totalLines + remainingLines, totalFiles + 1, "max_lines_reached"); + yield break; } - return new DiffPaginatedResult( - collectedDiffs, - totalLines, - totalFiles, - wasTruncated, - truncationReason); - } + totalLines += diffLineCount; + totalFiles++; - /// - /// Counts the total number of lines in a diff. - /// - public static int CountDiffLines(Diff diff) - { - if (diff.Hunks == null) - return 0; - - return diff.Hunks.Sum(hunk => - hunk.Segments?.Sum(segment => segment.Lines?.Count ?? 0) ?? 0); + yield return DiffStreamResult.Create(diff, totalLines, totalFiles); } + } - private static Diff TruncateDiff(Diff original, int maxLines) + /// + /// Takes diffs up to specified line and file limits, returning pagination metadata. + /// + /// The async enumerable of diffs to process. + /// Maximum total lines. Null for unlimited. + /// Maximum number of files. Null for unlimited. + /// Cancellation token. + /// A result containing the diffs and truncation metadata. + public static async Task TakeDiffsWithLimitsAsync( + this IAsyncEnumerable diffs, + int? maxLines = null, + int? maxFiles = null, + CancellationToken cancellationToken = default) + { + var collectedDiffs = new List(); + int totalLines = 0; + int totalFiles = 0; + bool wasTruncated = false; + string? truncationReason = null; + + await foreach (var diff in diffs.WithCancellation(cancellationToken).ConfigureAwait(false)) { - if (original.Hunks == null || maxLines <= 0) + // Check file limit + if (maxFiles.HasValue && totalFiles >= maxFiles.Value) { - return new Diff - { - Source = original.Source, - Destination = original.Destination, - Hunks = [] - }; + wasTruncated = true; + truncationReason = "max_files_reached"; + break; } - var truncatedHunks = new List(); - int linesRemaining = maxLines; + int diffLineCount = CountDiffLines(diff); - foreach (var hunk in original.Hunks) + // Check if this diff would exceed line limit + if (maxLines.HasValue && totalLines + diffLineCount > maxLines.Value) { - if (linesRemaining <= 0) - break; + int remainingLines = maxLines.Value - totalLines; - var truncatedHunk = TruncateHunk(hunk, linesRemaining); - truncatedHunks.Add(truncatedHunk); + if (remainingLines > 0) + { + var truncatedDiff = TruncateDiff(diff, remainingLines); + collectedDiffs.Add(truncatedDiff); + totalLines += remainingLines; + totalFiles++; + } - int hunkLines = truncatedHunk.Segments?.Sum(s => s.Lines?.Count ?? 0) ?? 0; - linesRemaining -= hunkLines; + wasTruncated = true; + truncationReason = "max_lines_reached"; + break; } + collectedDiffs.Add(diff); + totalLines += diffLineCount; + totalFiles++; + } + + return new DiffPaginatedResult( + collectedDiffs, + totalLines, + totalFiles, + wasTruncated, + truncationReason); + } + + /// + /// Counts the total number of lines in a diff. + /// + public static int CountDiffLines(Diff diff) + { + if (diff.Hunks == null) + return 0; + + return diff.Hunks.Sum(hunk => + hunk.Segments?.Sum(segment => segment.Lines?.Count ?? 0) ?? 0); + } + + private static Diff TruncateDiff(Diff original, int maxLines) + { + if (original.Hunks == null || maxLines <= 0) + { return new Diff { Source = original.Source, Destination = original.Destination, - Hunks = truncatedHunks + Hunks = [], }; } - private static DiffHunk TruncateHunk(DiffHunk original, int maxLines) - { - if (original.Segments == null || maxLines <= 0) - { - return new DiffHunk - { - SourceLine = original.SourceLine, - SourceSpan = original.SourceSpan, - DestinationLine = original.DestinationLine, - DestinationSpan = original.DestinationSpan, - Segments = [], - Truncated = true - }; - } + var truncatedHunks = new List(); + int linesRemaining = maxLines; - var truncatedSegments = new List(); - int linesRemaining = maxLines; + foreach (var hunk in original.Hunks) + { + if (linesRemaining <= 0) + break; - foreach (var segment in original.Segments) - { - if (linesRemaining <= 0) - break; + var truncatedHunk = TruncateHunk(hunk, linesRemaining); + truncatedHunks.Add(truncatedHunk); - var truncatedSegment = TruncateSegment(segment, linesRemaining); - truncatedSegments.Add(truncatedSegment); + int hunkLines = truncatedHunk.Segments?.Sum(s => s.Lines?.Count ?? 0) ?? 0; + linesRemaining -= hunkLines; + } - int segmentLines = truncatedSegment.Lines?.Count ?? 0; - linesRemaining -= segmentLines; - } + return new Diff + { + Source = original.Source, + Destination = original.Destination, + Hunks = truncatedHunks, + }; + } + private static DiffHunk TruncateHunk(DiffHunk original, int maxLines) + { + if (original.Segments == null || maxLines <= 0) + { return new DiffHunk { SourceLine = original.SourceLine, SourceSpan = original.SourceSpan, DestinationLine = original.DestinationLine, DestinationSpan = original.DestinationSpan, - Segments = truncatedSegments, - Truncated = linesRemaining <= 0 || original.Truncated + Segments = [], + Truncated = true, }; } - private static Segment TruncateSegment(Segment original, int maxLines) + var truncatedSegments = new List(); + int linesRemaining = maxLines; + + foreach (var segment in original.Segments) { - if (original.Lines == null || maxLines <= 0) - { - return new Segment - { - Type = original.Type, - Lines = [], - Truncated = true - }; - } + if (linesRemaining <= 0) + break; + + var truncatedSegment = TruncateSegment(segment, linesRemaining); + truncatedSegments.Add(truncatedSegment); - int linesToTake = Math.Min(original.Lines.Count, maxLines); - bool needsTruncation = linesToTake < original.Lines.Count; + int segmentLines = truncatedSegment.Lines?.Count ?? 0; + linesRemaining -= segmentLines; + } + + return new DiffHunk + { + SourceLine = original.SourceLine, + SourceSpan = original.SourceSpan, + DestinationLine = original.DestinationLine, + DestinationSpan = original.DestinationSpan, + Segments = truncatedSegments, + Truncated = linesRemaining <= 0 || original.Truncated, + }; + } + private static Segment TruncateSegment(Segment original, int maxLines) + { + if (original.Lines == null || maxLines <= 0) + { return new Segment { Type = original.Type, - Lines = original.Lines.Take(linesToTake).ToList(), - Truncated = needsTruncation || original.Truncated + Lines = [], + Truncated = true, }; } + + int linesToTake = Math.Min(original.Lines.Count, maxLines); + bool needsTruncation = linesToTake < original.Lines.Count; + + return new Segment + { + Type = original.Type, + Lines = [.. original.Lines.Take(linesToTake)], + Truncated = needsTruncation || original.Truncated, + }; } +} +/// +/// Result of streaming a single diff with metadata. +/// +public sealed class DiffStreamResult +{ /// - /// Result of streaming a single diff with metadata. + /// The diff content. Null if this is a truncation marker. /// - public sealed class DiffStreamResult - { - /// - /// The diff content. Null if this is a truncation marker. - /// - public Diff? Diff { get; } - - /// - /// Total lines yielded so far (including this diff). - /// - public int TotalLines { get; } - - /// - /// Total files yielded so far (including this diff). - /// - public int TotalFiles { get; } - - /// - /// True if this diff was partially truncated. - /// - public bool IsPartial { get; } - - /// - /// True if streaming was truncated after this result. - /// - public bool IsTruncated { get; } - - /// - /// Reason for truncation, if applicable. - /// - public string? TruncationReason { get; } - - private DiffStreamResult(Diff? diff, int totalLines, int totalFiles, bool isPartial, bool isTruncated, string? truncationReason) - { - Diff = diff; - TotalLines = totalLines; - TotalFiles = totalFiles; - IsPartial = isPartial; - IsTruncated = isTruncated; - TruncationReason = truncationReason; - } + public Diff? Diff { get; } - internal static DiffStreamResult Create(Diff diff, int totalLines, int totalFiles) - => new(diff, totalLines, totalFiles, isPartial: false, isTruncated: false, truncationReason: null); + /// + /// Total lines yielded so far (including this diff). + /// + public int TotalLines { get; } - internal static DiffStreamResult CreatePartial(Diff diff, int totalLines, int totalFiles) - => new(diff, totalLines, totalFiles, isPartial: true, isTruncated: false, truncationReason: null); + /// + /// Total files yielded so far (including this diff). + /// + public int TotalFiles { get; } - internal static DiffStreamResult CreateTruncated(int totalLines, int totalFiles, string reason) - => new(null, totalLines, totalFiles, isPartial: false, isTruncated: true, truncationReason: reason); - } + /// + /// True if this diff was partially truncated. + /// + public bool IsPartial { get; } /// - /// Result of taking diffs with limits, including truncation metadata. - /// This class is designed to be thread-safe for read operations. + /// True if streaming was truncated after this result. /// - public sealed class DiffPaginatedResult - { - private readonly List _diffs; - - /// - /// The collected diffs (may be truncated). Read-only view. - /// - public IReadOnlyList Diffs => _diffs; - - /// - /// Total lines in the result. - /// - public int TotalLines { get; } - - /// - /// Total files in the result. - /// - public int TotalFiles { get; } - - /// - /// True if the result was truncated due to limits. - /// - public bool WasTruncated { get; } - - /// - /// Reason for truncation, if applicable. Values: "max_lines_reached", "max_files_reached". - /// - public string? TruncationReason { get; } - - /// - /// Per MCP best practices, indicates if more results exist. - /// - public bool HasMore => WasTruncated; - - public DiffPaginatedResult(List diffs, int totalLines, int totalFiles, bool wasTruncated, string? truncationReason) - { - _diffs = diffs; - TotalLines = totalLines; - TotalFiles = totalFiles; - WasTruncated = wasTruncated; - TruncationReason = truncationReason; - } + public bool IsTruncated { get; } - /// - /// Deconstructs the result for tuple-style usage. - /// - public void Deconstruct(out IReadOnlyList diffs, out bool hasMore, out int totalLines, out int totalFiles) - { - diffs = Diffs; - hasMore = HasMore; - totalLines = TotalLines; - totalFiles = TotalFiles; - } + /// + /// Reason for truncation, if applicable. + /// + public string? TruncationReason { get; } + + private DiffStreamResult(Diff? diff, int totalLines, int totalFiles, bool isPartial, bool isTruncated, string? truncationReason) + { + Diff = diff; + TotalLines = totalLines; + TotalFiles = totalFiles; + IsPartial = isPartial; + IsTruncated = isTruncated; + TruncationReason = truncationReason; } + + internal static DiffStreamResult Create(Diff diff, int totalLines, int totalFiles) + => new(diff, totalLines, totalFiles, isPartial: false, isTruncated: false, truncationReason: null); + + internal static DiffStreamResult CreatePartial(Diff diff, int totalLines, int totalFiles) + => new(diff, totalLines, totalFiles, isPartial: true, isTruncated: false, truncationReason: null); + + internal static DiffStreamResult CreateTruncated(int totalLines, int totalFiles, string reason) + => new(diff: null, totalLines, totalFiles, isPartial: false, isTruncated: true, truncationReason: reason); } + +/// +/// Result of taking diffs with limits, including truncation metadata. +/// This class is designed to be thread-safe for read operations. +/// +public sealed class DiffPaginatedResult(List diffs, int totalLines, int totalFiles, bool wasTruncated, string? truncationReason) +{ + private readonly List _diffs = diffs; + + /// + /// The collected diffs (may be truncated). Read-only view. + /// + public IReadOnlyList Diffs => _diffs; + + /// + /// Total lines in the result. + /// + public int TotalLines { get; } = totalLines; + + /// + /// Total files in the result. + /// + public int TotalFiles { get; } = totalFiles; + + /// + /// True if the result was truncated due to limits. + /// + public bool WasTruncated { get; } = wasTruncated; + + /// + /// Reason for truncation, if applicable. Values: "max_lines_reached", "max_files_reached". + /// + public string? TruncationReason { get; } = truncationReason; + + /// + /// Per MCP best practices, indicates if more results exist. + /// + public bool HasMore => WasTruncated; + + /// + /// Deconstructs the result for tuple-style usage. + /// + public void Deconstruct(out IReadOnlyList diffs, out bool hasMore, out int totalLines, out int totalFiles) + { + diffs = Diffs; + hasMore = HasMore; + totalLines = TotalLines; + totalFiles = TotalFiles; + } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Mcp/McpExtensions.cs b/src/Bitbucket.Net/Common/Mcp/McpExtensions.cs index beec206..26f3dcc 100644 --- a/src/Bitbucket.Net/Common/Mcp/McpExtensions.cs +++ b/src/Bitbucket.Net/Common/Mcp/McpExtensions.cs @@ -3,227 +3,219 @@ using System.Threading; using System.Threading.Tasks; -namespace Bitbucket.Net.Common.Mcp +namespace Bitbucket.Net.Common.Mcp; + +/// +/// MCP-optimized extension methods for common truncation and pagination patterns. +/// Designed for Model Context Protocol (MCP) server integration where context window +/// limits require intelligent truncation of large result sets. +/// +public static class McpExtensions { /// - /// MCP-optimized extension methods for common truncation and pagination patterns. - /// Designed for Model Context Protocol (MCP) server integration where context window - /// limits require intelligent truncation of large result sets. + /// Takes the first N items from an async enumerable with pagination metadata preserved. + /// This method is optimized for MCP servers that need to truncate large result sets + /// while maintaining pagination information for follow-up requests. /// - public static class McpExtensions + /// The type of items in the sequence. + /// The async enumerable source. + /// Maximum number of items to return. + /// Cancellation token. + /// + /// A PaginatedResult containing: + /// - Items: The first N items from the source + /// - HasMore: True if there are more items beyond the limit + /// - NextOffset: The offset for the next page (equal to limit if HasMore is true) + /// + /// + /// Per MCP best practices, pagination responses should include has_more, next_offset, and total_count. + /// This method fetches limit+1 items to determine if more exist without fetching the entire collection. + /// + /// Usage with streaming APIs: + /// + /// var result = await client.GetPullRequestsStreamAsync(projectKey, repoSlug) + /// .TakeWithPaginationAsync(limit: 25); + /// + /// // Return to MCP client + /// return new { + /// items = result.Items, + /// has_more = result.HasMore, + /// next_offset = result.NextOffset + /// }; + /// + /// + public static async Task> TakeWithPaginationAsync( + this IAsyncEnumerable source, + int limit, + CancellationToken cancellationToken = default) { - /// - /// Takes the first N items from an async enumerable with pagination metadata preserved. - /// This method is optimized for MCP servers that need to truncate large result sets - /// while maintaining pagination information for follow-up requests. - /// - /// The type of items in the sequence. - /// The async enumerable source. - /// Maximum number of items to return. - /// Cancellation token. - /// - /// A PaginatedResult containing: - /// - Items: The first N items from the source - /// - HasMore: True if there are more items beyond the limit - /// - NextOffset: The offset for the next page (equal to limit if HasMore is true) - /// - /// - /// Per MCP best practices, pagination responses should include has_more, next_offset, and total_count. - /// This method fetches limit+1 items to determine if more exist without fetching the entire collection. - /// - /// Usage with streaming APIs: - /// - /// var result = await client.GetPullRequestsStreamAsync(projectKey, repoSlug) - /// .TakeWithPaginationAsync(limit: 25); - /// - /// // Return to MCP client - /// return new { - /// items = result.Items, - /// has_more = result.HasMore, - /// next_offset = result.NextOffset - /// }; - /// - /// - public static async Task> TakeWithPaginationAsync( - this IAsyncEnumerable source, - int limit, - CancellationToken cancellationToken = default) - { - var items = new List(limit); - int count = 0; + var items = new List(limit); + int count = 0; - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + { + if (count < limit) { - if (count < limit) - { - items.Add(item); - } - - count++; - - // Found one more than requested - we know there are more items - if (count > limit) - { - return new PaginatedResult(items, hasMore: true, nextOffset: limit); - } + items.Add(item); } - return new PaginatedResult(items, hasMore: false, nextOffset: null); - } + count++; - /// - /// Streams items with a hard limit, stopping enumeration after the limit is reached. - /// More memory-efficient than TakeWithPaginationAsync when you don't need HasMore metadata. - /// - /// The type of items in the sequence. - /// The async enumerable source. - /// Maximum number of items to yield. - /// Cancellation token. - /// An async enumerable that yields at most limit items. - /// - /// This is the most efficient option when you only need to limit results without - /// knowing if more exist. The enumeration stops immediately after yielding - /// the limit-th item. - /// - /// Usage: - /// - /// await foreach (var pr in client.GetPullRequestsStreamAsync(projectKey, repoSlug) - /// .TakeAsync(10)) - /// { - /// // Process at most 10 PRs - /// } - /// - /// - public static async IAsyncEnumerable TakeAsync( - this IAsyncEnumerable source, - int limit, - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - int count = 0; - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + // Found one more than requested - we know there are more items + if (count > limit) { - if (count >= limit) - { - yield break; - } - - yield return item; - count++; + return new PaginatedResult(items, hasMore: true, nextOffset: limit); } } - /// - /// Skips the first N items and then yields the remaining items. - /// Useful for implementing offset-based pagination on top of streaming APIs. - /// - /// The type of items in the sequence. - /// The async enumerable source. - /// Number of items to skip. - /// Cancellation token. - /// An async enumerable that skips the first count items. - public static async IAsyncEnumerable SkipAsync( - this IAsyncEnumerable source, - int count, - [EnumeratorCancellation] CancellationToken cancellationToken = default) + return new PaginatedResult(items, hasMore: false, nextOffset: null); + } + + /// + /// Streams items with a hard limit, stopping enumeration after the limit is reached. + /// More memory-efficient than TakeWithPaginationAsync when you don't need HasMore metadata. + /// + /// The type of items in the sequence. + /// The async enumerable source. + /// Maximum number of items to yield. + /// Cancellation token. + /// An async enumerable that yields at most limit items. + /// + /// This is the most efficient option when you only need to limit results without + /// knowing if more exist. The enumeration stops immediately after yielding + /// the limit-th item. + /// + /// Usage: + /// + /// await foreach (var pr in client.GetPullRequestsStreamAsync(projectKey, repoSlug) + /// .TakeAsync(10)) + /// { + /// // Process at most 10 PRs + /// } + /// + /// + public static async IAsyncEnumerable TakeAsync( + this IAsyncEnumerable source, + int limit, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + int count = 0; + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { - int skipped = 0; - await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) + if (count >= limit) { - if (skipped < count) - { - skipped++; - continue; - } - - yield return item; + yield break; } - } - /// - /// Implements offset/limit pagination on top of a streaming source. - /// Combines Skip and Take for traditional pagination patterns. - /// - /// The type of items in the sequence. - /// The async enumerable source. - /// Number of items to skip (0-based offset). - /// Maximum number of items to return. - /// Cancellation token. - /// - /// A PaginatedResult with items from offset to offset+limit-1. - /// Note: NextOffset in the result is relative to the current window (equals limit when HasMore is true). - /// To calculate the absolute offset for the next page, use: offset + result.NextOffset. - /// - /// - /// This is useful when an MCP client requests a specific page: - /// - /// // Client requests page 3 with 25 items per page - /// var result = await client.GetCommitsStreamAsync(projectKey, repoSlug) - /// .PageAsync(offset: 50, limit: 25); - /// - /// // To get next page offset: - /// int nextOffset = result.HasMore ? 50 + result.NextOffset.Value : -1; - /// - /// - public static async Task> PageAsync( - this IAsyncEnumerable source, - int offset, - int limit, - CancellationToken cancellationToken = default) - { - return await source - .SkipAsync(offset, cancellationToken) - .TakeWithPaginationAsync(limit, cancellationToken) - .ConfigureAwait(false); + yield return item; + count++; } } /// - /// Result of a paginated query with MCP-friendly metadata. - /// This class is designed to be thread-safe for read operations. + /// Skips the first N items and then yields the remaining items. + /// Useful for implementing offset-based pagination on top of streaming APIs. /// - /// The type of items in the result. - public sealed class PaginatedResult + /// The type of items in the sequence. + /// The async enumerable source. + /// Number of items to skip. + /// Cancellation token. + /// An async enumerable that skips the first count items. + public static async IAsyncEnumerable SkipAsync( + this IAsyncEnumerable source, + int count, + [EnumeratorCancellation] CancellationToken cancellationToken = default) { - private readonly List _items; - - /// - /// The items in the current page (read-only view). - /// - public IReadOnlyList Items => _items; - - /// - /// Indicates if more results are available beyond this page. - /// Per MCP best practices: pagination responses should include has_more. - /// - public bool HasMore { get; } - - /// - /// The offset for retrieving the next page of results. - /// Null if there are no more results. - /// Per MCP best practices: pagination responses should include next_offset. - /// - public int? NextOffset { get; } - - /// - /// The number of items in the current result set. - /// - public int Count => _items.Count; - - public PaginatedResult(List items, bool hasMore, int? nextOffset) + int skipped = 0; + await foreach (var item in source.WithCancellation(cancellationToken).ConfigureAwait(false)) { - _items = items; - HasMore = hasMore; - NextOffset = nextOffset; - } + if (skipped < count) + { + skipped++; + continue; + } - /// - /// Deconstructs the result for tuple-style usage. - /// - public void Deconstruct(out IReadOnlyList items, out bool hasMore, out int? nextOffset) - { - items = Items; - hasMore = HasMore; - nextOffset = NextOffset; + yield return item; } } + + /// + /// Implements offset/limit pagination on top of a streaming source. + /// Combines Skip and Take for traditional pagination patterns. + /// + /// The type of items in the sequence. + /// The async enumerable source. + /// Number of items to skip (0-based offset). + /// Maximum number of items to return. + /// Cancellation token. + /// + /// A PaginatedResult with items from offset to offset+limit-1. + /// Note: NextOffset in the result is relative to the current window (equals limit when HasMore is true). + /// To calculate the absolute offset for the next page, use: offset + result.NextOffset. + /// + /// + /// This is useful when an MCP client requests a specific page: + /// + /// // Client requests page 3 with 25 items per page + /// var result = await client.GetCommitsStreamAsync(projectKey, repoSlug) + /// .PageAsync(offset: 50, limit: 25); + /// + /// // To get next page offset: + /// int nextOffset = result.HasMore ? 50 + result.NextOffset.Value : -1; + /// + /// + public static async Task> PageAsync( + this IAsyncEnumerable source, + int offset, + int limit, + CancellationToken cancellationToken = default) + { + return await source + .SkipAsync(offset, cancellationToken) + .TakeWithPaginationAsync(limit, cancellationToken) + .ConfigureAwait(false); + } } + +/// +/// Result of a paginated query with MCP-friendly metadata. +/// This class is designed to be thread-safe for read operations. +/// +/// The type of items in the result. +public sealed class PaginatedResult(List items, bool hasMore, int? nextOffset) +{ + private readonly List _items = items; + + /// + /// The items in the current page (read-only view). + /// + public IReadOnlyList Items => _items; + + /// + /// Indicates if more results are available beyond this page. + /// Per MCP best practices: pagination responses should include has_more. + /// + public bool HasMore { get; } = hasMore; + + /// + /// The offset for retrieving the next page of results. + /// Null if there are no more results. + /// Per MCP best practices: pagination responses should include next_offset. + /// + public int? NextOffset { get; } = nextOffset; + + /// + /// The number of items in the current result set. + /// + public int Count => _items.Count; + + /// + /// Deconstructs the result for tuple-style usage. + /// + public void Deconstruct(out IReadOnlyList items, out bool hasMore, out int? nextOffset) + { + items = Items; + hasMore = HasMore; + nextOffset = NextOffset; + } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Models/Error.cs b/src/Bitbucket.Net/Common/Models/Error.cs index b620ca7..f0c4621 100644 --- a/src/Bitbucket.Net/Common/Models/Error.cs +++ b/src/Bitbucket.Net/Common/Models/Error.cs @@ -1,23 +1,22 @@ -namespace Bitbucket.Net.Common.Models +namespace Bitbucket.Net.Common.Models; + +/// +/// Represents an error returned by the Bitbucket Server API. +/// +public class Error { /// - /// Represents an error returned by the Bitbucket Server API. + /// Gets or sets the context of the error (e.g., the field or resource that caused the error). /// - public class Error - { - /// - /// Gets or sets the context of the error (e.g., the field or resource that caused the error). - /// - public string? Context { get; set; } + public string? Context { get; set; } - /// - /// Gets or sets the error message. - /// - public string Message { get; set; } = string.Empty; + /// + /// Gets or sets the error message. + /// + public string Message { get; set; } = string.Empty; - /// - /// Gets or sets the name of the exception that occurred on the server, if available. - /// - public string? ExceptionName { get; set; } - } -} + /// + /// Gets or sets the name of the exception that occurred on the server, if available. + /// + public string? ExceptionName { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Models/ErrorResponse.cs b/src/Bitbucket.Net/Common/Models/ErrorResponse.cs index 059a706..ac06484 100644 --- a/src/Bitbucket.Net/Common/Models/ErrorResponse.cs +++ b/src/Bitbucket.Net/Common/Models/ErrorResponse.cs @@ -1,15 +1,14 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Common.Models +namespace Bitbucket.Net.Common.Models; + +/// +/// Represents the error response returned by the Bitbucket Server API. +/// +public class ErrorResponse { /// - /// Represents the error response returned by the Bitbucket Server API. + /// Gets or sets the collection of errors returned by the server. /// - public class ErrorResponse - { - /// - /// Gets or sets the collection of errors returned by the server. - /// - public IEnumerable? Errors { get; set; } - } -} + public IEnumerable? Errors { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Models/PagedResults.cs b/src/Bitbucket.Net/Common/Models/PagedResults.cs index 0cc7955..413a3a4 100644 --- a/src/Bitbucket.Net/Common/Models/PagedResults.cs +++ b/src/Bitbucket.Net/Common/Models/PagedResults.cs @@ -1,23 +1,22 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Common.Models +namespace Bitbucket.Net.Common.Models; + +public class PagedResults : PagedResultsBase { - public class PagedResults : PagedResultsBase - { - public int Limit { get; set; } - public List Values { get; set; } = []; - public int? NextPageStart { get; set; } + public int Limit { get; set; } + public List Values { get; set; } = []; + public int? NextPageStart { get; set; } - /// - /// MCP-friendly property indicating if more results are available. - /// Per MCP best practices, pagination responses should include has_more. - /// - public bool HasMore => !IsLastPage; + /// + /// MCP-friendly property indicating if more results are available. + /// Per MCP best practices, pagination responses should include has_more. + /// + public bool HasMore => !IsLastPage; - /// - /// MCP-friendly property for the current offset position. - /// Per MCP best practices, pagination responses should include current offset. - /// - public int CurrentOffset => Start; - } -} + /// + /// MCP-friendly property for the current offset position. + /// Per MCP best practices, pagination responses should include current offset. + /// + public int CurrentOffset => Start; +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/Models/PagedResultsBase.cs b/src/Bitbucket.Net/Common/Models/PagedResultsBase.cs index a59118a..e66322a 100644 --- a/src/Bitbucket.Net/Common/Models/PagedResultsBase.cs +++ b/src/Bitbucket.Net/Common/Models/PagedResultsBase.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Common.Models +namespace Bitbucket.Net.Common.Models; + +public abstract class PagedResultsBase { - public abstract class PagedResultsBase - { - public int Size { get; set; } - public bool IsLastPage { get; set; } - public int Start { get; set; } - } + public int Size { get; set; } + public bool IsLastPage { get; set; } + public int Start { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/TypeExtensions.cs b/src/Bitbucket.Net/Common/TypeExtensions.cs index 030b0dd..582e871 100644 --- a/src/Bitbucket.Net/Common/TypeExtensions.cs +++ b/src/Bitbucket.Net/Common/TypeExtensions.cs @@ -1,10 +1,9 @@ using System; using System.Reflection; -namespace Bitbucket.Net.Common +namespace Bitbucket.Net.Common; + +public static class TypeExtensions { - public static class TypeExtensions - { - public static bool IsNullableType(Type type) => type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); - } -} + public static bool IsNullableType(Type type) => type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs b/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs index 392927d..b9ca21d 100644 --- a/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs +++ b/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs @@ -1,19 +1,17 @@ using System; -namespace Bitbucket.Net.Common +namespace Bitbucket.Net.Common; + +public static class UnixDateTimeExtensions { - public static class UnixDateTimeExtensions + public static DateTimeOffset FromUnixTimeSeconds(this long value) { - public static DateTimeOffset FromUnixTimeSeconds(this long value) - { - return new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero) - .AddMilliseconds(value) - .ToLocalTime(); - } + return DateTimeOffset.UnixEpoch.AddMilliseconds(value) + .ToLocalTime(); + } - public static long ToUnixTimeSeconds(this DateTimeOffset dateTimeOffset) - { - return dateTimeOffset.Subtract(new DateTimeOffset(1970, 1, 1, 0, 0, 0, 0, TimeSpan.Zero)).Ticks; - } + public static long ToUnixTimeSeconds(this DateTimeOffset dateTimeOffset) + { + return dateTimeOffset.Subtract(DateTimeOffset.UnixEpoch).Ticks; } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs index 013d054..8b97b4e 100644 --- a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs @@ -1,518 +1,530 @@ -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Users; using Flurl.Http; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; using PasswordChange = Bitbucket.Net.Models.Core.Admin.PasswordChange; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient + private IFlurlRequest GetAdminUrl() => GetBaseUrl() + .AppendPathSegment("/admin"); + + private IFlurlRequest GetAdminUrl(string path) => GetAdminUrl() + .AppendPathSegment(path); + + public async Task> GetAdminGroupsAsync(string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) { - private IFlurlRequest GetAdminUrl() => GetBaseUrl() - .AppendPathSegment("/admin"); + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/groups") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - private IFlurlRequest GetAdminUrl(string path) => GetAdminUrl() - .AppendPathSegment(path); + public async Task CreateAdminGroupAsync(string name, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/groups") + .SetQueryParam("name", name) + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task> GetAdminGroupsAsync(string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/groups") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task CreateAdminGroupAsync(string name, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/groups") - .SetQueryParam("name", name) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task DeleteAdminGroupAsync(string name, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/groups") + .SetQueryParam("name", name) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task DeleteAdminGroupAsync(string name, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/groups") - .SetQueryParam("name", name) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task AddAdminGroupUsersAsync(GroupUsers groupUsers, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/groups/add-users") + .PostJsonAsync(groupUsers, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task AddAdminGroupUsersAsync(GroupUsers groupUsers, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/groups/add-users") - .PostJsonAsync(groupUsers, cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - public async Task> GetAdminGroupMoreMembersAsync(string context, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["context"] = context, - ["filter"] = filter, - ["avatarSize"] = avatarSize - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/groups/more-members") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task> GetAdminGroupMoreNonMembersAsync(string context, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["context"] = context, - ["filter"] = filter, - ["avatarSize"] = avatarSize - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/groups/more-non-members") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task> GetAdminUsersAsync(string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - ["avatarSize"] = avatarSize - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/users") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task CreateAdminUserAsync(string name, string password, string displayName, string emailAddress, - bool addToDefaultGroup = true, string notify = "false", CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["name"] = name, - ["password"] = password, - ["displayName"] = displayName, - ["emailAddress"] = emailAddress, - ["addToDefaultGroup"] = BitbucketHelpers.BoolToString(addToDefaultGroup), - ["notify"] = notify - }; - - var response = await GetAdminUrl("/users") - .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - public async Task UpdateAdminUserAsync(string? name = null, string? displayName = null, string? emailAddress = null, CancellationToken cancellationToken = default) - { - var data = new - { - name, - displayName, - email = emailAddress - }; + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - var response = await GetAdminUrl("/users") - .PutJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetAdminGroupMoreMembersAsync(string context, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["context"] = context, + ["filter"] = filter, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/groups/more-members") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task> GetAdminGroupMoreNonMembersAsync(string context, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["context"] = context, + ["filter"] = filter, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/groups/more-non-members") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task DeleteAdminUserAsync(string name, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/users") - .SetQueryParam("name", name) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetAdminUsersAsync(string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/users") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task CreateAdminUserAsync(string name, string password, string displayName, string emailAddress, + bool addToDefaultGroup = true, string notify = "false", CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["name"] = name, + ["password"] = password, + ["displayName"] = displayName, + ["emailAddress"] = emailAddress, + ["addToDefaultGroup"] = BitbucketHelpers.BoolToString(addToDefaultGroup), + ["notify"] = notify, + }; + + var response = await GetAdminUrl("/users") + .SetQueryParams(queryParamValues) + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task AddAdminUserGroupsAsync(UserGroups userGroups, CancellationToken cancellationToken = default) + public async Task UpdateAdminUserAsync(string? name = null, string? displayName = null, string? emailAddress = null, CancellationToken cancellationToken = default) + { + var data = new { - var response = await GetAdminUrl("/users/add-groups") - .PostJsonAsync(userGroups, cancellationToken: cancellationToken) - .ConfigureAwait(false); + name, + displayName, + email = emailAddress, + }; - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + var response = await GetAdminUrl("/users") + .PutJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task DeleteAdminUserCaptcha(string name, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/users/captcha") - .SetQueryParam("name", name) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task DeleteAdminUserAsync(string name, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/users") + .SetQueryParam("name", name) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task UpdateAdminUserCredentialsAsync(PasswordChange passwordChange, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/users/credentials") - .PutJsonAsync(passwordChange, cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - public async Task> GetAdminUserMoreMembersAsync(string context, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["context"] = context, - ["filter"] = filter - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/users/more-members") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task> GetAdminUserMoreNonMembersAsync(string context, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["context"] = context, - ["filter"] = filter - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/users/more-non-members") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task RemoveAdminUserFromGroupAsync(string userName, string groupName, CancellationToken cancellationToken = default) - { - var data = new - { - context = userName, - itemName = groupName - }; + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - var response = await GetAdminUrl("/users/remove-group") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task AddAdminUserGroupsAsync(UserGroups userGroups, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/users/add-groups") + .PostJsonAsync(userGroups, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task RenameAdminUserAsync(UserRename userRename, int? avatarSize = null, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("users/rename") - .SetQueryParam("avatarSize", avatarSize) - .PostJsonAsync(userRename, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteAdminUserCaptcha(string name, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/users/captcha") + .SetQueryParam("name", name) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task GetAdminClusterAsync(CancellationToken cancellationToken = default) - { - return await GetAdminUrl("/cluster") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task UpdateAdminUserCredentialsAsync(PasswordChange passwordChange, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/users/credentials") + .PutJsonAsync(passwordChange, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task GetAdminLicenseAsync(CancellationToken cancellationToken = default) - { - return await GetAdminUrl("/license") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task UpdateAdminLicenseAsync(LicenseInfo licenseInfo, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/license") - .PostJsonAsync(licenseInfo, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetAdminUserMoreMembersAsync(string context, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["context"] = context, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/users/more-members") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task> GetAdminUserMoreNonMembersAsync(string context, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["context"] = context, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/users/more-non-members") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task GetAdminMailServerAsync(CancellationToken cancellationToken = default) + public async Task RemoveAdminUserFromGroupAsync(string userName, string groupName, CancellationToken cancellationToken = default) + { + var data = new { - return await GetAdminUrl("/mail-server") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + context = userName, + itemName = groupName, + }; - public async Task UpdateAdminMailServerAsync(MailServerConfiguration mailServerConfiguration, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/mail-server") - .PutJsonAsync(mailServerConfiguration, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetAdminUrl("/users/remove-group") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task DeleteAdminMailServerAsync(CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/mail-server") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task RenameAdminUserAsync(UserRename userRename, int? avatarSize = null, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("users/rename") + .SetQueryParam("avatarSize", avatarSize) + .PostJsonAsync(userRename, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetAdminMailServerSenderAddressAsync(CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/mail-server/sender-address") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetAdminClusterAsync(CancellationToken cancellationToken = default) + { + return await GetAdminUrl("/cluster") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); - } + public async Task GetAdminLicenseAsync(CancellationToken cancellationToken = default) + { + return await GetAdminUrl("/license") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdateAdminMailServerSenderAddressAsync(string senderAddress, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/mail-server/sender-address") - .PutJsonAsync(senderAddress, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateAdminLicenseAsync(LicenseInfo licenseInfo, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/license") + .PostJsonAsync(licenseInfo, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteAdminMailServerSenderAddressAsync(CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/mail-server/sender-address") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - public async Task> GetAdminGroupPermissionsAsync(string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/permissions/groups") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task UpdateAdminGroupPermissionsAsync(Permissions permission, string name, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["permission"] = permission, - ["name"] = name - }; + public async Task GetAdminMailServerAsync(CancellationToken cancellationToken = default) + { + return await GetAdminUrl("/mail-server") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - var response = await GetAdminUrl("/permissions/groups") - .SetQueryParams(queryParamValues) - .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateAdminMailServerAsync(MailServerConfiguration mailServerConfiguration, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/mail-server") + .PutJsonAsync(mailServerConfiguration, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteAdminGroupPermissionsAsync(string name, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/permissions/groups") - .SetQueryParam("name", name) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - public async Task> GetAdminGroupPermissionsNoneAsync(string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/permissions/groups/none") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task> GetAdminUserPermissionsAsync(string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - ["avatarSize"] = avatarSize - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/permissions/users") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task UpdateAdminUserPermissionsAsync(Permissions permission, string name, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["permission"] = permission, - ["name"] = name - }; + public async Task DeleteAdminMailServerAsync(CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/mail-server") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - var response = await GetAdminUrl("/permissions/users") - .SetQueryParams(queryParamValues) - .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetAdminMailServerSenderAddressAsync(CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/mail-server/sender-address") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); + } - public async Task DeleteAdminUserPermissionsAsync(string name, CancellationToken cancellationToken = default) - { - var response = await GetAdminUrl("/permissions/users") - .SetQueryParam("name", name) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - public async Task> GetAdminUserPermissionsNoneAsync(string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - ["avatarSize"] = avatarSize - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/permissions/users/none") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task GetAdminPullRequestsMergeStrategiesAsync(string scmId, CancellationToken cancellationToken = default) + public async Task UpdateAdminMailServerSenderAddressAsync(string senderAddress, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/mail-server/sender-address") + .PutJsonAsync(senderAddress, cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); + } + + public async Task DeleteAdminMailServerSenderAddressAsync(CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/mail-server/sender-address") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + public async Task> GetAdminGroupPermissionsAsync(string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/permissions/groups") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task UpdateAdminGroupPermissionsAsync(Permissions permission, string name, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - return await GetAdminUrl($"/pull-requests/{scmId}") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + ["permission"] = permission, + ["name"] = name, + }; + + var response = await GetAdminUrl("/permissions/groups") + .SetQueryParams(queryParamValues) + .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + public async Task DeleteAdminGroupPermissionsAsync(string name, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/permissions/groups") + .SetQueryParam("name", name) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + public async Task> GetAdminGroupPermissionsNoneAsync(string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/permissions/groups/none") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task> GetAdminUserPermissionsAsync(string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/permissions/users") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdateAdminPullRequestsMergeStrategiesAsync(string scmId, MergeStrategies mergeStrategies, CancellationToken cancellationToken = default) + public async Task UpdateAdminUserPermissionsAsync(Permissions permission, string name, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var response = await GetAdminUrl($"/pull-requests/{scmId}") - .PostJsonAsync(mergeStrategies, cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["permission"] = permission, + ["name"] = name, + }; + + var response = await GetAdminUrl("/permissions/users") + .SetQueryParams(queryParamValues) + .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + public async Task DeleteAdminUserPermissionsAsync(string name, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl("/permissions/users") + .SetQueryParam("name", name) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + public async Task> GetAdminUserPermissionsNoneAsync(string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetAdminUrl("/permissions/users/none") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task GetAdminPullRequestsMergeStrategiesAsync(string scmId, CancellationToken cancellationToken = default) + { + return await GetAdminUrl($"/pull-requests/{scmId}") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } + + public async Task UpdateAdminPullRequestsMergeStrategiesAsync(string scmId, MergeStrategies mergeStrategies, CancellationToken cancellationToken = default) + { + var response = await GetAdminUrl($"/pull-requests/{scmId}") + .PostJsonAsync(mergeStrategies, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs b/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs index b2642a0..56b396d 100644 --- a/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs @@ -1,23 +1,22 @@ +using Flurl.Http; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Flurl.Http; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetApplicationPropertiesUrl() => GetBaseUrl() - .AppendPathSegment("/application-properties"); + private IFlurlRequest GetApplicationPropertiesUrl() => GetBaseUrl() + .AppendPathSegment("/application-properties"); - public async Task> GetApplicationPropertiesAsync(CancellationToken cancellationToken = default) - { - var response = await GetApplicationPropertiesUrl() - .GetJsonAsync>(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetApplicationPropertiesAsync(CancellationToken cancellationToken = default) + { + var response = await GetApplicationPropertiesUrl() + .GetJsonAsync>(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return response; - } + return response; } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs index 9686adc..7f4a426 100644 --- a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs @@ -1,70 +1,71 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Projects; using Flurl.Http; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Bitbucket.Net; -namespace Bitbucket.Net +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetDashboardUrl() => GetBaseUrl() - .AppendPathSegment("/dashboard"); + private IFlurlRequest GetDashboardUrl() => GetBaseUrl() + .AppendPathSegment("/dashboard"); - private IFlurlRequest GetDashboardUrl(string path) => GetDashboardUrl() - .AppendPathSegment(path); + private IFlurlRequest GetDashboardUrl(string path) => GetDashboardUrl() + .AppendPathSegment(path); - public async Task> GetDashboardPullRequestsAsync(PullRequestStates? state = null, - Roles? role = null, - List? status = null, - PullRequestOrders? order = PullRequestOrders.Newest, - int? closedSinceSeconds = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetDashboardPullRequestsAsync(PullRequestStates? state = null, + Roles? role = null, + List? status = null, + PullRequestOrders? order = PullRequestOrders.Newest, + int? closedSinceSeconds = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["state"] = BitbucketHelpers.PullRequestStateToString(state), - ["role"] = BitbucketHelpers.RoleToString(role), - ["status"] = status != null ? string.Join(",", status.Select(BitbucketHelpers.ParticipantStatusToString)) : null, - ["order"] = BitbucketHelpers.PullRequestOrderToString(order), - ["closedSince"] = closedSinceSeconds, - ["limit"] = limit, - ["start"] = start - }; + ["state"] = BitbucketHelpers.PullRequestStateToString(state), + ["role"] = BitbucketHelpers.RoleToString(role), + ["status"] = status != null ? string.Join(',', status.Select(BitbucketHelpers.ParticipantStatusToString)) : null, + ["order"] = BitbucketHelpers.PullRequestOrderToString(order), + ["closedSince"] = closedSinceSeconds, + ["limit"] = limit, + ["start"] = start, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetDashboardUrl("/pull-requests") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetDashboardUrl("/pull-requests") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetDashboardPullRequestSuggestionsAsync(int changesSinceSeconds = 172800, - int? maxPages = null, - int? limit = 3, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetDashboardPullRequestSuggestionsAsync(int changesSinceSeconds = 172800, + int? maxPages = null, + int? limit = 3, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["changesSince"] = changesSinceSeconds, - ["limit"] = limit, - ["start"] = start - }; + ["changesSince"] = changesSinceSeconds, + ["limit"] = limit, + ["start"] = start, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetDashboardUrl("/pull-request-suggestions") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetDashboardUrl("/pull-request-suggestions") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs index d9508f3..7042081 100644 --- a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs @@ -1,35 +1,35 @@ +using Bitbucket.Net.Common.Models; +using Flurl.Http; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common.Models; -using Flurl.Http; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetGroupsUrl() => GetBaseUrl() - .AppendPathSegment("/groups"); + private IFlurlRequest GetGroupsUrl() => GetBaseUrl() + .AppendPathSegment("/groups"); - public async Task> GetGroupNamesAsync(string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetGroupNamesAsync(string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["filter"] = filter, - ["limit"] = limit, - ["start"] = start - }; + ["filter"] = filter, + ["limit"] = limit, + ["start"] = start, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetGroupsUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetGroupsUrl() + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs b/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs index df77155..1e01ad1 100644 --- a/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs @@ -1,21 +1,20 @@ +using Flurl.Http; using System.Threading; using System.Threading.Tasks; -using Flurl.Http; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetHooksUrl() => GetBaseUrl() - .AppendPathSegment("/hooks"); + private IFlurlRequest GetHooksUrl() => GetBaseUrl() + .AppendPathSegment("/hooks"); - public async Task GetProjectHooksAvatarAsync(string hookKey, string? version = null, CancellationToken cancellationToken = default) - { - return await GetHooksUrl() - .AppendPathSegment($"/{hookKey}/avatar") - .SetQueryParam("version", version) - .GetBytesAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetProjectHooksAvatarAsync(string hookKey, string? version = null, CancellationToken cancellationToken = default) + { + return await GetHooksUrl() + .AppendPathSegment($"/{hookKey}/avatar") + .SetQueryParam("version", version) + .GetBytesAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs index 3d68017..c8abf84 100644 --- a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs @@ -1,56 +1,56 @@ -using System.Collections.Generic; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Projects; using Flurl.Http; +using System.Collections.Generic; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetInboxUrl() => GetBaseUrl() - .AppendPathSegment("/inbox"); + private IFlurlRequest GetInboxUrl() => GetBaseUrl() + .AppendPathSegment("/inbox"); - private IFlurlRequest GetInboxUrl(string path) => GetInboxUrl() - .AppendPathSegment(path); + private IFlurlRequest GetInboxUrl(string path) => GetInboxUrl() + .AppendPathSegment(path); - public async Task> GetInboxPullRequestsAsync( - int? maxPages = null, - int? limit = 25, - int? start = 0, - Roles role = Roles.Reviewer, - CancellationToken cancellationToken = default) + public async Task> GetInboxPullRequestsAsync( + int? maxPages = null, + int? limit = 25, + int? start = 0, + Roles role = Roles.Reviewer, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["role"] = BitbucketHelpers.RoleToString(role) - }; + ["limit"] = limit, + ["start"] = start, + ["role"] = BitbucketHelpers.RoleToString(role), + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetInboxUrl("/pull-requests") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetInboxUrl("/pull-requests") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task GetInboxPullRequestsCountAsync(CancellationToken cancellationToken = default) - { - var response = await GetInboxUrl("/pull-requests/count") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetInboxPullRequestsCountAsync(CancellationToken cancellationToken = default) + { + var response = await GetInboxUrl("/pull-requests/count") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, s => - { - using var doc = JsonDocument.Parse(s); - return doc.RootElement.GetProperty("count").GetInt32(); - }, cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync(response, s => + { + using var doc = JsonDocument.Parse(s); + return doc.RootElement.GetProperty("count").GetInt32(); + }, cancellationToken) + .ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs b/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs index ea033d2..c9bbec7 100644 --- a/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs @@ -1,65 +1,64 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Models.Core.Logs; +using Flurl.Http; using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common; -using Bitbucket.Net.Models.Core.Logs; -using Flurl.Http; -namespace Bitbucket.Net -{ - public partial class BitbucketClient - { - private IFlurlRequest GetLogsUrl() => GetBaseUrl() - .AppendPathSegment("/logs"); +namespace Bitbucket.Net; - private IFlurlRequest GetLogsUrl(string path) => GetLogsUrl() - .AppendPathSegment(path); +public partial class BitbucketClient +{ + private IFlurlRequest GetLogsUrl() => GetBaseUrl() + .AppendPathSegment("/logs"); - public async Task GetLogLevelAsync(string loggerName, CancellationToken cancellationToken = default) - { - var response = await GetLogsUrl($"/logger/{loggerName}") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + private IFlurlRequest GetLogsUrl(string path) => GetLogsUrl() + .AppendPathSegment(path); - return await HandleResponseAsync(response, s => - { - using var doc = JsonDocument.Parse(s); - return BitbucketHelpers.StringToLogLevel(doc.RootElement.GetProperty("logLevel").GetString()!); - }, cancellationToken) - .ConfigureAwait(false); - } + public async Task GetLogLevelAsync(string loggerName, CancellationToken cancellationToken = default) + { + var response = await GetLogsUrl($"/logger/{loggerName}") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task SetLogLevelAsync(string loggerName, LogLevels logLevel, CancellationToken cancellationToken = default) + return await HandleResponseAsync(response, s => { - var response = await GetLogsUrl($"/logger/{loggerName}/{BitbucketHelpers.LogLevelToString(logLevel)}") - .PutAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + using var doc = JsonDocument.Parse(s); + return BitbucketHelpers.StringToLogLevel(doc.RootElement.GetProperty("logLevel").GetString()!); + }, cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task SetLogLevelAsync(string loggerName, LogLevels logLevel, CancellationToken cancellationToken = default) + { + var response = await GetLogsUrl($"/logger/{loggerName}/{BitbucketHelpers.LogLevelToString(logLevel)}") + .PutAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task GetRootLogLevelAsync(CancellationToken cancellationToken = default) - { - var response = await GetLogsUrl("/logger/rootLogger") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, s => - { - using var doc = JsonDocument.Parse(s); - return BitbucketHelpers.StringToLogLevel(doc.RootElement.GetProperty("logLevel").GetString()!); - }, cancellationToken) - .ConfigureAwait(false); - } + public async Task GetRootLogLevelAsync(CancellationToken cancellationToken = default) + { + var response = await GetLogsUrl("/logger/rootLogger") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task SetRootLogLevelAsync(LogLevels logLevel, CancellationToken cancellationToken = default) + return await HandleResponseAsync(response, s => { - var response = await GetLogsUrl($"/logger/rootLogger/{BitbucketHelpers.LogLevelToString(logLevel)}") - .PutAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + using var doc = JsonDocument.Parse(s); + return BitbucketHelpers.StringToLogLevel(doc.RootElement.GetProperty("logLevel").GetString()!); + }, cancellationToken) + .ConfigureAwait(false); + } + + public async Task SetRootLogLevelAsync(LogLevels logLevel, CancellationToken cancellationToken = default) + { + var response = await GetLogsUrl($"/logger/rootLogger/{BitbucketHelpers.LogLevelToString(logLevel)}") + .PutAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs index 07e1574..2922fd2 100644 --- a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs @@ -1,46 +1,46 @@ +using Bitbucket.Net.Common; +using Flurl.Http; using System.Collections.Generic; using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common; -using Flurl.Http; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetMarkupUrl() => GetBaseUrl() - .AppendPathSegment("/markup"); + private IFlurlRequest GetMarkupUrl() => GetBaseUrl() + .AppendPathSegment("/markup"); - private IFlurlRequest GetMarkupUrl(string path) => GetMarkupUrl() - .AppendPathSegment(path); + private IFlurlRequest GetMarkupUrl(string path) => GetMarkupUrl() + .AppendPathSegment(path); - public async Task PreviewMarkupAsync(string text, - string? urlMode = null, - bool? hardWrap = null, - bool? htmlEscape = null, - CancellationToken cancellationToken = default) + public async Task PreviewMarkupAsync(string text, + string? urlMode = null, + bool? hardWrap = null, + bool? htmlEscape = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["urlMode"] = urlMode, - ["hardWrap"] = BitbucketHelpers.BoolToString(hardWrap), - ["htmlEscape"] = BitbucketHelpers.BoolToString(htmlEscape) - }; + ["urlMode"] = urlMode, + ["hardWrap"] = BitbucketHelpers.BoolToString(hardWrap), + ["htmlEscape"] = BitbucketHelpers.BoolToString(htmlEscape), + }; - var response = await GetMarkupUrl("/preview") - .WithHeader("X-Atlassian-Token", "no-check") - .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(text), cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetMarkupUrl("/preview") + .WithHeader("X-Atlassian-Token", "no-check") + .SetQueryParams(queryParamValues) + .PostJsonAsync(new StringContent(text), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, s => - { - using var doc = JsonDocument.Parse(s); - return doc.RootElement.GetProperty("html").GetString()!; - }, cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync(response, s => + { + using var doc = JsonDocument.Parse(s); + return doc.RootElement.GetProperty("html").GetString()!; + }, cancellationToken) + .ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs index 9f1dfd6..13a089f 100644 --- a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs @@ -1,41 +1,41 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Projects; using Flurl.Http; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Bitbucket.Net; -namespace Bitbucket.Net +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetProfileUrl() => GetBaseUrl() - .AppendPathSegment("/profile"); + private IFlurlRequest GetProfileUrl() => GetBaseUrl() + .AppendPathSegment("/profile"); - private IFlurlRequest GetProfileUrl(string path) => GetProfileUrl() - .AppendPathSegment(path); + private IFlurlRequest GetProfileUrl(string path) => GetProfileUrl() + .AppendPathSegment(path); - public async Task> GetRecentReposAsync(Permissions? permission = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetRecentReposAsync(Permissions? permission = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["permission"] = BitbucketHelpers.PermissionToString(permission) - }; + ["limit"] = limit, + ["start"] = start, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProfileUrl("/recent/repos") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProfileUrl("/recent/repos") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs index ac9c267..2360a67 100644 --- a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs @@ -1,3 +1,11 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Exceptions; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Admin; +using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Models.Core.Tasks; +using Bitbucket.Net.Models.Core.Users; +using Flurl.Http; using System; using System.Buffers; using System.Collections.Generic; @@ -9,2737 +17,2794 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common; -using Bitbucket.Net.Common.Exceptions; -using Bitbucket.Net.Common.Models; -using Bitbucket.Net.Models.Core.Admin; -using Bitbucket.Net.Models.Core.Projects; -using Bitbucket.Net.Models.Core.Tasks; -using Bitbucket.Net.Models.Core.Users; -using Flurl.Http; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetProjectsUrl() => GetBaseUrl() - .AppendPathSegment("/projects"); + private IFlurlRequest GetProjectsUrl() => GetBaseUrl() + .AppendPathSegment("/projects"); - private IFlurlRequest GetProjectsUrl(string path) => GetProjectsUrl() - .AppendPathSegment(path); + private IFlurlRequest GetProjectsUrl(string path) => GetProjectsUrl() + .AppendPathSegment(path); - private IFlurlRequest GetProjectUrl(string projectKey) => GetProjectsUrl() - .AppendPathSegment($"/{projectKey}"); + private IFlurlRequest GetProjectUrl(string projectKey) => GetProjectsUrl() + .AppendPathSegment($"/{projectKey}"); - private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug) => GetProjectsUrl($"/{projectKey}/repos/{repositorySlug}"); + private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug) => GetProjectsUrl($"/{projectKey}/repos/{repositorySlug}"); - private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug, string path) => GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment(path); + private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug, string path) => GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment(path); - public async Task> GetProjectsAsync( - int? maxPages = null, - int? limit = null, - int? start = null, - string? name = null, - Permissions? permission = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["name"] = name, - ["permission"] = BitbucketHelpers.PermissionToString(permission) - }; + public async Task> GetProjectsAsync( + int? maxPages = null, + int? limit = null, + int? start = null, + string? name = null, + Permissions? permission = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["name"] = name, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsUrl() + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + /// + /// Streams all projects as an IAsyncEnumerable, yielding items as they are retrieved. + /// This is more memory-efficient for large result sets. + /// + public IAsyncEnumerable GetProjectsStreamAsync( + int? maxPages = null, + int? limit = null, + int? start = null, + string? name = null, + Permissions? permission = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["name"] = name, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsUrl() + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken); + } - /// - /// Streams all projects as an IAsyncEnumerable, yielding items as they are retrieved. - /// This is more memory-efficient for large result sets. - /// - public IAsyncEnumerable GetProjectsStreamAsync( - int? maxPages = null, - int? limit = null, - int? start = null, - string? name = null, - Permissions? permission = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["name"] = name, - ["permission"] = BitbucketHelpers.PermissionToString(permission) - }; + public async Task CreateProjectAsync(ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl() + .PostJsonAsync(projectDefinition, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task CreateProjectAsync(ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) - { - var response = await GetProjectsUrl() - .PostJsonAsync(projectDefinition, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteProjectAsync(string projectKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl($"/{projectKey}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task DeleteProjectAsync(string projectKey, CancellationToken cancellationToken = default) - { - var response = await GetProjectsUrl($"/{projectKey}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateProjectAsync(string projectKey, ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl($"/{projectKey}") + .PutJsonAsync(projectDefinition, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task UpdateProjectAsync(string projectKey, ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) - { - var response = await GetProjectsUrl($"/{projectKey}") - .PutJsonAsync(projectDefinition, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetProjectAsync(string projectKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl($"/{projectKey}") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + public async Task> GetProjectUserPermissionsAsync(string projectKey, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsUrl($"/{projectKey}/permissions/users") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task GetProjectAsync(string projectKey, CancellationToken cancellationToken = default) + public async Task DeleteProjectUserPermissionsAsync(string projectKey, string userName, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var response = await GetProjectsUrl($"/{projectKey}") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["name"] = userName, + }; - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + var response = await GetProjectsUrl($"/{projectKey}/permissions/users") + .SetQueryParams(queryParamValues) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task> GetProjectUserPermissionsAsync(string projectKey, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) + public async Task UpdateProjectUserPermissionsAsync(string projectKey, string userName, Permissions permission, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - ["avatarSize"] = avatarSize - }; + ["name"] = userName, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/permissions/users") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + var response = await GetProjectsUrl($"/{projectKey}/permissions/users") + .SetQueryParams(queryParamValues) + .PutAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task DeleteProjectUserPermissionsAsync(string projectKey, string userName, CancellationToken cancellationToken = default) + public async Task> GetProjectUserPermissionsNoneAsync(string projectKey, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["name"] = userName - }; + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; - var response = await GetProjectsUrl($"/{projectKey}/permissions/users") - .SetQueryParams(queryParamValues) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsUrl($"/{projectKey}/permissions/users/none") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task> GetProjectGroupPermissionsAsync(string projectKey, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsUrl($"/{projectKey}/permissions/groups") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdateProjectUserPermissionsAsync(string projectKey, string userName, Permissions permission, CancellationToken cancellationToken = default) + public async Task DeleteProjectGroupPermissionsAsync(string projectKey, string groupName, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["name"] = userName, - ["permission"] = BitbucketHelpers.PermissionToString(permission) - }; + ["name"] = groupName, + }; - var response = await GetProjectsUrl($"/{projectKey}/permissions/users") - .SetQueryParams(queryParamValues) - .PutAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") + .SetQueryParams(queryParamValues) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task> GetProjectUserPermissionsNoneAsync(string projectKey, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task UpdateProjectGroupPermissionsAsync(string projectKey, string groupName, Permissions permission, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter - }; + ["name"] = groupName, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/permissions/users/none") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") + .SetQueryParams(queryParamValues) + .PutAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task> GetProjectGroupPermissionsAsync(string projectKey, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetProjectGroupPermissionsNoneAsync(string projectKey, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter - }; + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/permissions/groups") + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsUrl($"/{projectKey}/permissions/groups/none") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) + .GetJsonAsync>(cancellationToken: ct) .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task DeleteProjectGroupPermissionsAsync(string projectKey, string groupName, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["name"] = groupName - }; + .ConfigureAwait(false); + } - var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") - .SetQueryParams(queryParamValues) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task IsProjectDefaultPermissionAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, s => + { + using var doc = JsonDocument.Parse(s); + return doc.RootElement.GetProperty("permitted").GetBoolean(); + }, cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdateProjectGroupPermissionsAsync(string projectKey, string groupName, Permissions permission, CancellationToken cancellationToken = default) + private async Task SetProjectDefaultPermissionAsync(string projectKey, Permissions permission, bool allow, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["name"] = groupName, - ["permission"] = BitbucketHelpers.PermissionToString(permission) - }; + ["allow"] = BitbucketHelpers.BoolToString(allow), + }; - var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") - .SetQueryParams(queryParamValues) - .PutAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") + .SetQueryParams(queryParamValues) + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task> GetProjectGroupPermissionsNoneAsync(string projectKey, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter - }; + public async Task GrantProjectPermissionToAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) + { + return await SetProjectDefaultPermissionAsync(projectKey, permission, allow: true, cancellationToken).ConfigureAwait(false); + } - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/permissions/groups/none") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + public async Task RevokeProjectPermissionFromAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) + { + return await SetProjectDefaultPermissionAsync(projectKey, permission, allow: false, cancellationToken).ConfigureAwait(false); + } - public async Task IsProjectDefaultPermissionAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) - { - var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetProjectRepositoriesAsync(string projectKey, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsUrl($"/{projectKey}/repos") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, s => - { - using var doc = JsonDocument.Parse(s); - return doc.RootElement.GetProperty("permitted").GetBoolean(); - }, cancellationToken) - .ConfigureAwait(false); - } + /// + /// Streams all repositories for a project as an IAsyncEnumerable. + /// + public IAsyncEnumerable GetProjectRepositoriesStreamAsync(string projectKey, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsUrl($"/{projectKey}/repos") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken); + } - private async Task SetProjectDefaultPermissionAsync(string projectKey, Permissions permission, bool allow, CancellationToken cancellationToken = default) + public async Task CreateProjectRepositoryAsync(string projectKey, string repositoryName, string scmId = "git", CancellationToken cancellationToken = default) + { + var data = new { - var queryParamValues = new Dictionary - { - ["allow"] = BitbucketHelpers.BoolToString(allow) - }; + name = repositoryName, + scmId, + }; - var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") - .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetProjectsUrl($"/{projectKey}/repos") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GrantProjectPermissionToAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) - { - return await SetProjectDefaultPermissionAsync(projectKey, permission, true, cancellationToken).ConfigureAwait(false); - } + public async Task GetProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task RevokeProjectPermissionFromAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) + public async Task CreateProjectRepositoryForkAsync(string projectKey, string repositorySlug, string? targetProjectKey = null, string? targetSlug = null, string? targetName = null, CancellationToken cancellationToken = default) + { + var data = new { - return await SetProjectDefaultPermissionAsync(projectKey, permission, false, cancellationToken).ConfigureAwait(false); - } + slug = targetSlug ?? repositorySlug, + name = targetName, + project = targetProjectKey == null ? null : new ProjectRef { Key = targetProjectKey }, + }; - public async Task> GetProjectRepositoriesAsync(string projectKey, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start - }; + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/repos") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - /// - /// Streams all repositories for a project as an IAsyncEnumerable. - /// - public IAsyncEnumerable GetProjectRepositoriesStreamAsync(string projectKey, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start - }; + public async Task ScheduleProjectRepositoryForDeletionAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/repos") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task CreateProjectRepositoryAsync(string projectKey, string repositoryName, string scmId = "git", CancellationToken cancellationToken = default) + public async Task UpdateProjectRepositoryAsync(string projectKey, string repositorySlug, + string? targetName = null, + bool? isForkable = null, + string? targetProjectKey = null, + bool? isPublic = null, + CancellationToken cancellationToken = default) + { + var data = new { - var data = new - { - name = repositoryName, - scmId - }; - - var response = await GetProjectsUrl($"/{projectKey}/repos") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + name = targetName, + forkable = isForkable, + project = targetProjectKey == null ? null : new ProjectRef { Key = targetProjectKey }, + @public = isPublic, + }; - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .PutJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task GetProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task CreateProjectRepositoryForkAsync(string projectKey, string repositorySlug, string? targetProjectKey = null, string? targetSlug = null, string? targetName = null, CancellationToken cancellationToken = default) + public async Task> GetProjectRepositoryForksAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var data = new - { - slug = targetSlug ?? repositorySlug, - name = targetName, - project = targetProjectKey == null ? null : new ProjectRef { Key = targetProjectKey } - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["limit"] = limit, + ["start"] = start, + }; - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/forks") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task ScheduleProjectRepositoryForDeletionAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task RecreateProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/recreate") + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task UpdateProjectRepositoryAsync(string projectKey, string repositorySlug, - string? targetName = null, - bool? isForkable = null, - string? targetProjectKey = null, - bool? isPublic = null, - CancellationToken cancellationToken = default) + public async Task> GetRelatedProjectRepositoriesAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var data = new - { - name = targetName, - forkable = isForkable, - project = targetProjectKey == null ? null : new ProjectRef { Key = targetProjectKey }, - @public = isPublic - }; + ["limit"] = limit, + ["start"] = start, + }; - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .PutJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/related") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task GetProjectRepositoryArchiveAsync(string projectKey, string repositorySlug, + string at, + string fileName, + ArchiveFormats archiveFormat, + string path, + string prefix, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["at"] = at, + ["fileName"] = fileName, + ["format"] = BitbucketHelpers.ArchiveFormatToString(archiveFormat), + ["path"] = path, + ["prefix"] = prefix, + }; + + return await GetProjectsReposUrl(projectKey, repositorySlug, "/archive") + .SetQueryParams(queryParamValues) + .GetBytesAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetProjectRepositoryForksAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, + string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start - }; + ["filter"] = filter, + ["limit"] = limit, + ["start"] = start, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/forks") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task RecreateProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + public async Task UpdateProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/recreate") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["permission"] = BitbucketHelpers.PermissionToString(permission), + ["name"] = name, + }; - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") + .SetQueryParams(queryParamValues) + .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task> GetRelatedProjectRepositoriesAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start - }; + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/related") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + public async Task DeleteProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, string name, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") + .SetQueryParam("name", name) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task GetProjectRepositoryArchiveAsync(string projectKey, string repositorySlug, - string at, - string fileName, - ArchiveFormats archiveFormat, - string path, - string prefix, - CancellationToken cancellationToken = default) + public async Task> GetProjectRepositoryGroupPermissionsNoneAsync(string projectKey, string repositorySlug, + string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["at"] = at, - ["fileName"] = fileName, - ["format"] = BitbucketHelpers.ArchiveFormatToString(archiveFormat), - ["path"] = path, - ["prefix"] = prefix - }; + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; - return await GetProjectsReposUrl(projectKey, repositorySlug, "/archive") - .SetQueryParams(queryParamValues) - .GetBytesAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups/none") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, - string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, + string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["filter"] = filter, - ["limit"] = limit, - ["start"] = start - }; + ["filter"] = filter, + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdateProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) + public async Task UpdateProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["permission"] = BitbucketHelpers.PermissionToString(permission), - ["name"] = name - }; + ["permission"] = BitbucketHelpers.PermissionToString(permission), + ["name"] = name, + }; - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") - .SetQueryParams(queryParamValues) - .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") + .SetQueryParams(queryParamValues) + .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task DeleteProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, string name, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") - .SetQueryParam("name", name) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, string name, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") + .SetQueryParam("name", name) + .SetQueryParam("avatarSize", avatarSize) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task> GetProjectRepositoryGroupPermissionsNoneAsync(string projectKey, string repositorySlug, - string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetProjectRepositoryUserPermissionsNoneAsync(string projectKey, string repositorySlug, + string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter - }; + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups/none") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users/none") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, - string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["filter"] = filter, - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize - }; + public async Task> GetBranchesAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + string? baseBranchOrTag = null, + bool? details = null, + string? filterText = null, + BranchOrderBy? orderBy = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["base"] = baseBranchOrTag, + ["details"] = details.HasValue ? BitbucketHelpers.BoolToString(details.Value) : null, + ["filterText"] = filterText, + ["orderBy"] = orderBy.HasValue ? BitbucketHelpers.BranchOrderByToString(orderBy.Value) : null, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + /// + /// Streams all branches for a repository as an IAsyncEnumerable. + /// + public IAsyncEnumerable GetBranchesStreamAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + string? baseBranchOrTag = null, + bool? details = null, + string? filterText = null, + BranchOrderBy? orderBy = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["base"] = baseBranchOrTag, + ["details"] = details.HasValue ? BitbucketHelpers.BoolToString(details.Value) : null, + ["filterText"] = filterText, + ["orderBy"] = orderBy.HasValue ? BitbucketHelpers.BranchOrderByToString(orderBy.Value) : null, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken); + } - public async Task UpdateProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["permission"] = BitbucketHelpers.PermissionToString(permission), - ["name"] = name - }; + public async Task CreateBranchAsync(string projectKey, string repositorySlug, BranchInfo branchInfo, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .PostJsonAsync(branchInfo, cancellationToken: cancellationToken) + .ConfigureAwait(false); - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") - .SetQueryParams(queryParamValues) - .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task GetDefaultBranchAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, "/branches/default") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task DeleteProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, string name, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") - .SetQueryParam("name", name) - .SetQueryParam("avatarSize", avatarSize) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task SetDefaultBranchAsync(string projectKey, string repositorySlug, BranchRef branchRef, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .PutJsonAsync(branchRef, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task> GetProjectRepositoryUserPermissionsNoneAsync(string projectKey, string repositorySlug, - string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task BrowseProjectRepositoryAsync(string projectKey, string repositorySlug, string at, bool type = false, + bool blame = false, + bool noContent = false, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users/none") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); + ["at"] = at, + ["type"] = BitbucketHelpers.BoolToString(type), + }; + if (blame) + { + queryParamValues.Add("blame", value: null); } - - public async Task> GetBranchesAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - string? baseBranchOrTag = null, - bool? details = null, - string? filterText = null, - BranchOrderBy? orderBy = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["base"] = baseBranchOrTag, - ["details"] = details.HasValue ? BitbucketHelpers.BoolToString(details.Value) : null, - ["filterText"] = filterText, - ["orderBy"] = orderBy.HasValue ? BitbucketHelpers.BranchOrderByToString(orderBy.Value) : null - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); + if (blame && noContent) + { + queryParamValues.Add("noContent", value: null); } - /// - /// Streams all branches for a repository as an IAsyncEnumerable. - /// - public IAsyncEnumerable GetBranchesStreamAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - string? baseBranchOrTag = null, - bool? details = null, - string? filterText = null, - BranchOrderBy? orderBy = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["base"] = baseBranchOrTag, - ["details"] = details.HasValue ? BitbucketHelpers.BoolToString(details.Value) : null, - ["filterText"] = filterText, - ["orderBy"] = orderBy.HasValue ? BitbucketHelpers.BranchOrderByToString(orderBy.Value) : null - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); - } + return await GetProjectsReposUrl(projectKey, repositorySlug, "/browse") + .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task CreateBranchAsync(string projectKey, string repositorySlug, BranchInfo branchInfo, CancellationToken cancellationToken = default) + public async Task BrowseProjectRepositoryPathAsync(string projectKey, string repositorySlug, string path, string at, bool type = false, + bool blame = false, + bool noContent = false, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .PostJsonAsync(branchInfo, cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - public async Task GetDefaultBranchAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + ["at"] = at, + ["type"] = BitbucketHelpers.BoolToString(type), + }; + if (blame) { - return await GetProjectsReposUrl(projectKey, repositorySlug, "/branches/default") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + queryParamValues.Add("blame", value: null); } - - public async Task SetDefaultBranchAsync(string projectKey, string repositorySlug, BranchRef branchRef, CancellationToken cancellationToken = default) + if (blame && noContent) { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .PutJsonAsync(branchRef, cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + queryParamValues.Add("noContent", value: null); } - public async Task BrowseProjectRepositoryAsync(string projectKey, string repositorySlug, string at, bool type = false, - bool blame = false, - bool noContent = false, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["at"] = at, - ["type"] = BitbucketHelpers.BoolToString(type) - }; - if (blame) - { - queryParamValues.Add("blame", null); - } - if (blame && noContent) - { - queryParamValues.Add("noContent", null); - } + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") + .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await GetProjectsReposUrl(projectKey, repositorySlug, "/browse") - .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + /// + /// Gets the raw content of a file as a stream. This is optimal for large files as it doesn't buffer the entire content in memory. + /// + /// The project key. + /// The repository slug. + /// The file path within the repository. + /// Optional ref (branch, tag, or commit) to get the file content at. Defaults to default branch. + /// Cancellation token. + /// A stream containing the raw file content. Caller is responsible for disposing. + public async Task GetRawFileContentStreamAsync(string projectKey, string repositorySlug, string path, + string? at = null, + CancellationToken cancellationToken = default) + { + var request = GetProjectsReposUrl(projectKey, repositorySlug, $"/raw/{path}"); - public async Task BrowseProjectRepositoryPathAsync(string projectKey, string repositorySlug, string path, string at, bool type = false, - bool blame = false, - bool noContent = false, - CancellationToken cancellationToken = default) + if (!string.IsNullOrEmpty(at)) { - var queryParamValues = new Dictionary - { - ["at"] = at, - ["type"] = BitbucketHelpers.BoolToString(type) - }; - if (blame) - { - queryParamValues.Add("blame", null); - } - if (blame && noContent) - { - queryParamValues.Add("noContent", null); - } - - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") - .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + request = request.SetQueryParam("at", at); } - /// - /// Gets the raw content of a file as a stream. This is optimal for large files as it doesn't buffer the entire content in memory. - /// - /// The project key. - /// The repository slug. - /// The file path within the repository. - /// Optional ref (branch, tag, or commit) to get the file content at. Defaults to default branch. - /// Cancellation token. - /// A stream containing the raw file content. Caller is responsible for disposing. - public async Task GetRawFileContentStreamAsync(string projectKey, string repositorySlug, string path, - string? at = null, - CancellationToken cancellationToken = default) - { - var request = GetProjectsReposUrl(projectKey, repositorySlug, $"/raw/{path}"); - - if (!string.IsNullOrEmpty(at)) - { - request = request.SetQueryParam("at", at); - } + return await request + .GetStreamAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await request - .GetStreamAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + /// + /// Streams the raw content of a file line by line. This is optimal for large text files. + /// + /// The project key. + /// The repository slug. + /// The file path within the repository. + /// Optional ref (branch, tag, or commit) to get the file content at. Defaults to default branch. + /// Cancellation token. + /// An async enumerable of lines from the file. + public async IAsyncEnumerable GetRawFileContentLinesStreamAsync(string projectKey, string repositorySlug, string path, + string? at = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var stream = await GetRawFileContentStreamAsync(projectKey, repositorySlug, path, at, cancellationToken).ConfigureAwait(false); - /// - /// Streams the raw content of a file line by line. This is optimal for large text files. - /// - /// The project key. - /// The repository slug. - /// The file path within the repository. - /// Optional ref (branch, tag, or commit) to get the file content at. Defaults to default branch. - /// Cancellation token. - /// An async enumerable of lines from the file. - public async IAsyncEnumerable GetRawFileContentLinesStreamAsync(string projectKey, string repositorySlug, string path, - string? at = null, - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var stream = await GetRawFileContentStreamAsync(projectKey, repositorySlug, path, at, cancellationToken).ConfigureAwait(false); - - await using (stream.ConfigureAwait(false)) + await using (stream.ConfigureAwait(false)) + { + using var reader = new StreamReader(stream); + while (!reader.EndOfStream) { - using var reader = new StreamReader(stream); - while (!reader.EndOfStream) + cancellationToken.ThrowIfCancellationRequested(); + var line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); + if (line is not null) { - cancellationToken.ThrowIfCancellationRequested(); - var line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); - if (line is not null) - { - yield return line; - } + yield return line; } } } + } - /// - /// Updates a file at the specified path in the repository. - /// Uses ArrayPool<byte> for zero-copy buffer management to minimize heap allocations. - /// - public async Task UpdateProjectRepositoryPathAsync(string projectKey, string repositorySlug, string path, - string fileName, - string branch, - string? message = null, - string? sourceCommitId = null, - string? sourceBranch = null, - CancellationToken cancellationToken = default) - { - if (!File.Exists(fileName)) - { - throw new ArgumentException($"File doesn't exist: {fileName}", nameof(fileName)); - } - - var fileInfo = new FileInfo(fileName); - int fileSize = checked((int)fileInfo.Length); - - // Use ArrayPool to rent a buffer instead of allocating new array - byte[] buffer = ArrayPool.Shared.Rent(fileSize); - try - { - int bytesRead; - var stm = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); - await using (stm.ConfigureAwait(false)) - { - bytesRead = await stm.ReadAsync(buffer.AsMemory(0, fileSize), cancellationToken).ConfigureAwait(false); - } - - // Create MemoryStream over the exact bytes read (not the rented buffer size) - using var memoryStream = new MemoryStream(buffer, 0, bytesRead, writable: false); - - var data = new DynamicMultipartFormDataContent - { - { new StreamContent(memoryStream), "content" }, - { new StringContent(branch), "branch" }, - { message, message == null ? null : new StringContent(message), "message" }, - { sourceCommitId, sourceCommitId == null ? null : new StringContent(sourceCommitId), "sourceCommitId" }, - { sourceBranch, sourceBranch == null ? null : new StringContent(sourceBranch), "sourceBranch" } - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") - .PutAsync(data.ToMultipartFormDataContent(), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - finally - { - // Always return the buffer to the pool - ArrayPool.Shared.Return(buffer); - } + /// + /// Updates a file at the specified path in the repository. + /// Uses ArrayPool<byte> for zero-copy buffer management to minimize heap allocations. + /// + public async Task UpdateProjectRepositoryPathAsync(string projectKey, string repositorySlug, string path, + string fileName, + string branch, + string? message = null, + string? sourceCommitId = null, + string? sourceBranch = null, + CancellationToken cancellationToken = default) + { + if (!File.Exists(fileName)) + { + throw new ArgumentException($"File doesn't exist: {fileName}", nameof(fileName)); } - public async Task> GetChangesAsync(string projectKey, string repositorySlug, string until, string? since = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + var fileInfo = new FileInfo(fileName); + int fileSize = checked((int)fileInfo.Length); + + // Use ArrayPool to rent a buffer instead of allocating new array + byte[] buffer = ArrayPool.Shared.Rent(fileSize); + try { - var queryParamValues = new Dictionary + int bytesRead; + var stm = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); + await using (stm.ConfigureAwait(false)) { - ["limit"] = limit, - ["start"] = start, - ["since"] = since, - ["until"] = until - }; + bytesRead = await stm.ReadAsync(buffer.AsMemory(0, fileSize), cancellationToken).ConfigureAwait(false); + } - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + // Create MemoryStream over the exact bytes read (not the rented buffer size) + using var memoryStream = new MemoryStream(buffer, 0, bytesRead, writable: false); - public async Task> GetCommitsAsync(string projectKey, string repositorySlug, - string until, - bool followRenames = false, - bool ignoreMissing = false, - MergeCommits merges = MergeCommits.Exclude, - string? path = null, - string? since = null, - bool withCounts = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary + var data = new DynamicMultipartFormDataContent { - ["limit"] = limit, - ["start"] = start, - ["followRenames"] = BitbucketHelpers.BoolToString(followRenames), - ["ignoreMissing"] = BitbucketHelpers.BoolToString(ignoreMissing), - ["merges"] = BitbucketHelpers.MergeCommitsToString(merges), - ["path"] = path, - ["since"] = since, - ["until"] = until, - ["withCounts"] = BitbucketHelpers.BoolToString(withCounts) + { new StreamContent(memoryStream), "content" }, + { new StringContent(branch), "branch" }, + { message, message == null ? null : new StringContent(message), "message" }, + { sourceCommitId, sourceCommitId == null ? null : new StringContent(sourceCommitId), "sourceCommitId" }, + { sourceBranch, sourceBranch == null ? null : new StringContent(sourceBranch), "sourceBranch" }, }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") + .PutAsync(data.ToMultipartFormDataContent(), cancellationToken: cancellationToken) .ConfigureAwait(false); - } - - /// - /// Streams all commits for a repository as an IAsyncEnumerable. - /// - public IAsyncEnumerable GetCommitsStreamAsync(string projectKey, string repositorySlug, - string until, - bool followRenames = false, - bool ignoreMissing = false, - MergeCommits merges = MergeCommits.Exclude, - string? path = null, - string? since = null, - bool withCounts = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["followRenames"] = BitbucketHelpers.BoolToString(followRenames), - ["ignoreMissing"] = BitbucketHelpers.BoolToString(ignoreMissing), - ["merges"] = BitbucketHelpers.MergeCommitsToString(merges), - ["path"] = path, - ["since"] = since, - ["until"] = until, - ["withCounts"] = BitbucketHelpers.BoolToString(withCounts) - }; - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } - - public async Task GetCommitAsync(string projectKey, string repositorySlug, string commitId, string? path = null, CancellationToken cancellationToken = default) + finally { - var queryParamValues = new Dictionary - { - ["path"] = path - }; - - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}") - .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + // Always return the buffer to the pool + ArrayPool.Shared.Return(buffer); } + } - public async Task> GetCommitChangesAsync(string projectKey, string repositorySlug, string commitId, - string? since = null, - bool withComments = true, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetChangesAsync(string projectKey, string repositorySlug, string until, string? since = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["since"] = since, - ["withComments"] = BitbucketHelpers.BoolToString(withComments) - }; + ["limit"] = limit, + ["start"] = start, + ["since"] = since, + ["until"] = until, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/changes") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetCommitCommentsAsync(string projectKey, string repositorySlug, string commitId, - string path, - string? since = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["path"] = path, - ["since"] = since - }; + public async Task> GetCommitsAsync(string projectKey, string repositorySlug, + string until, + bool followRenames = false, + bool ignoreMissing = false, + MergeCommits merges = MergeCommits.Exclude, + string? path = null, + string? since = null, + bool withCounts = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["followRenames"] = BitbucketHelpers.BoolToString(followRenames), + ["ignoreMissing"] = BitbucketHelpers.BoolToString(ignoreMissing), + ["merges"] = BitbucketHelpers.MergeCommitsToString(merges), + ["path"] = path, + ["since"] = since, + ["until"] = until, + ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + /// + /// Streams all commits for a repository as an IAsyncEnumerable. + /// + public IAsyncEnumerable GetCommitsStreamAsync(string projectKey, string repositorySlug, + string until, + bool followRenames = false, + bool ignoreMissing = false, + MergeCommits merges = MergeCommits.Exclude, + string? path = null, + string? since = null, + bool withCounts = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["followRenames"] = BitbucketHelpers.BoolToString(followRenames), + ["ignoreMissing"] = BitbucketHelpers.BoolToString(ignoreMissing), + ["merges"] = BitbucketHelpers.MergeCommitsToString(merges), + ["path"] = path, + ["since"] = since, + ["until"] = until, + ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken); + } - public async Task CreateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, - CommentInfo commentInfo, string? since = null, CancellationToken cancellationToken = default) + public async Task GetCommitAsync(string projectKey, string repositorySlug, string commitId, string? path = null, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["since"] = since - }; + ["path"] = path, + }; - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") - .SetQueryParams(queryParamValues) - .PostJsonAsync(commentInfo, cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}") + .SetQueryParams(queryParamValues) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task GetCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, - int? avatarSize = null, - CancellationToken cancellationToken = default) + public async Task> GetCommitChangesAsync(string projectKey, string repositorySlug, string commitId, + string? since = null, + bool withComments = true, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") - .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + ["limit"] = limit, + ["start"] = start, + ["since"] = since, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; - public async Task UpdateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, - CommentText commentText, CancellationToken cancellationToken = default) + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/changes") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task> GetCommitCommentsAsync(string projectKey, string repositorySlug, string commitId, + string path, + string? since = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") - .PutJsonAsync(commentText, cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["limit"] = limit, + ["start"] = start, + ["path"] = path, + ["since"] = since, + }; - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task DeleteCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, - int version = -1, - CancellationToken cancellationToken = default) + public async Task CreateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, + CommentInfo commentInfo, string? since = null, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["version"] = version - }; + ["since"] = since, + }; - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") - .SetQueryParams(queryParamValues) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") + .SetQueryParams(queryParamValues) + .PostJsonAsync(commentInfo, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetCommitDiffAsync(string projectKey, string repositorySlug, string commitId, - bool autoSrcPath = false, - int contextLines = -1, - string? since = null, - string? srcPath = null, - string whitespace = "ignore-all", - bool withComments = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), - ["contextLines"] = contextLines, - ["since"] = since, - ["srcPath"] = srcPath, - ["whitespace"] = whitespace, - ["withComments"] = BitbucketHelpers.BoolToString(withComments) - }; + public async Task GetCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") + .SetQueryParam("avatarSize", avatarSize) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") - .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task UpdateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, + CommentText commentText, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") + .PutJsonAsync(commentText, cancellationToken: cancellationToken) + .ConfigureAwait(false); - /// - /// Streams the diff for a specific commit, yielding individual diff entries as they are parsed. - /// This is more memory-efficient for large diffs. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// Auto source path. - /// Number of context lines. - /// Since commit. - /// Source path filter. - /// Whitespace handling. - /// Include comments. - /// Cancellation token. - /// An async enumerable of diffs. - public async IAsyncEnumerable GetCommitDiffStreamAsync(string projectKey, string repositorySlug, string commitId, - bool autoSrcPath = false, - int contextLines = -1, - string? since = null, - string? srcPath = null, - string whitespace = "ignore-all", - bool withComments = true, - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), - ["contextLines"] = contextLines, - ["since"] = since, - ["srcPath"] = srcPath, - ["whitespace"] = whitespace, - ["withComments"] = BitbucketHelpers.BoolToString(withComments) - }; + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") - .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, + int version = -1, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["version"] = version, + }; - await using (responseStream.ConfigureAwait(false)) - { - await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) - { - yield return diff; - } - } - } + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") + .SetQueryParams(queryParamValues) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task CreateCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task GetCommitDiffAsync(string projectKey, string repositorySlug, string commitId, + bool autoSrcPath = false, + int contextLines = -1, + string? since = null, + string? srcPath = null, + string whitespace = "ignore-all", + bool withComments = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), + ["contextLines"] = contextLines, + ["since"] = since, + ["srcPath"] = srcPath, + ["whitespace"] = whitespace, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") + .SetQueryParams(queryParamValues) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task DeleteCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) + /// + /// Streams the diff for a specific commit, yielding individual diff entries as they are parsed. + /// This is more memory-efficient for large diffs. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Auto source path. + /// Number of context lines. + /// Since commit. + /// Source path filter. + /// Whitespace handling. + /// Include comments. + /// Cancellation token. + /// An async enumerable of diffs. + public async IAsyncEnumerable GetCommitDiffStreamAsync(string projectKey, string repositorySlug, string commitId, + bool autoSrcPath = false, + int contextLines = -1, + string? since = null, + string? srcPath = null, + string whitespace = "ignore-all", + bool withComments = true, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), + ["contextLines"] = contextLines, + ["since"] = since, + ["srcPath"] = srcPath, + ["whitespace"] = whitespace, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") + .SetQueryParams(queryParamValues) + .GetStreamAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task> GetRepositoryCompareChangesAsync(string projectKey, string repositorySlug, string from, string to, - string? fromRepo = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + await using (responseStream.ConfigureAwait(false)) { - var queryParamValues = new Dictionary + await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) { - ["limit"] = limit, - ["start"] = start, - ["from"] = from, - ["to"] = to, - ["fromRepo"] = fromRepo - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/changes") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); + yield return diff; + } } + } - public async Task GetRepositoryCompareDiffAsync(string projectKey, string repositorySlug, string from, string to, - string? fromRepo = null, - string? srcPath = null, - int contextLines = -1, - string whitespace = "ignore-all", - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["from"] = from, - ["to"] = to, - ["fromRepo"] = fromRepo, - ["srcPath"] = srcPath, - ["contextLines"] = contextLines, - ["whitespace"] = whitespace - }; + public async Task CreateCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") - .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - /// - /// Streams the compare diff between two refs, yielding individual diff entries as they are parsed. - /// This is more memory-efficient for large diffs. - /// - /// The project key. - /// The repository slug. - /// The source ref (branch, tag, or commit). - /// The target ref (branch, tag, or commit). - /// Optional source repository if comparing across forks. - /// Source path filter. - /// Number of context lines. - /// Whitespace handling. - /// Cancellation token. - /// An async enumerable of diffs. - public async IAsyncEnumerable GetRepositoryCompareDiffStreamAsync(string projectKey, string repositorySlug, string from, string to, - string? fromRepo = null, - string? srcPath = null, - int contextLines = -1, - string whitespace = "ignore-all", - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["from"] = from, - ["to"] = to, - ["fromRepo"] = fromRepo, - ["srcPath"] = srcPath, - ["contextLines"] = contextLines, - ["whitespace"] = whitespace - }; + public async Task DeleteCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") - .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - await using (responseStream.ConfigureAwait(false)) - { - await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) - { - yield return diff; - } - } - } + public async Task> GetRepositoryCompareChangesAsync(string projectKey, string repositorySlug, string from, string to, + string? fromRepo = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["from"] = from, + ["to"] = to, + ["fromRepo"] = fromRepo, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/changes") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetRepositoryCompareCommitsAsync(string projectKey, string repositorySlug, string from, string to, - string? fromRepo = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task GetRepositoryCompareDiffAsync(string projectKey, string repositorySlug, string from, string to, + string? fromRepo = null, + string? srcPath = null, + int contextLines = -1, + string whitespace = "ignore-all", + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["from"] = from, + ["to"] = to, + ["fromRepo"] = fromRepo, + ["srcPath"] = srcPath, + ["contextLines"] = contextLines, + ["whitespace"] = whitespace, + }; + + return await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") + .SetQueryParams(queryParamValues) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams the compare diff between two refs, yielding individual diff entries as they are parsed. + /// This is more memory-efficient for large diffs. + /// + /// The project key. + /// The repository slug. + /// The source ref (branch, tag, or commit). + /// The target ref (branch, tag, or commit). + /// Optional source repository if comparing across forks. + /// Source path filter. + /// Number of context lines. + /// Whitespace handling. + /// Cancellation token. + /// An async enumerable of diffs. + public async IAsyncEnumerable GetRepositoryCompareDiffStreamAsync(string projectKey, string repositorySlug, string from, string to, + string? fromRepo = null, + string? srcPath = null, + int contextLines = -1, + string whitespace = "ignore-all", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["from"] = from, - ["to"] = to, - ["fromRepo"] = fromRepo - }; + ["from"] = from, + ["to"] = to, + ["fromRepo"] = fromRepo, + ["srcPath"] = srcPath, + ["contextLines"] = contextLines, + ["whitespace"] = whitespace, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/commits") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") + .SetQueryParams(queryParamValues) + .GetStreamAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task GetRepositoryDiffAsync(string projectKey, string repositorySlug, string until, - int contextLines = -1, - string? since = null, - string? srcPath = null, - string whitespace = "ignore-all", - CancellationToken cancellationToken = default) + await using (responseStream.ConfigureAwait(false)) { - var queryParamValues = new Dictionary + await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) { - ["contextLines"] = contextLines, - ["since"] = since, - ["srcPath"] = srcPath, - ["until"] = until, - ["whitespace"] = whitespace - }; - - return await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") - .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + yield return diff; + } } + } - /// - /// Streams the repository diff, yielding individual diff entries as they are parsed. - /// This is more memory-efficient for large diffs. - /// - /// The project key. - /// The repository slug. - /// The commit ID to diff until. - /// Number of context lines. - /// The commit ID to diff since. - /// Source path filter. - /// Whitespace handling. - /// Cancellation token. - /// An async enumerable of diffs. - public async IAsyncEnumerable GetRepositoryDiffStreamAsync(string projectKey, string repositorySlug, string until, - int contextLines = -1, - string? since = null, - string? srcPath = null, - string whitespace = "ignore-all", - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["contextLines"] = contextLines, - ["since"] = since, - ["srcPath"] = srcPath, - ["until"] = until, - ["whitespace"] = whitespace - }; - - var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") - .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetRepositoryCompareCommitsAsync(string projectKey, string repositorySlug, string from, string to, + string? fromRepo = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["from"] = from, + ["to"] = to, + ["fromRepo"] = fromRepo, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/commits") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - await using (responseStream.ConfigureAwait(false)) - { - await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) - { - yield return diff; - } - } - } + public async Task GetRepositoryDiffAsync(string projectKey, string repositorySlug, string until, + int contextLines = -1, + string? since = null, + string? srcPath = null, + string whitespace = "ignore-all", + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["contextLines"] = contextLines, + ["since"] = since, + ["srcPath"] = srcPath, + ["until"] = until, + ["whitespace"] = whitespace, + }; + + return await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") + .SetQueryParams(queryParamValues) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetRepositoryFilesAsync(string projectKey, string repositorySlug, string? at = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + /// + /// Streams the repository diff, yielding individual diff entries as they are parsed. + /// This is more memory-efficient for large diffs. + /// + /// The project key. + /// The repository slug. + /// The commit ID to diff until. + /// Number of context lines. + /// The commit ID to diff since. + /// Source path filter. + /// Whitespace handling. + /// Cancellation token. + /// An async enumerable of diffs. + public async IAsyncEnumerable GetRepositoryDiffStreamAsync(string projectKey, string repositorySlug, string until, + int contextLines = -1, + string? since = null, + string? srcPath = null, + string whitespace = "ignore-all", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["at"] = at - }; + ["contextLines"] = contextLines, + ["since"] = since, + ["srcPath"] = srcPath, + ["until"] = until, + ["whitespace"] = whitespace, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/files") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") + .SetQueryParams(queryParamValues) + .GetStreamAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task GetProjectRepositoryLastModifiedAsync(string projectKey, string repositorySlug, string at, CancellationToken cancellationToken = default) + await using (responseStream.ConfigureAwait(false)) { - return await GetProjectsReposUrl(projectKey, repositorySlug, "/last-modified") - .SetQueryParam("at", at) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) + { + yield return diff; + } } + } - public async Task> GetRepositoryParticipantsAsync(string projectKey, string repositorySlug, - PullRequestDirections direction = PullRequestDirections.Incoming, - string? filter = null, - Roles? role = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetRepositoryFilesAsync(string projectKey, string repositorySlug, string? at = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), - ["filter"] = filter, - ["role"] = BitbucketHelpers.RoleToString(role) - }; + ["limit"] = limit, + ["start"] = start, + ["at"] = at, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/participants") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/files") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetPullRequestsAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - PullRequestDirections direction = PullRequestDirections.Incoming, - string? branchId = null, - PullRequestStates state = PullRequestStates.Open, - PullRequestOrders order = PullRequestOrders.Newest, - bool withAttributes = true, - bool withProperties = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), - ["at"] = branchId, - ["state"] = BitbucketHelpers.PullRequestStateToString(state), - ["order"] = BitbucketHelpers.PullRequestOrderToString(order), - ["withAttributes"] = BitbucketHelpers.BoolToString(withAttributes), - ["withProperties"] = BitbucketHelpers.BoolToString(withProperties) - }; + public async Task GetProjectRepositoryLastModifiedAsync(string projectKey, string repositorySlug, string at, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, "/last-modified") + .SetQueryParam("at", at) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") + public async Task> GetRepositoryParticipantsAsync(string projectKey, string repositorySlug, + PullRequestDirections direction = PullRequestDirections.Incoming, + string? filter = null, + Roles? role = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), + ["filter"] = filter, + ["role"] = BitbucketHelpers.RoleToString(role), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/participants") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) + .GetJsonAsync>(cancellationToken: ct) .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + .ConfigureAwait(false); + } - /// - /// Streams all pull requests for a repository as an IAsyncEnumerable. - /// - public IAsyncEnumerable GetPullRequestsStreamAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - PullRequestDirections direction = PullRequestDirections.Incoming, - string? branchId = null, - PullRequestStates state = PullRequestStates.Open, - PullRequestOrders order = PullRequestOrders.Newest, - bool withAttributes = true, - bool withProperties = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), - ["at"] = branchId, - ["state"] = BitbucketHelpers.PullRequestStateToString(state), - ["order"] = BitbucketHelpers.PullRequestOrderToString(order), - ["withAttributes"] = BitbucketHelpers.BoolToString(withAttributes), - ["withProperties"] = BitbucketHelpers.BoolToString(withProperties) - }; + public async Task> GetPullRequestsAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + PullRequestDirections direction = PullRequestDirections.Incoming, + string? branchId = null, + PullRequestStates state = PullRequestStates.Open, + PullRequestOrders order = PullRequestOrders.Newest, + bool withAttributes = true, + bool withProperties = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), + ["at"] = branchId, + ["state"] = BitbucketHelpers.PullRequestStateToString(state), + ["order"] = BitbucketHelpers.PullRequestOrderToString(order), + ["withAttributes"] = BitbucketHelpers.BoolToString(withAttributes), + ["withProperties"] = BitbucketHelpers.BoolToString(withProperties), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); - } + /// + /// Streams all pull requests for a repository as an IAsyncEnumerable. + /// + public IAsyncEnumerable GetPullRequestsStreamAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + PullRequestDirections direction = PullRequestDirections.Incoming, + string? branchId = null, + PullRequestStates state = PullRequestStates.Open, + PullRequestOrders order = PullRequestOrders.Newest, + bool withAttributes = true, + bool withProperties = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), + ["at"] = branchId, + ["state"] = BitbucketHelpers.PullRequestStateToString(state), + ["order"] = BitbucketHelpers.PullRequestOrderToString(order), + ["withAttributes"] = BitbucketHelpers.BoolToString(withAttributes), + ["withProperties"] = BitbucketHelpers.BoolToString(withProperties), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken); + } - public async Task CreatePullRequestAsync(string projectKey, string repositorySlug, PullRequestInfo pullRequestInfo, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") - .PostJsonAsync(pullRequestInfo, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task CreatePullRequestAsync(string projectKey, string repositorySlug, PullRequestInfo pullRequestInfo, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") + .PostJsonAsync(pullRequestInfo, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdatePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, PullRequestUpdate pullRequestUpdate, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") - .PutJsonAsync(pullRequestUpdate, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdatePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, PullRequestUpdate pullRequestUpdate, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") + .PutJsonAsync(pullRequestUpdate, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeletePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, VersionInfo versionInfo, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") - .SendJsonAsync(HttpMethod.Delete, versionInfo, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeletePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, VersionInfo versionInfo, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") + .SendJsonAsync(HttpMethod.Delete, versionInfo, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task> GetPullRequestActivitiesAsync(string projectKey, string repositorySlug, long pullRequestId, - long? fromId = null, - PullRequestFromTypes? fromType = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) + public async Task> GetPullRequestActivitiesAsync(string projectKey, string repositorySlug, long pullRequestId, + long? fromId = null, + PullRequestFromTypes? fromType = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["fromId"] = fromId, + ["fromType"] = BitbucketHelpers.PullRequestFromTypeToString(fromType), + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/activities") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task DeclinePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["fromId"] = fromId, - ["fromType"] = BitbucketHelpers.PullRequestFromTypeToString(fromType), - ["avatarSize"] = avatarSize - }; + ["version"] = version, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/activities") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/decline") + .SetQueryParams(queryParamValues) + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task DeclinePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + public async Task GetPullRequestMergeStateAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["version"] = version - }; + ["version"] = version, + }; - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/decline") - .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") + .SetQueryParams(queryParamValues) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + /// + /// Gets the merge base (common ancestor) commit for a pull request. + /// This is the best common ancestor between the latest commits of the source and target branches. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The merge base commit, or null if not found (HTTP 204 - no common ancestor exists). + /// + /// This endpoint is useful for creating line-specific comments on pull requests. + /// The returned commit ID can be used as the fromHash parameter when creating anchored comments, + /// while the toHash can be obtained from on the pull request's FromRef. + /// + public async Task GetPullRequestMergeBaseAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge-base") + .AllowHttpStatus(204) + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task GetPullRequestMergeStateAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + // HTTP 204 indicates no common ancestor exists (e.g., unrelated histories) + if (response.StatusCode == 204) { - var queryParamValues = new Dictionary - { - ["version"] = version - }; - - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") - .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return null; } - /// - /// Gets the merge base (common ancestor) commit for a pull request. - /// This is the best common ancestor between the latest commits of the source and target branches. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Cancellation token. - /// The merge base commit, or null if not found (HTTP 204 - no common ancestor exists). - /// - /// This endpoint is useful for creating line-specific comments on pull requests. - /// The returned commit ID can be used as the fromHash parameter when creating anchored comments, - /// while the toHash can be obtained from on the pull request's FromRef. - /// - public async Task GetPullRequestMergeBaseAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge-base") - .AllowHttpStatus(204) - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await response.GetJsonAsync().ConfigureAwait(false); + } - // HTTP 204 indicates no common ancestor exists (e.g., unrelated histories) - if (response.StatusCode == 204) - { - return null; - } + public async Task MergePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["version"] = version, + }; - return await response.GetJsonAsync().ConfigureAwait(false); - } + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") + .SetQueryParams(queryParamValues) + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task MergePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + public async Task ReopenPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["version"] = version - }; + ["version"] = version, + }; - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") - .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/reopen") + .SetQueryParams(queryParamValues) + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task ReopenPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["version"] = version - }; + public async Task ApprovePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/reopen") - .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task DeletePullRequestApprovalAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task ApprovePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task> GetPullRequestChangesAsync(string projectKey, string repositorySlug, long pullRequestId, + ChangeScopes changeScope = ChangeScopes.All, + string? sinceId = null, + string? untilId = null, + bool withComments = true, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["changeScope"] = BitbucketHelpers.ChangeScopeToString(changeScope), + ["sinceId"] = sinceId, + ["untilId"] = untilId, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/changes") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task DeletePullRequestApprovalAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + public async Task CreatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, + string text, + string? parentId = null, + DiffTypes? diffType = null, + string? fromHash = null, + string? path = null, + string? srcPath = null, + string? toHash = null, + int? line = null, + FileTypes? fileType = null, + LineTypes? lineType = null, + CancellationToken cancellationToken = default) + { + // Build the comment payload dynamically to avoid sending empty anchor objects + // which Bitbucket Server 9.0 rejects with HTTP 500. + // See: BUG-003 - add_pull_request_comment returns 500 error + var data = new Dictionary +(StringComparer.Ordinal) { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["text"] = text, + }; - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + if (!string.IsNullOrEmpty(parentId)) + { + data["parent"] = new { id = parentId }; } - public async Task> GetPullRequestChangesAsync(string projectKey, string repositorySlug, long pullRequestId, - ChangeScopes changeScope = ChangeScopes.All, - string? sinceId = null, - string? untilId = null, - bool withComments = true, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary + // Only include anchor if at least one anchor-related field is specified + // Empty anchor objects cause HTTP 500 on Bitbucket Server 9.0 + var hasAnchorData = diffType.HasValue + || !string.IsNullOrEmpty(fromHash) + || !string.IsNullOrEmpty(path) + || !string.IsNullOrEmpty(srcPath) + || !string.IsNullOrEmpty(toHash) + || line.HasValue + || fileType.HasValue + || lineType.HasValue; + + if (hasAnchorData) + { + data["anchor"] = new { - ["limit"] = limit, - ["start"] = start, - ["changeScope"] = BitbucketHelpers.ChangeScopeToString(changeScope), - ["sinceId"] = sinceId, - ["untilId"] = untilId, - ["withComments"] = BitbucketHelpers.BoolToString(withComments) + diffType = BitbucketHelpers.DiffTypeToString(diffType), + fromHash, + path, + srcPath, + toHash, + line, + fileType = BitbucketHelpers.FileTypeToString(fileType), + lineType = BitbucketHelpers.LineTypeToString(lineType), }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/changes") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); } - public async Task CreatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, - string text, - string? parentId = null, - DiffTypes? diffType = null, - string? fromHash = null, - string? path = null, - string? srcPath = null, - string? toHash = null, - int? line = null, - FileTypes? fileType = null, - LineTypes? lineType = null, - CancellationToken cancellationToken = default) - { - // Build the comment payload dynamically to avoid sending empty anchor objects - // which Bitbucket Server 9.0 rejects with HTTP 500. - // See: BUG-003 - add_pull_request_comment returns 500 error - var data = new Dictionary - { - ["text"] = text - }; + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/comments") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - if (!string.IsNullOrEmpty(parentId)) - { - data["parent"] = new { id = parentId }; - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - // Only include anchor if at least one anchor-related field is specified - // Empty anchor objects cause HTTP 500 on Bitbucket Server 9.0 - var hasAnchorData = diffType.HasValue - || !string.IsNullOrEmpty(fromHash) - || !string.IsNullOrEmpty(path) - || !string.IsNullOrEmpty(srcPath) - || !string.IsNullOrEmpty(toHash) - || line.HasValue - || fileType.HasValue - || lineType.HasValue; - - if (hasAnchorData) - { - data["anchor"] = new - { - diffType = BitbucketHelpers.DiffTypeToString(diffType), - fromHash, - path, - srcPath, - toHash, - line, - fileType = BitbucketHelpers.FileTypeToString(fileType), - lineType = BitbucketHelpers.LineTypeToString(lineType) - }; - } + public async Task> GetPullRequestCommentsAsync(string projectKey, string repositorySlug, long pullRequestId, + string path, + AnchorStates anchorState = AnchorStates.Active, + DiffTypes diffType = DiffTypes.Effective, + string? fromHash = null, + string? toHash = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + ["path"] = path, + ["anchorState"] = BitbucketHelpers.AnchorStateToString(anchorState), + ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), + ["fromHash"] = fromHash, + ["toHash"] = toHash, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/comments") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/comments") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetPullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") + .SetQueryParam("avatarSize", avatarSize) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task UpdatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, + int version, string text, CancellationToken cancellationToken = default) + { + var data = new + { + version, + text, + }; - public async Task> GetPullRequestCommentsAsync(string projectKey, string repositorySlug, long pullRequestId, - string path, - AnchorStates anchorState = AnchorStates.Active, - DiffTypes diffType = DiffTypes.Effective, - string? fromHash = null, - string? toHash = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - ["path"] = path, - ["anchorState"] = BitbucketHelpers.AnchorStateToString(anchorState), - ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), - ["fromHash"] = fromHash, - ["toHash"] = toHash - }; + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") + .SetQueryParam("version", version) + .PutJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/comments") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + public async Task DeletePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, + int version = -1, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") + .SetQueryParam("version", version) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task GetPullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, - int? avatarSize = null, - CancellationToken cancellationToken = default) + public async Task> GetPullRequestCommitsAsync(string projectKey, string repositorySlug, long pullRequestId, + bool withCounts = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - return await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") - .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + ["limit"] = limit, + ["start"] = start, + ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, - int version, string text, CancellationToken cancellationToken = default) + /// + /// Streams all commits for a pull request as an IAsyncEnumerable. + /// + public IAsyncEnumerable GetPullRequestCommitsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + bool withCounts = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var data = new - { - version, - text - }; + ["limit"] = limit, + ["start"] = start, + ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), + }; - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") - .SetQueryParam("version", version) - .PutJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task GetPullRequestDiffAsync(string projectKey, string repositorySlug, long pullRequestId, + int contextLines = -1, + DiffTypes diffType = DiffTypes.Effective, + string? sinceId = null, + string? srcPath = null, + string? untilId = null, + string whitespace = "ignore-all", + bool withComments = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - public async Task DeletePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, - int version = -1, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") - .SetQueryParam("version", version) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") + .SetQueryParams(queryParamValues) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async IAsyncEnumerable GetPullRequestDiffStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + int contextLines = -1, + DiffTypes diffType = DiffTypes.Effective, + string? sinceId = null, + string? srcPath = null, + string? untilId = null, + string whitespace = "ignore-all", + bool withComments = true, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); + var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") + .SetQueryParams(queryParamValues) + .GetStreamAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task> GetPullRequestCommitsAsync(string projectKey, string repositorySlug, long pullRequestId, - bool withCounts = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + try { - var queryParamValues = new Dictionary + await foreach (var diff in DeserializePullRequestDiffsAsync(responseStream, cancellationToken).ConfigureAwait(false)) { - ["limit"] = limit, - ["start"] = start, - ["withCounts"] = BitbucketHelpers.BoolToString(withCounts) - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); + yield return diff; + } } - - /// - /// Streams all commits for a pull request as an IAsyncEnumerable. - /// - public IAsyncEnumerable GetPullRequestCommitsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, - bool withCounts = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["withCounts"] = BitbucketHelpers.BoolToString(withCounts) - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); + finally + { + await responseStream.DisposeAsync(); } + } - public async Task GetPullRequestDiffAsync(string projectKey, string repositorySlug, long pullRequestId, - int contextLines = -1, - DiffTypes diffType = DiffTypes.Effective, - string? sinceId = null, - string? srcPath = null, - string? untilId = null, - string whitespace = "ignore-all", - bool withComments = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - - return await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") - .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetPullRequestDiffPathAsync(string projectKey, string repositorySlug, long pullRequestId, + string path, + int contextLines = -1, + DiffTypes diffType = DiffTypes.Effective, + string? sinceId = null, + string? srcPath = null, + string? untilId = null, + string whitespace = "ignore-all", + bool withComments = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - public async IAsyncEnumerable GetPullRequestDiffStreamAsync(string projectKey, string repositorySlug, long pullRequestId, - int contextLines = -1, - DiffTypes diffType = DiffTypes.Effective, - string? sinceId = null, - string? srcPath = null, - string? untilId = null, - string whitespace = "ignore-all", - bool withComments = true, - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") - .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/diff/{path}") + .SetQueryParams(queryParamValues) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - try - { - await foreach (var diff in DeserializePullRequestDiffsAsync(responseStream, cancellationToken).ConfigureAwait(false)) - { - yield return diff; - } - } - finally - { - responseStream.Dispose(); - } - } + private static Dictionary CreatePullRequestDiffQueryParams(int contextLines, DiffTypes diffType, string? sinceId, + string? srcPath, string? untilId, string whitespace, bool withComments) + { + return new Dictionary +(StringComparer.Ordinal) + { + ["contextLines"] = contextLines, + ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), + ["sinceId"] = sinceId, + ["srcPath"] = srcPath, + ["untilId"] = untilId, + ["whitespace"] = whitespace, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + } - public async Task GetPullRequestDiffPathAsync(string projectKey, string repositorySlug, long pullRequestId, - string path, - int contextLines = -1, - DiffTypes diffType = DiffTypes.Effective, - string? sinceId = null, - string? srcPath = null, - string? untilId = null, - string whitespace = "ignore-all", - bool withComments = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - - return await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/diff/{path}") - .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + private static async IAsyncEnumerable DeserializePullRequestDiffsAsync(Stream responseStream, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) + { + yield return diff; } + } - private static Dictionary CreatePullRequestDiffQueryParams(int contextLines, DiffTypes diffType, string? sinceId, - string? srcPath, string? untilId, string whitespace, bool withComments) + /// + /// Deserializes diff entries from a JSON stream containing a "diffs" array. + /// Used by all diff streaming methods (commit, repository, compare, pull request). + /// Uses zero-copy deserialization directly from JsonElement to avoid intermediate string allocations. + /// + private static async IAsyncEnumerable DeserializeDiffsFromStreamAsync(Stream responseStream, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + using var doc = await JsonDocument.ParseAsync(responseStream, cancellationToken: cancellationToken).ConfigureAwait(false); + + if (!doc.RootElement.TryGetProperty("diffs", out var diffsArray) || diffsArray.ValueKind != JsonValueKind.Array) { - return new Dictionary - { - ["contextLines"] = contextLines, - ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), - ["sinceId"] = sinceId, - ["srcPath"] = srcPath, - ["untilId"] = untilId, - ["whitespace"] = whitespace, - ["withComments"] = BitbucketHelpers.BoolToString(withComments) - }; + yield break; } - private static async IAsyncEnumerable DeserializePullRequestDiffsAsync(Stream responseStream, - [EnumeratorCancellation] CancellationToken cancellationToken) + foreach (var diffElement in diffsArray.EnumerateArray()) { - await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) + cancellationToken.ThrowIfCancellationRequested(); + + // Zero-copy: Deserialize directly from JsonElement instead of GetRawText() string allocation + var diff = diffElement.Deserialize(s_jsonOptions); + if (diff is not null) { yield return diff; } } + } - /// - /// Deserializes diff entries from a JSON stream containing a "diffs" array. - /// Used by all diff streaming methods (commit, repository, compare, pull request). - /// Uses zero-copy deserialization directly from JsonElement to avoid intermediate string allocations. - /// - private static async IAsyncEnumerable DeserializeDiffsFromStreamAsync(Stream responseStream, - [EnumeratorCancellation] CancellationToken cancellationToken) - { - using var doc = await JsonDocument.ParseAsync(responseStream, cancellationToken: cancellationToken).ConfigureAwait(false); - - if (!doc.RootElement.TryGetProperty("diffs", out var diffsArray) || diffsArray.ValueKind != JsonValueKind.Array) - { - yield break; - } + // Note: MoveToDiffArrayAsync is no longer needed with System.Text.Json approach - foreach (var diffElement in diffsArray.EnumerateArray()) - { - cancellationToken.ThrowIfCancellationRequested(); - - // Zero-copy: Deserialize directly from JsonElement instead of GetRawText() string allocation - var diff = diffElement.Deserialize(s_jsonOptions); - if (diff is not null) - { - yield return diff; - } - } - } + public async Task> GetPullRequestParticipantsAsync(string projectKey, string repositorySlug, long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; - // Note: MoveToDiffArrayAsync is no longer needed with System.Text.Json approach + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetPullRequestParticipantsAsync(string projectKey, string repositorySlug, long pullRequestId, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) + public async Task AssignUserRoleToPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, + Named named, + Roles role, + CancellationToken cancellationToken = default) + { + var data = new { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize - }; + user = named, + role = BitbucketHelpers.RoleToString(role), + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task AssignUserRoleToPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, - Named named, - Roles role, - CancellationToken cancellationToken = default) - { - var data = new - { - user = named, - role = BitbucketHelpers.RoleToString(role) - }; + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeletePullRequestParticipantAsync(string projectKey, string repositorySlug, long pullRequestId, string userName, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") + .SetQueryParam("username", userName) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task DeletePullRequestParticipantAsync(string projectKey, string repositorySlug, long pullRequestId, string userName, CancellationToken cancellationToken = default) + public async Task UpdatePullRequestParticipantStatus(string projectKey, string repositorySlug, long pullRequestId, + string userSlug, + Named named, + bool approved, + ParticipantStatus participantStatus, + CancellationToken cancellationToken = default) + { + var data = new { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") - .SetQueryParam("username", userName) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + user = named, + approved = BitbucketHelpers.BoolToString(approved), + status = BitbucketHelpers.ParticipantStatusToString(participantStatus), + }; - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants/{userSlug}") + .PutJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task UpdatePullRequestParticipantStatus(string projectKey, string repositorySlug, long pullRequestId, - string userSlug, - Named named, - bool approved, - ParticipantStatus participantStatus, - CancellationToken cancellationToken = default) - { - var data = new - { - user = named, - approved = BitbucketHelpers.BoolToString(approved), - status = BitbucketHelpers.ParticipantStatusToString(participantStatus) - }; + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants/{userSlug}") - .PutJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UnassignUserFromPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, string userSlug, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants/{userSlug}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task UnassignUserFromPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, string userSlug, CancellationToken cancellationToken = default) + /// + /// Gets tasks for a pull request using the legacy tasks endpoint. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Maximum number of pages to retrieve. + /// Maximum number of results per page. + /// Pagination start index. + /// Avatar size for user avatars. + /// Cancellation token. + /// Collection of tasks. + /// + /// + /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and returns 404 Not Found on servers version 9.0+. + /// + /// + /// For Bitbucket Server 9.0+, use instead. + /// For cross-version compatibility, use . + /// + /// + [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsAsync for 9.0+ or GetPullRequestTasksWithFallbackAsync for cross-version compatibility.")] + public async Task> GetPullRequestTasksAsync(string projectKey, string repositorySlug, long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants/{userSlug}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/tasks") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - /// - /// Gets tasks for a pull request using the legacy tasks endpoint. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Maximum number of pages to retrieve. - /// Maximum number of results per page. - /// Pagination start index. - /// Avatar size for user avatars. - /// Cancellation token. - /// Collection of tasks. - /// - /// - /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and returns 404 Not Found on servers version 9.0+. - /// - /// - /// For Bitbucket Server 9.0+, use instead. - /// For cross-version compatibility, use . - /// - /// - [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsAsync for 9.0+ or GetPullRequestTasksWithFallbackAsync for cross-version compatibility.")] - public async Task> GetPullRequestTasksAsync(string projectKey, string repositorySlug, long pullRequestId, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize - }; + /// + /// Gets the task count for a pull request using the legacy tasks endpoint. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The task count. + /// + /// + /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and may return 404 Not Found on servers version 9.0+. + /// + /// + /// For Bitbucket Server 9.0+, use and count the results. + /// + /// + [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsAsync and count the results for 9.0+ compatibility.")] + public async Task GetPullRequestTaskCountAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/tasks/count") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/tasks") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + #region Blocker Comments (Bitbucket Server 9.0+) + + /// + /// Gets blocker comments (tasks) for a pull request. + /// This endpoint is available in Bitbucket Server 9.0+ and replaces the legacy tasks endpoint. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional filter: , , or null for all. + /// Maximum number of pages to retrieve. + /// Maximum number of results per page. + /// Pagination start index. + /// Cancellation token. + /// Collection of blocker comments. + /// + /// + /// In Bitbucket Server 9.0+, tasks have been replaced by blocker comments. + /// A blocker comment is a comment with severity: 'BLOCKER' that must be resolved before the pull request can be merged. + /// + /// + /// For servers prior to 9.0, use instead. + /// + /// + public async Task> GetPullRequestBlockerCommentsAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + BlockerCommentState? state = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["state"] = BitbucketHelpers.BlockerCommentStateToString(state), + }; - /// - /// Gets the task count for a pull request using the legacy tasks endpoint. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Cancellation token. - /// The task count. - /// - /// - /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and may return 404 Not Found on servers version 9.0+. - /// - /// - /// For Bitbucket Server 9.0+, use and count the results. - /// - /// - [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsAsync and count the results for 9.0+ compatibility.")] - public async Task GetPullRequestTaskCountAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/tasks/count") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/blocker-comments") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - #region Blocker Comments (Bitbucket Server 9.0+) - - /// - /// Gets blocker comments (tasks) for a pull request. - /// This endpoint is available in Bitbucket Server 9.0+ and replaces the legacy tasks endpoint. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional filter: , , or null for all. - /// Maximum number of pages to retrieve. - /// Maximum number of results per page. - /// Pagination start index. - /// Cancellation token. - /// Collection of blocker comments. - /// - /// - /// In Bitbucket Server 9.0+, tasks have been replaced by blocker comments. - /// A blocker comment is a comment with severity: 'BLOCKER' that must be resolved before the pull request can be merged. - /// - /// - /// For servers prior to 9.0, use instead. - /// - /// - public async Task> GetPullRequestBlockerCommentsAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - BlockerCommentState? state = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["state"] = BitbucketHelpers.BlockerCommentStateToString(state) - }; + /// + /// Gets a single blocker comment by ID. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// Cancellation token. + /// The blocker comment. + public async Task GetPullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/blocker-comments") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + /// + /// Creates a blocker comment (task) on a pull request. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment text. + /// Optional anchor for file/line-specific blockers. + /// Cancellation token. + /// The created blocker comment. + public async Task CreatePullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + string text, + CommentAnchor? anchor = null, + CancellationToken cancellationToken = default) + { + var data = new + { + text, + severity = "BLOCKER", + anchor, + }; - /// - /// Gets a single blocker comment by ID. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// Cancellation token. - /// The blocker comment. - public async Task GetPullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - /// - /// Creates a blocker comment (task) on a pull request. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment text. - /// Optional anchor for file/line-specific blockers. - /// Cancellation token. - /// The created blocker comment. - public async Task CreatePullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - string text, - CommentAnchor? anchor = null, - CancellationToken cancellationToken = default) - { - var data = new - { - text, - severity = "BLOCKER", - anchor - }; + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + /// + /// Updates a blocker comment's text. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// The updated blocker comment text. + /// The version of the blocker comment (for optimistic locking). + /// Cancellation token. + /// The updated blocker comment. + public async Task UpdatePullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + string text, + int version, + CancellationToken cancellationToken = default) + { + var data = new + { + text, + version, + }; - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") + .PutJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - /// - /// Updates a blocker comment's text. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// The updated blocker comment text. - /// The version of the blocker comment (for optimistic locking). - /// Cancellation token. - /// The updated blocker comment. - public async Task UpdatePullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - string text, - int version, - CancellationToken cancellationToken = default) - { - var data = new - { - text, - version - }; + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") - .PutJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + /// + /// Deletes a blocker comment. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// The version of the blocker comment (for optimistic locking). + /// Cancellation token. + /// True if the blocker comment was deleted successfully. + public async Task DeletePullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + int version, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") + .SetQueryParam("version", version) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - /// - /// Deletes a blocker comment. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// The version of the blocker comment (for optimistic locking). - /// Cancellation token. - /// True if the blocker comment was deleted successfully. - public async Task DeletePullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - int version, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") - .SetQueryParam("version", version) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + /// + /// Resolves a blocker comment (marks the task as complete). + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// The version of the blocker comment (for optimistic locking). + /// Cancellation token. + /// The resolved blocker comment. + public async Task ResolvePullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + int version, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}/resolve") + .SetQueryParam("version", version) + .PutAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - /// - /// Resolves a blocker comment (marks the task as complete). - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// The version of the blocker comment (for optimistic locking). - /// Cancellation token. - /// The resolved blocker comment. - public async Task ResolvePullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - int version, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}/resolve") - .SetQueryParam("version", version) - .PutAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + /// + /// Reopens a resolved blocker comment. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// The version of the blocker comment (for optimistic locking). + /// Cancellation token. + /// The reopened blocker comment. + public async Task ReopenPullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + int version, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}/reopen") + .SetQueryParam("version", version) + .PutAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - /// - /// Reopens a resolved blocker comment. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// The version of the blocker comment (for optimistic locking). - /// Cancellation token. - /// The reopened blocker comment. - public async Task ReopenPullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - int version, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}/reopen") - .SetQueryParam("version", version) - .PutAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + /// + /// Gets pull request tasks with automatic fallback for cross-version compatibility. + /// + /// + /// + /// This method provides backward compatibility across Bitbucket Server versions: + /// + /// + /// Bitbucket Server 9.0+: Uses the new /blocker-comments endpoint. + /// Bitbucket Server < 9.0: Falls back to the legacy /tasks endpoint. + /// + /// + /// The method first tries the new blocker-comments endpoint. If it returns 404 (Not Found), + /// it automatically falls back to the legacy tasks endpoint. + /// + /// + /// For new code targeting Bitbucket Server 9.0+, prefer using + /// directly for better type safety. + /// + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Maximum number of pages to retrieve. + /// Maximum number of results per page. + /// Pagination start index. + /// Cancellation token. + /// + /// A collection of blocker comments () on Bitbucket 9.0+, + /// or legacy tasks () on older versions. + /// + public async Task> GetPullRequestTasksWithFallbackAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + try + { + // Try new blocker-comments endpoint first (Bitbucket 9.0+) + var blockerComments = await GetPullRequestBlockerCommentsAsync( + projectKey, repositorySlug, pullRequestId, + maxPages: maxPages, limit: limit, start: start, + cancellationToken: cancellationToken).ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + return blockerComments.Cast(); } - - /// - /// Gets pull request tasks with automatic fallback for cross-version compatibility. - /// - /// - /// - /// This method provides backward compatibility across Bitbucket Server versions: - /// - /// - /// Bitbucket Server 9.0+: Uses the new /blocker-comments endpoint. - /// Bitbucket Server < 9.0: Falls back to the legacy /tasks endpoint. - /// - /// - /// The method first tries the new blocker-comments endpoint. If it returns 404 (Not Found), - /// it automatically falls back to the legacy tasks endpoint. - /// - /// - /// For new code targeting Bitbucket Server 9.0+, prefer using - /// directly for better type safety. - /// - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Maximum number of pages to retrieve. - /// Maximum number of results per page. - /// Pagination start index. - /// Cancellation token. - /// - /// A collection of blocker comments () on Bitbucket 9.0+, - /// or legacy tasks () on older versions. - /// - public async Task> GetPullRequestTasksWithFallbackAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - try - { - // Try new blocker-comments endpoint first (Bitbucket 9.0+) - var blockerComments = await GetPullRequestBlockerCommentsAsync( - projectKey, repositorySlug, pullRequestId, - maxPages: maxPages, limit: limit, start: start, - cancellationToken: cancellationToken).ConfigureAwait(false); - - return blockerComments.Cast(); - } - catch (BitbucketNotFoundException) - { - // Fall back to legacy tasks endpoint (Bitbucket < 9.0) + catch (BitbucketNotFoundException) + { + // Fall back to legacy tasks endpoint (Bitbucket < 9.0) #pragma warning disable CS0618 // Type or member is obsolete - intentional fallback - var tasks = await GetPullRequestTasksAsync( - projectKey, repositorySlug, pullRequestId, - maxPages: maxPages, limit: limit, start: start, - cancellationToken: cancellationToken).ConfigureAwait(false); + var tasks = await GetPullRequestTasksAsync( + projectKey, repositorySlug, pullRequestId, + maxPages: maxPages, limit: limit, start: start, + cancellationToken: cancellationToken).ConfigureAwait(false); #pragma warning restore CS0618 - return tasks.Cast(); - } + return tasks.Cast(); } + } - #endregion - - public async Task WatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/watch") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + #endregion - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task WatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/watch") + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task UnwatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/watch") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task UnwatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/watch") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task RetrieveRawContentAsync(string projectKey, string repositorySlug, string path, - string? at = null, - bool markup = false, - bool hardWrap = true, - bool htmlEscape = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["at"] = at, - ["markup"] = BitbucketHelpers.BoolToString(markup), - ["hardWrap"] = BitbucketHelpers.BoolToString(hardWrap), - ["htmlEscape"] = BitbucketHelpers.BoolToString(htmlEscape) - }; + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - return await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/raw/{path}") - .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task RetrieveRawContentAsync(string projectKey, string repositorySlug, string path, + string? at = null, + bool markup = false, + bool hardWrap = true, + bool htmlEscape = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["at"] = at, + ["markup"] = BitbucketHelpers.BoolToString(markup), + ["hardWrap"] = BitbucketHelpers.BoolToString(hardWrap), + ["htmlEscape"] = BitbucketHelpers.BoolToString(htmlEscape), + }; + + return await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/raw/{path}") + .SetQueryParams(queryParamValues) + .GetStreamAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task GetProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdateProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, - PullRequestSettings pullRequestSettings, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") - .PostJsonAsync(pullRequestSettings, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, + PullRequestSettings pullRequestSettings, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") + .PostJsonAsync(pullRequestSettings, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task> GetProjectRepositoryHooksSettingsAsync(string projectKey, string repositorySlug, - HookTypes? hookType = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetProjectRepositoryHooksSettingsAsync(string projectKey, string repositorySlug, + HookTypes? hookType = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["type"] = hookType - }; + ["limit"] = limit, + ["start"] = start, + ["type"] = hookType, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/hooks") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/hooks") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task GetProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task DeleteProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task EnableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, object? hookSettings = null, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") - .PutJsonAsync(hookSettings, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task EnableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, object? hookSettings = null, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") + .PutJsonAsync(hookSettings, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DisableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DisableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task> GetProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") - .GetJsonAsync>(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task> GetProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") + .GetJsonAsync>(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task> UpdateProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, - Dictionary allSettings, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") - .PutJsonAsync(allSettings, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> UpdateProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, + Dictionary allSettings, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") + .PutJsonAsync(allSettings, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, CancellationToken cancellationToken = default) - { - return await GetProjectUrl(projectKey) - .AppendPathSegment($"/settings/pull-requests/{scmId}") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, CancellationToken cancellationToken = default) + { + return await GetProjectUrl(projectKey) + .AppendPathSegment($"/settings/pull-requests/{scmId}") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdateProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, MergeStrategies mergeStrategies, CancellationToken cancellationToken = default) - { - var response = await GetProjectUrl(projectKey) - .AppendPathSegment($"/settings/pull-requests/{scmId}") - .PostJsonAsync(mergeStrategies, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, MergeStrategies mergeStrategies, CancellationToken cancellationToken = default) + { + var response = await GetProjectUrl(projectKey) + .AppendPathSegment($"/settings/pull-requests/{scmId}") + .PostJsonAsync(mergeStrategies, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task> GetProjectRepositoryTagsAsync(string projectKey, string repositorySlug, - string filterText, - BranchOrderBy orderBy, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetProjectRepositoryTagsAsync(string projectKey, string repositorySlug, + string filterText, + BranchOrderBy orderBy, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filterText"] = filterText, - ["orderBy"] = BitbucketHelpers.BranchOrderByToString(orderBy) - }; + ["limit"] = limit, + ["start"] = start, + ["filterText"] = filterText, + ["orderBy"] = BitbucketHelpers.BranchOrderByToString(orderBy), + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task CreateProjectRepositoryTagAsync(string projectKey, string repositorySlug, - string name, - string startPoint, - string message, - CancellationToken cancellationToken = default) + public async Task CreateProjectRepositoryTagAsync(string projectKey, string repositorySlug, + string name, + string startPoint, + string message, + CancellationToken cancellationToken = default) + { + var data = new { - var data = new - { - name, - startPoint, - message - }; + name, + startPoint, + message, + }; - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetProjectRepositoryTagAsync(string projectKey, string repositorySlug, string tagName, CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/tags/{tagName}") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetProjectRepositoryTagAsync(string projectKey, string repositorySlug, string tagName, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/tags/{tagName}") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetProjectRepositoryWebHooksAsync(string projectKey, string repositorySlug, - string? @event = null, - bool statistics = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetProjectRepositoryWebHooksAsync(string projectKey, string repositorySlug, + string? @event = null, + bool statistics = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["event"] = @event, - ["statistics"] = BitbucketHelpers.BoolToString(statistics) - }; + ["limit"] = limit, + ["start"] = start, + ["event"] = @event, + ["statistics"] = BitbucketHelpers.BoolToString(statistics), + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task CreateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, WebHook webHook, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") - .PostJsonAsync(webHook, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task CreateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, WebHook webHook, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") + .PostJsonAsync(webHook, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task TestProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, string url, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks/test") - .SetQueryParam("url", url) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task TestProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, string url, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks/test") + .SetQueryParam("url", url) + .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, - string webHookId, - bool statistics = false, - CancellationToken cancellationToken = default) + public async Task GetProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, + string webHookId, + bool statistics = false, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["statistics"] = BitbucketHelpers.BoolToString(statistics) - }; - - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") - .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + ["statistics"] = BitbucketHelpers.BoolToString(statistics), + }; - public async Task UpdateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, - string webHookId, WebHook webHook, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") - .PutJsonAsync(webHook, cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") + .SetQueryParams(queryParamValues) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task UpdateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, + string webHookId, WebHook webHook, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") + .PutJsonAsync(webHook, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task DeleteProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, - string webHookId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task DeleteProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, + string webHookId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - //public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, - public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, - string webHookId, - string? @event = null, - WebHookOutcomes? outcome = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["event"] = @event, - ["outcome"] = BitbucketHelpers.WebHookOutcomeToString(outcome) - }; + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/latest") - .SetQueryParams(queryParamValues) - //.GetJsonAsync() - .GetStringAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + //public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, + public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, + string webHookId, + string? @event = null, + WebHookOutcomes? outcome = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["event"] = @event, + ["outcome"] = BitbucketHelpers.WebHookOutcomeToString(outcome), + }; + + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/latest") + .SetQueryParams(queryParamValues) + //.GetJsonAsync() + .GetStringAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task GetProjectRepositoryWebHookStatisticsAsync(string projectKey, string repositorySlug, - string webHookId, - string? @event = null, - CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics") - .SetQueryParam("event", @event) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetProjectRepositoryWebHookStatisticsAsync(string projectKey, string repositorySlug, + string webHookId, + string? @event = null, + CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics") + .SetQueryParam("event", @event) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task> GetProjectRepositoryWebHookStatisticsSummaryAsync(string projectKey, string repositorySlug, - string webHookId, CancellationToken cancellationToken = default) - { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics/summary") - .GetJsonAsync>(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task> GetProjectRepositoryWebHookStatisticsSummaryAsync(string projectKey, string repositorySlug, + string webHookId, CancellationToken cancellationToken = default) + { + return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics/summary") + .GetJsonAsync>(cancellationToken: cancellationToken) + .ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs index 308eb6d..95efdba 100644 --- a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs @@ -1,75 +1,76 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Projects; using Flurl.Http; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetReposUrl() => GetBaseUrl() - .AppendPathSegment("/repos"); + private IFlurlRequest GetReposUrl() => GetBaseUrl() + .AppendPathSegment("/repos"); - public async Task> GetRepositoriesAsync( - int? maxPages = null, - int? limit = null, - int? start = null, - string? name = null, - string? projectName = null, - Permissions? permission = null, - bool isPublic = false, - CancellationToken cancellationToken = default) + public async Task> GetRepositoriesAsync( + int? maxPages = null, + int? limit = null, + int? start = null, + string? name = null, + string? projectName = null, + Permissions? permission = null, + bool isPublic = false, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["name"] = name, - ["projectname"] = projectName, - ["permission"] = BitbucketHelpers.PermissionToString(permission), - ["visibility"] = isPublic ? "public" : "private" - }; + ["limit"] = limit, + ["start"] = start, + ["name"] = name, + ["projectname"] = projectName, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + ["visibility"] = isPublic ? "public" : "private", + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetReposUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetReposUrl() + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - /// - /// Streams all repositories as an IAsyncEnumerable, yielding items as they are retrieved. - /// - public IAsyncEnumerable GetRepositoriesStreamAsync( - int? maxPages = null, - int? limit = null, - int? start = null, - string? name = null, - string? projectName = null, - Permissions? permission = null, - bool isPublic = false, - CancellationToken cancellationToken = default) + /// + /// Streams all repositories as an IAsyncEnumerable, yielding items as they are retrieved. + /// + public IAsyncEnumerable GetRepositoriesStreamAsync( + int? maxPages = null, + int? limit = null, + int? start = null, + string? name = null, + string? projectName = null, + Permissions? permission = null, + bool isPublic = false, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["name"] = name, - ["projectname"] = projectName, - ["permission"] = BitbucketHelpers.PermissionToString(permission), - ["visibility"] = isPublic ? "public" : "private" - }; + ["limit"] = limit, + ["start"] = start, + ["name"] = name, + ["projectname"] = projectName, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + ["visibility"] = isPublic ? "public" : "private", + }; - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetReposUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); - } + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetReposUrl() + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs index d49a0ef..42b6877 100644 --- a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs @@ -1,57 +1,56 @@ -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Tasks; using Flurl.Http; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetTasksUrl() => GetBaseUrl() - .AppendPathSegment("/tasks"); + private IFlurlRequest GetTasksUrl() => GetBaseUrl() + .AppendPathSegment("/tasks"); - private IFlurlRequest GetTasksUrl(string path) => GetTasksUrl() - .AppendPathSegment(path); + private IFlurlRequest GetTasksUrl(string path) => GetTasksUrl() + .AppendPathSegment(path); - public async Task CreateTaskAsync(TaskInfo taskInfo, CancellationToken cancellationToken = default) - { - var response = await GetTasksUrl() - .PostJsonAsync(taskInfo, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task CreateTaskAsync(TaskInfo taskInfo, CancellationToken cancellationToken = default) + { + var response = await GetTasksUrl() + .PostJsonAsync(taskInfo, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetTaskAsync(long taskId, int? avatarSize = null, CancellationToken cancellationToken = default) - { - return await GetTasksUrl($"/{taskId}") - .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetTaskAsync(long taskId, int? avatarSize = null, CancellationToken cancellationToken = default) + { + return await GetTasksUrl($"/{taskId}") + .SetQueryParam("avatarSize", avatarSize) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task UpdateTaskAsync(long taskId, string text, CancellationToken cancellationToken = default) + public async Task UpdateTaskAsync(long taskId, string text, CancellationToken cancellationToken = default) + { + var obj = new { - var obj = new - { - id = taskId, - text - }; + id = taskId, + text, + }; - var response = await GetTasksUrl($"/{taskId}") - .PutJsonAsync(obj, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetTasksUrl($"/{taskId}") + .PutJsonAsync(obj, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteTaskAsync(long taskId, CancellationToken cancellationToken = default) - { - var response = await GetTasksUrl($"/{taskId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteTaskAsync(long taskId, CancellationToken cancellationToken = default) + { + var response = await GetTasksUrl($"/{taskId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs index 329d51a..9cdd694 100644 --- a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs @@ -1,111 +1,111 @@ +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Users; +using Flurl.Http; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common.Models; -using Bitbucket.Net.Models.Core.Users; -using Flurl.Http; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient + private IFlurlRequest GetUsersUrl() => GetBaseUrl() + .AppendPathSegment("/users"); + + private IFlurlRequest GetUsersUrl(string path) => GetUsersUrl() + .AppendPathSegment(path); + + public async Task> GetUsersAsync(string? filter = null, string? group = null, string? permission = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default, + params string[] permissionN) { - private IFlurlRequest GetUsersUrl() => GetBaseUrl() - .AppendPathSegment("/users"); - - private IFlurlRequest GetUsersUrl(string path) => GetUsersUrl() - .AppendPathSegment(path); - - public async Task> GetUsersAsync(string? filter = null, string? group = null, string? permission = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default, - params string[] permissionN) + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + ["filter"] = filter, + ["group"] = group, + ["permission"] = permission, + }; + + int permissionNCounter = 0; + foreach (string perm in permissionN) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - ["filter"] = filter, - ["group"] = group, - ["permission"] = permission - }; - - int permissionNCounter = 0; - foreach (string perm in permissionN) - { - permissionNCounter++; - queryParamValues.Add($"permission.{permissionNCounter}", perm); - } - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetUsersUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); + permissionNCounter++; + queryParamValues.Add($"permission.{permissionNCounter}", perm); } - public async Task UpdateUserAsync(string? email = null, string? displayName = null, CancellationToken cancellationToken = default) + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetUsersUrl() + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task UpdateUserAsync(string? email = null, string? displayName = null, CancellationToken cancellationToken = default) + { + var obj = new { - var obj = new - { - displayName, - email - }; + displayName, + email, + }; - var response = await GetUsersUrl() - .PutJsonAsync(obj, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetUsersUrl() + .PutJsonAsync(obj, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task UpdateUserCredentialsAsync(PasswordChange passwordChange, CancellationToken cancellationToken = default) - { - var response = await GetUsersUrl("/credentials") - .PutJsonAsync(passwordChange, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateUserCredentialsAsync(PasswordChange passwordChange, CancellationToken cancellationToken = default) + { + var response = await GetUsersUrl("/credentials") + .PutJsonAsync(passwordChange, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task GetUserAsync(string userSlug, int? avatarSize = null, CancellationToken cancellationToken = default) - { - return await GetUsersUrl($"/{userSlug}") - .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetUserAsync(string userSlug, int? avatarSize = null, CancellationToken cancellationToken = default) + { + return await GetUsersUrl($"/{userSlug}") + .SetQueryParam("avatarSize", avatarSize) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task DeleteUserAvatarAsync(string userSlug, CancellationToken cancellationToken = default) - { - var response = await GetUsersUrl($"/{userSlug}/avatar.png") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteUserAvatarAsync(string userSlug, CancellationToken cancellationToken = default) + { + var response = await GetUsersUrl($"/{userSlug}/avatar.png") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task> GetUserSettingsAsync(string userSlug, CancellationToken cancellationToken = default) - { - var response = await GetUsersUrl($"/{userSlug}/settings") - .GetJsonAsync>(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetUserSettingsAsync(string userSlug, CancellationToken cancellationToken = default) + { + var response = await GetUsersUrl($"/{userSlug}/settings") + .GetJsonAsync>(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return response; - } + return response; + } - public async Task UpdateUserSettingsAsync(string userSlug, IDictionary userSettings, CancellationToken cancellationToken = default) - { - var response = await GetUsersUrl($"/{userSlug}/settings") - .PostJsonAsync(userSettings, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateUserSettingsAsync(string userSlug, IDictionary userSettings, CancellationToken cancellationToken = default) + { + var response = await GetUsersUrl($"/{userSlug}/settings") + .PostJsonAsync(userSettings, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs b/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs index b41196d..ee2a699 100644 --- a/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs @@ -1,72 +1,71 @@ -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Flurl; using Flurl.Http; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient + /// + /// Gets the username of the currently authenticated user. + /// Uses the /plugins/servlet/applinks/whoami endpoint which returns + /// just the username as plain text. + /// + /// Cancellation token. + /// The username of the authenticated user, or null if not authenticated. + /// + /// This endpoint is essential for MCP servers and other integrations that need to + /// identify the current user context. Unlike GetUsersAsync(), this returns the + /// authenticated user specifically, not a list of all users. + /// + /// Usage example: + /// + /// var client = new BitbucketClient(url, () => token); + /// + /// // Get the authenticated user's username + /// var username = await client.GetWhoAmIAsync(); + /// + /// // Then fetch full user details if needed + /// if (username != null) + /// { + /// var currentUser = await client.GetUserAsync(username); + /// } + /// + /// + public async Task GetWhoAmIAsync(CancellationToken cancellationToken = default) { - /// - /// Gets the username of the currently authenticated user. - /// Uses the /plugins/servlet/applinks/whoami endpoint which returns - /// just the username as plain text. - /// - /// Cancellation token. - /// The username of the authenticated user, or null if not authenticated. - /// - /// This endpoint is essential for MCP servers and other integrations that need to - /// identify the current user context. Unlike GetUsersAsync(), this returns the - /// authenticated user specifically, not a list of all users. - /// - /// Usage example: - /// - /// var client = new BitbucketClient(url, () => token); - /// - /// // Get the authenticated user's username - /// var username = await client.GetWhoAmIAsync(); - /// - /// // Then fetch full user details if needed - /// if (username != null) - /// { - /// var currentUser = await client.GetUserAsync(username); - /// } - /// - /// - public async Task GetWhoAmIAsync(CancellationToken cancellationToken = default) - { - string response; + string response; - // Handle DI constructor scenario (injected IFlurlClient or HttpClient) - if (_injectedClient != null) - { - var request = _injectedClient - .Request() - .AppendPathSegment("/plugins/servlet/applinks/whoami"); - - // Apply token authentication if provided - if (_getToken != null) - { - request = request.WithOAuthBearerToken(_getToken()); - } + // Handle DI constructor scenario (injected IFlurlClient or HttpClient) + if (_injectedClient != null) + { + var request = _injectedClient + .Request() + .AppendPathSegment("/plugins/servlet/applinks/whoami"); - response = await request - .GetStringAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } - else + // Apply token authentication if provided + if (_getToken != null) { - // Original behavior for non-DI scenarios - // Construct full URL and convert to IFlurlRequest for authentication - var fullUrl = new Url(_url).AppendPathSegment("/plugins/servlet/applinks/whoami"); - response = await new FlurlRequest(fullUrl) - .WithAuthentication(_getToken, _userName, _password) - .GetStringAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + request = request.WithOAuthBearerToken(_getToken()); } - return string.IsNullOrWhiteSpace(response) ? null : response.Trim(); + response = await request + .GetStringAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } + else + { + // Original behavior for non-DI scenarios + // Construct full URL and convert to IFlurlRequest for authentication + var fullUrl = new Url(_url).AppendPathSegment("/plugins/servlet/applinks/whoami"); + response = await new FlurlRequest(fullUrl) + .WithAuthentication(_getToken, _userName, _password) + .GetStringAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } + + return string.IsNullOrWhiteSpace(response) ? null : response.Trim(); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs index 28cbb77..d26fe11 100644 --- a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs +++ b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs @@ -1,118 +1,118 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Users; using Bitbucket.Net.Models.DefaultReviewers; using Flurl.Http; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Bitbucket.Net; -namespace Bitbucket.Net +public partial class BitbucketClient { - public partial class BitbucketClient + private IFlurlRequest GetDefaultReviewersUrl() => GetBaseUrl("/default-reviewers"); + + private IFlurlRequest GetDefaultReviewersUrl(string path) => GetDefaultReviewersUrl() + .AppendPathSegment(path); + + public async Task> GetDefaultReviewerConditionsAsync(string projectKey, + int? avatarSize = null, CancellationToken cancellationToken = default) { - private IFlurlRequest GetDefaultReviewersUrl() => GetBaseUrl("/default-reviewers"); + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - private IFlurlRequest GetDefaultReviewersUrl(string path) => GetDefaultReviewersUrl() - .AppendPathSegment(path); + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task> GetDefaultReviewerConditionsAsync(string projectKey, - int? avatarSize = null, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task CreateDefaultReviewerConditionAsync(string projectKey, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") + .PostJsonAsync(condition, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task CreateDefaultReviewerConditionAsync(string projectKey, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") - .PostJsonAsync(condition, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") + .PutJsonAsync(condition, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") - .PutJsonAsync(condition, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetDefaultReviewerConditionsAsync(string projectKey, string repositorySlug, + int? avatarSize = null, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task> GetDefaultReviewerConditionsAsync(string projectKey, string repositorySlug, - int? avatarSize = null, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task CreateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") + .PostJsonAsync(condition, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task CreateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") - .PostJsonAsync(condition, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") + .PutJsonAsync(condition, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") - .PutJsonAsync(condition, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - public async Task> GetDefaultReviewersAsync(string projectKey, string repositorySlug, - int? sourceRepoId = null, - int? targetRepoId = null, - string? sourceRefId = null, - string? targetRefId = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) + public async Task> GetDefaultReviewersAsync(string projectKey, string repositorySlug, + int? sourceRepoId = null, + int? targetRepoId = null, + string? sourceRefId = null, + string? targetRefId = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["sourceRepoId"] = sourceRepoId, - ["targetRepoId"] = targetRepoId, - ["sourceRefId"] = sourceRefId, - ["targetRefId"] = targetRefId, - ["avatarSize"] = avatarSize - }; - - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/reviewers") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + ["sourceRepoId"] = sourceRepoId, + ["targetRepoId"] = targetRepoId, + ["sourceRefId"] = sourceRefId, + ["targetRefId"] = targetRefId, + ["avatarSize"] = avatarSize, + }; + + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/reviewers") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Git/BitbucketClient.cs b/src/Bitbucket.Net/Git/BitbucketClient.cs index 0b62bb2..8b8ca0f 100644 --- a/src/Bitbucket.Net/Git/BitbucketClient.cs +++ b/src/Bitbucket.Net/Git/BitbucketClient.cs @@ -1,61 +1,60 @@ -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Git; using Flurl.Http; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetGitUrl() => GetBaseUrl("/git"); + private IFlurlRequest GetGitUrl() => GetBaseUrl("/git"); - private IFlurlRequest GetGitUrl(string path) => GetGitUrl() - .AppendPathSegment(path); + private IFlurlRequest GetGitUrl(string path) => GetGitUrl() + .AppendPathSegment(path); - public async Task GetCanRebasePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/rebase") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetCanRebasePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/rebase") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task RebasePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version, CancellationToken cancellationToken = default) - { - var data = new { version }; - var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/rebase") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task RebasePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version, CancellationToken cancellationToken = default) + { + var data = new { version }; + var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/rebase") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task CreateTagAsync(string projectKey, string repositorySlug, TagTypes tagType, string tagName, string startPoint, CancellationToken cancellationToken = default) + public async Task CreateTagAsync(string projectKey, string repositorySlug, TagTypes tagType, string tagName, string startPoint, CancellationToken cancellationToken = default) + { + var data = new { - var data = new - { - type = BitbucketHelpers.TagTypeToString(tagType), - name = tagName, - startPoint - }; + type = BitbucketHelpers.TagTypeToString(tagType), + name = tagName, + startPoint, + }; - var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/tags") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/tags") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteTagAsync(string projectKey, string repositorySlug, string tagName, CancellationToken cancellationToken = default) - { - var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/tags/{tagName}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteTagAsync(string projectKey, string repositorySlug, string tagName, CancellationToken cancellationToken = default) + { + var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/tags/{tagName}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Jira/BitbucketClient.cs b/src/Bitbucket.Net/Jira/BitbucketClient.cs index 7b03427..764431b 100644 --- a/src/Bitbucket.Net/Jira/BitbucketClient.cs +++ b/src/Bitbucket.Net/Jira/BitbucketClient.cs @@ -1,65 +1,65 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Builds; using Bitbucket.Net.Models.Jira; using Flurl.Http; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Bitbucket.Net; -namespace Bitbucket.Net +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetJiraUrl() => GetBaseUrl("/jira"); + private IFlurlRequest GetJiraUrl() => GetBaseUrl("/jira"); - private IFlurlRequest GetJiraUrl(string path) => GetJiraUrl() - .AppendPathSegment(path); + private IFlurlRequest GetJiraUrl(string path) => GetJiraUrl() + .AppendPathSegment(path); - public async Task> GetChangeSetsAsync(string issueKey, int maxChanges = 10, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task> GetChangeSetsAsync(string issueKey, int maxChanges = 10, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["maxChanges"] = maxChanges - }; + ["limit"] = limit, + ["start"] = start, + ["maxChanges"] = maxChanges, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetJiraUrl($"/issues/{issueKey}/commits") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetJiraUrl($"/issues/{issueKey}/commits") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task CreateJiraIssueAsync(string pullRequestCommentId, string applicationId, string title, string type, CancellationToken cancellationToken = default) + public async Task CreateJiraIssueAsync(string pullRequestCommentId, string applicationId, string title, string type, CancellationToken cancellationToken = default) + { + var data = new { - var data = new - { - id = "https://docs.atlassian.com/jira/REST/schema/string#", - title, - type - }; + id = "https://docs.atlassian.com/jira/REST/schema/string#", + title, + type, + }; - var response = await GetJiraUrl($"/comments/{pullRequestCommentId}/issues") - .SetQueryParam("applicationId", applicationId) - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetJiraUrl($"/comments/{pullRequestCommentId}/issues") + .SetQueryParam("applicationId", applicationId) + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task> GetJiraIssuesAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetJiraUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/issues") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> GetJiraIssuesAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetJiraUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/issues") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Audit/AuditEvent.cs b/src/Bitbucket.Net/Models/Audit/AuditEvent.cs index 02702e4..30c925c 100644 --- a/src/Bitbucket.Net/Models/Audit/AuditEvent.cs +++ b/src/Bitbucket.Net/Models/Audit/AuditEvent.cs @@ -1,12 +1,11 @@ using Bitbucket.Net.Models.Core.Users; -namespace Bitbucket.Net.Models.Audit +namespace Bitbucket.Net.Models.Audit; + +public class AuditEvent { - public class AuditEvent - { - public string Action { get; set; } - public long Timestamp { get; set; } - public string Details { get; set; } - public User User { get; set; } - } -} + public string? Action { get; set; } + public long Timestamp { get; set; } + public string? Details { get; set; } + public User? User { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Branches/BranchModel.cs b/src/Bitbucket.Net/Models/Branches/BranchModel.cs index ae3f5ab..17e8704 100644 --- a/src/Bitbucket.Net/Models/Branches/BranchModel.cs +++ b/src/Bitbucket.Net/Models/Branches/BranchModel.cs @@ -1,12 +1,11 @@ -using System.Collections.Generic; -using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Models.Core.Projects; +using System.Collections.Generic; -namespace Bitbucket.Net.Models.Branches +namespace Bitbucket.Net.Models.Branches; + +public class BranchModel { - public class BranchModel - { - public Branch Development { get; set; } - public Branch Production { get; set; } - public List Types { get; set; } - } -} + public Branch? Development { get; set; } + public Branch? Production { get; set; } + public List? Types { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Branches/BranchModelType.cs b/src/Bitbucket.Net/Models/Branches/BranchModelType.cs index a1c8b39..c894ab4 100644 --- a/src/Bitbucket.Net/Models/Branches/BranchModelType.cs +++ b/src/Bitbucket.Net/Models/Branches/BranchModelType.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Branches +namespace Bitbucket.Net.Models.Branches; + +public class BranchModelType { - public class BranchModelType - { - public string Id { get; set; } - public string DisplayName { get; set; } - public string Prefix { get; set; } - } -} + public string? Id { get; set; } + public string? DisplayName { get; set; } + public string? Prefix { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Builds/BuildStats.cs b/src/Bitbucket.Net/Models/Builds/BuildStats.cs index d802374..66439ae 100644 --- a/src/Bitbucket.Net/Models/Builds/BuildStats.cs +++ b/src/Bitbucket.Net/Models/Builds/BuildStats.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Builds +namespace Bitbucket.Net.Models.Builds; + +public class BuildStats { - public class BuildStats - { - public int Successful { get; set; } - public int InProgress { get; set; } - public int Failed { get; set; } - } -} + public int Successful { get; set; } + public int InProgress { get; set; } + public int Failed { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Builds/BuildStatus.cs b/src/Bitbucket.Net/Models/Builds/BuildStatus.cs index e4657b7..bdb3c3e 100644 --- a/src/Bitbucket.Net/Models/Builds/BuildStatus.cs +++ b/src/Bitbucket.Net/Models/Builds/BuildStatus.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Builds +namespace Bitbucket.Net.Models.Builds; + +public class BuildStatus : KeyedUrl { - public class BuildStatus : KeyedUrl - { - public string State { get; set; } - public string Name { get; set; } - public string Description { get; set; } - public long DateAdded { get; set; } - } -} + public string? State { get; set; } + public string? Name { get; set; } + public string? Description { get; set; } + public long DateAdded { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Builds/KeyedUrl.cs b/src/Bitbucket.Net/Models/Builds/KeyedUrl.cs index 26ad1e7..445268b 100644 --- a/src/Bitbucket.Net/Models/Builds/KeyedUrl.cs +++ b/src/Bitbucket.Net/Models/Builds/KeyedUrl.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Builds +namespace Bitbucket.Net.Models.Builds; + +public class KeyedUrl { - public class KeyedUrl - { - public string Key { get; set; } - public string Url { get; set; } - } + public string? Key { get; set; } + public string? Url { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/Address.cs b/src/Bitbucket.Net/Models/Core/Admin/Address.cs index a49833a..822cc0a 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/Address.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/Address.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class Address { - public class Address - { - public string HostName { get; set; } - public int Port { get; set; } - } + public string? HostName { get; set; } + public int Port { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/Cluster.cs b/src/Bitbucket.Net/Models/Core/Admin/Cluster.cs index 1a3d8dd..74fef8f 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/Cluster.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/Cluster.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class Cluster { - public class Cluster - { - public Node LocalNode { get; set; } - public List Nodes { get; set; } - public bool Running { get; set; } - } -} + public Node? LocalNode { get; set; } + public List? Nodes { get; set; } + public bool Running { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/DeletableGroupOrUser.cs b/src/Bitbucket.Net/Models/Core/Admin/DeletableGroupOrUser.cs index edcab79..c2b72f2 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/DeletableGroupOrUser.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/DeletableGroupOrUser.cs @@ -1,9 +1,8 @@ using Bitbucket.Net.Models.Core.Users; -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class DeletableGroupOrUser : Named { - public class DeletableGroupOrUser : Named - { - public bool Deletable { get; set; } - } -} + public bool Deletable { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/GroupPermission.cs b/src/Bitbucket.Net/Models/Core/Admin/GroupPermission.cs index f5e67a7..6b83bdd 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/GroupPermission.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/GroupPermission.cs @@ -2,14 +2,13 @@ using Bitbucket.Net.Models.Core.Users; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class GroupPermission { - public class GroupPermission - { - public Named Group { get; set; } - [JsonConverter(typeof(PermissionsConverter))] - public Permissions Permission { get; set; } + public Named? Group { get; set; } + [JsonConverter(typeof(PermissionsConverter))] + public Permissions Permission { get; set; } - public override string ToString() => $"{Permission} - {Group}"; - } -} + public override string ToString() => $"{Permission} - {Group}"; +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/GroupUsers.cs b/src/Bitbucket.Net/Models/Core/Admin/GroupUsers.cs index 8d91594..d114582 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/GroupUsers.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/GroupUsers.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class GroupUsers { - public class GroupUsers - { - public string Group { get; set; } - public List Users { get; set; } - } -} + public string? Group { get; set; } + public List? Users { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs b/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs index b307ddc..505c4cb 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs @@ -1,28 +1,27 @@ -using System; using Bitbucket.Net.Common.Converters; +using System; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class LicenseDetails : LicenseInfo { - public class LicenseDetails : LicenseInfo - { - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? CreationDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? PurchaseDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? ExpiryDate { get; set; } - public int NumberOfDaysBeforeExpiry { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? MaintenanceExpiryDate { get; set; } - public int NumberOfDaysBeforeMaintenanceExpiry { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? GracePeriodEndDate { get; set; } - public int NumberOfDaysBeforeGracePeriodExpiry { get; set; } - public int MaximumNumberOfUsers { get; set; } - public bool UnlimitedNumberOfUsers { get; set; } - public string ServerId { get; set; } - public string SupportEntitlementNumber { get; set; } - public LicenseStatus Status { get; set; } - } -} + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? CreationDate { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? PurchaseDate { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? ExpiryDate { get; set; } + public int NumberOfDaysBeforeExpiry { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? MaintenanceExpiryDate { get; set; } + public int NumberOfDaysBeforeMaintenanceExpiry { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? GracePeriodEndDate { get; set; } + public int NumberOfDaysBeforeGracePeriodExpiry { get; set; } + public int MaximumNumberOfUsers { get; set; } + public bool UnlimitedNumberOfUsers { get; set; } + public string? ServerId { get; set; } + public string? SupportEntitlementNumber { get; set; } + public LicenseStatus? Status { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/LicenseInfo.cs b/src/Bitbucket.Net/Models/Core/Admin/LicenseInfo.cs index 58f9842..3de91d4 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/LicenseInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/LicenseInfo.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class LicenseInfo { - public class LicenseInfo - { - public string License { get; set; } - } + public string? License { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/LicenseStatus.cs b/src/Bitbucket.Net/Models/Core/Admin/LicenseStatus.cs index 4a45e74..4fb5606 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/LicenseStatus.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/LicenseStatus.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class LicenseStatus { - public class LicenseStatus - { - public string ServerId { get; set; } - public int CurrentNumberOfUsers { get; set; } - } + public string? ServerId { get; set; } + public int CurrentNumberOfUsers { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/MailServerConfiguration.cs b/src/Bitbucket.Net/Models/Core/Admin/MailServerConfiguration.cs index d5ec6b2..faf5d65 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/MailServerConfiguration.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/MailServerConfiguration.cs @@ -1,14 +1,12 @@ -namespace Bitbucket.Net.Models.Core.Admin -{ - public class MailServerConfiguration - { - public string HostName { get; set; } - public int Port { get; set; } - public string Protocol { get; set; } - public bool UseStartTls { get; set; } - public bool RequireStartTls { get; set; } - public string UserName { get; set; } - public string SenderAddress { get; set; } - } +namespace Bitbucket.Net.Models.Core.Admin; -} +public class MailServerConfiguration +{ + public string? HostName { get; set; } + public int Port { get; set; } + public string? Protocol { get; set; } + public bool UseStartTls { get; set; } + public bool RequireStartTls { get; set; } + public string? UserName { get; set; } + public string? SenderAddress { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/MergeStrategies.cs b/src/Bitbucket.Net/Models/Core/Admin/MergeStrategies.cs index ca6fa6f..1659b9b 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/MergeStrategies.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/MergeStrategies.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class MergeStrategies { - public class MergeStrategies - { - public MergeStrategy DefaultStrategy { get; set; } + public MergeStrategy? DefaultStrategy { get; set; } - public List Strategies { get; set; } + public List? Strategies { get; set; } - public string Type { get; set; } - } -} + public string? Type { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/MergeStrategy.cs b/src/Bitbucket.Net/Models/Core/Admin/MergeStrategy.cs index ceb7cbe..1b33f34 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/MergeStrategy.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/MergeStrategy.cs @@ -1,11 +1,10 @@ -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class MergeStrategy { - public class MergeStrategy - { - public string Description { get; set; } - public bool Enabled { get; set; } - public string Flag { get; set; } - public string Id { get; set; } - public string Name { get; set; } - } + public string? Description { get; set; } + public bool Enabled { get; set; } + public string? Flag { get; set; } + public string? Id { get; set; } + public string? Name { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/Node.cs b/src/Bitbucket.Net/Models/Core/Admin/Node.cs index f0cc014..df4e021 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/Node.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/Node.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class Node { - public class Node - { - public string Id { get; set; } - public string Name { get; set; } - public Address Address { get; set; } - public bool Local { get; set; } - } + public string? Id { get; set; } + public string? Name { get; set; } + public Address? Address { get; set; } + public bool Local { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/PasswordBasic.cs b/src/Bitbucket.Net/Models/Core/Admin/PasswordBasic.cs index 6d7175c..d8880f1 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/PasswordBasic.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/PasswordBasic.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public abstract class PasswordBasic { - public abstract class PasswordBasic - { - public string Password { get; set; } - public string PasswordConfirm { get; set; } - } -} + public string? Password { get; set; } + public string? PasswordConfirm { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/PasswordChange.cs b/src/Bitbucket.Net/Models/Core/Admin/PasswordChange.cs index c8a3f05..d560ed0 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/PasswordChange.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/PasswordChange.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class PasswordChange : PasswordBasic { - public class PasswordChange : PasswordBasic - { - public string Name { get; set; } - } -} + public string? Name { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/Permissions.cs b/src/Bitbucket.Net/Models/Core/Admin/Permissions.cs index 23424c4..01cff67 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/Permissions.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/Permissions.cs @@ -1,17 +1,16 @@ -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public enum Permissions { - public enum Permissions - { - Admin, - LicensedUser, - ProjectAdmin, - ProjectCreate, - ProjectRead, - ProjectView, - ProjectWrite, - RepoAdmin, - RepoRead, - RepoWrite, - SysAdmin - } -} + Admin, + LicensedUser, + ProjectAdmin, + ProjectCreate, + ProjectRead, + ProjectView, + ProjectWrite, + RepoAdmin, + RepoRead, + RepoWrite, + SysAdmin, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/UserGroups.cs b/src/Bitbucket.Net/Models/Core/Admin/UserGroups.cs index 3e2d796..9ef172d 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/UserGroups.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/UserGroups.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class UserGroups { - public class UserGroups - { - public string User { get; set; } - public List Groups { get; set; } - } -} + public string? User { get; set; } + public List? Groups { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/UserInfo.cs b/src/Bitbucket.Net/Models/Core/Admin/UserInfo.cs index 96e4c62..c86c105 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/UserInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/UserInfo.cs @@ -1,14 +1,12 @@ using Bitbucket.Net.Models.Core.Users; -namespace Bitbucket.Net.Models.Core.Admin -{ - public class UserInfo : User - { - public string DirectoryName { get; set; } - public bool Deletable { get; set; } - public long LastAuthenticationTimestamp { get; set; } - public bool MutableDetails { get; set; } - public bool MutableGroups { get; set; } - } +namespace Bitbucket.Net.Models.Core.Admin; -} +public class UserInfo : User +{ + public string? DirectoryName { get; set; } + public bool Deletable { get; set; } + public long LastAuthenticationTimestamp { get; set; } + public bool MutableDetails { get; set; } + public bool MutableGroups { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/UserPermission.cs b/src/Bitbucket.Net/Models/Core/Admin/UserPermission.cs index a23bd2e..652e037 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/UserPermission.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/UserPermission.cs @@ -2,14 +2,13 @@ using Bitbucket.Net.Models.Core.Users; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class UserPermission { - public class UserPermission - { - public User User { get; set; } - [JsonConverter(typeof(PermissionsConverter))] - public Permissions Permission { get; set; } + public User? User { get; set; } + [JsonConverter(typeof(PermissionsConverter))] + public Permissions Permission { get; set; } - public override string ToString() => $"{Permission} - {User?.DisplayName}"; - } -} + public override string ToString() => $"{Permission} - {User?.DisplayName}"; +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/UserRename.cs b/src/Bitbucket.Net/Models/Core/Admin/UserRename.cs index 84a1143..a01a1e7 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/UserRename.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/UserRename.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Admin +namespace Bitbucket.Net.Models.Core.Admin; + +public class UserRename { - public class UserRename - { - public string Name { get; set; } - public string NewName { get; set; } - } -} + public string? Name { get; set; } + public string? NewName { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Logs/LogLevels.cs b/src/Bitbucket.Net/Models/Core/Logs/LogLevels.cs index 00c2ad7..78ddf4b 100644 --- a/src/Bitbucket.Net/Models/Core/Logs/LogLevels.cs +++ b/src/Bitbucket.Net/Models/Core/Logs/LogLevels.cs @@ -1,11 +1,10 @@ -namespace Bitbucket.Net.Models.Core.Logs +namespace Bitbucket.Net.Models.Core.Logs; + +public enum LogLevels { - public enum LogLevels - { - Trace, - Debug, - Info, - Warn, - Error - } -} + Trace, + Debug, + Info, + Warn, + Error, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/AheadBehindMetaData.cs b/src/Bitbucket.Net/Models/Core/Projects/AheadBehindMetaData.cs index c124a9b..05cf78c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/AheadBehindMetaData.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/AheadBehindMetaData.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class AheadBehindMetaData { - public class AheadBehindMetaData - { - public int Ahead { get; set; } - public int Behind { get; set; } - } -} + public int Ahead { get; set; } + public int Behind { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/AnchorStates.cs b/src/Bitbucket.Net/Models/Core/Projects/AnchorStates.cs index 05b9525..b3e76ab 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/AnchorStates.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/AnchorStates.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum AnchorStates { - public enum AnchorStates - { - Active, - Orphaned, - All - } -} + Active, + Orphaned, + All, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/ArchiveFormats.cs b/src/Bitbucket.Net/Models/Core/Projects/ArchiveFormats.cs index bfd966f..f759bc4 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ArchiveFormats.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ArchiveFormats.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum ArchiveFormats { - public enum ArchiveFormats - { - Zip, - Tar, - TarGz, - Tgz - } -} + Zip, + Tar, + TarGz, + Tgz, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Author.cs b/src/Bitbucket.Net/Models/Core/Projects/Author.cs index 066db48..630e4ad 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Author.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Author.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Author { - public class Author - { - public string Name { get; set; } - public string EmailAddress { get; set; } - } + public string? Name { get; set; } + public string? EmailAddress { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs b/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs index 4534a77..3b3f6a9 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs @@ -1,102 +1,101 @@ -using System; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Users; +using System; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +/// +/// Represents a blocker comment (task) in Bitbucket Server 9.0+. +/// Blocker comments are comments with severity +/// that must be resolved before the pull request can be merged. +/// +/// +/// +/// In Bitbucket Server 9.0+, the legacy /pull-requests/{id}/tasks endpoint was deprecated +/// and replaced with the blocker comments model. Tasks are now represented as comments with +/// severity: 'BLOCKER' and accessed via the /blocker-comments endpoint. +/// +/// +/// Use to retrieve blocker comments +/// from Bitbucket Server 9.0+. +/// +/// +public class BlockerComment { /// - /// Represents a blocker comment (task) in Bitbucket Server 9.0+. - /// Blocker comments are comments with severity - /// that must be resolved before the pull request can be merged. + /// The unique identifier of the blocker comment. /// - /// - /// - /// In Bitbucket Server 9.0+, the legacy /pull-requests/{id}/tasks endpoint was deprecated - /// and replaced with the blocker comments model. Tasks are now represented as comments with - /// severity: 'BLOCKER' and accessed via the /blocker-comments endpoint. - /// - /// - /// Use to retrieve blocker comments - /// from Bitbucket Server 9.0+. - /// - /// - public class BlockerComment - { - /// - /// The unique identifier of the blocker comment. - /// - public int Id { get; set; } + public int Id { get; set; } - /// - /// The version of the blocker comment, used for optimistic locking. - /// - public int Version { get; set; } + /// + /// The version of the blocker comment, used for optimistic locking. + /// + public int Version { get; set; } - /// - /// The text content of the blocker comment. - /// - public string Text { get; set; } = string.Empty; + /// + /// The text content of the blocker comment. + /// + public string Text { get; set; } = string.Empty; - /// - /// The user who created the blocker comment. - /// - public User? Author { get; set; } + /// + /// The user who created the blocker comment. + /// + public User? Author { get; set; } - /// - /// When the blocker comment was created. - /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? CreatedDate { get; set; } + /// + /// When the blocker comment was created. + /// + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? CreatedDate { get; set; } - /// - /// When the blocker comment was last updated. - /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? UpdatedDate { get; set; } + /// + /// When the blocker comment was last updated. + /// + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? UpdatedDate { get; set; } - /// - /// The severity level of the comment. For blocker comments, this is always . - /// - [JsonConverter(typeof(CommentSeverityConverter))] - public CommentSeverity Severity { get; set; } = CommentSeverity.Blocker; + /// + /// The severity level of the comment. For blocker comments, this is always . + /// + [JsonConverter(typeof(CommentSeverityConverter))] + public CommentSeverity Severity { get; set; } = CommentSeverity.Blocker; - /// - /// The state of the blocker comment. - /// - [JsonConverter(typeof(BlockerCommentStateConverter))] - public BlockerCommentState State { get; set; } = BlockerCommentState.Open; + /// + /// The state of the blocker comment. + /// + [JsonConverter(typeof(BlockerCommentStateConverter))] + public BlockerCommentState State { get; set; } = BlockerCommentState.Open; - /// - /// The user who resolved the blocker comment, if resolved. - /// - public User? Resolver { get; set; } + /// + /// The user who resolved the blocker comment, if resolved. + /// + public User? Resolver { get; set; } - /// - /// When the blocker comment was resolved. - /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? ResolvedDate { get; set; } + /// + /// When the blocker comment was resolved. + /// + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? ResolvedDate { get; set; } - /// - /// The anchor point for the comment (file, line number, etc.). - /// Null for general pull request-level blocker comments. - /// - public CommentAnchor? Anchor { get; set; } + /// + /// The anchor point for the comment (file, line number, etc.). + /// Null for general pull request-level blocker comments. + /// + public CommentAnchor? Anchor { get; set; } - /// - /// The parent comment this blocker is attached to, if any. - /// - public CommentRef? Parent { get; set; } + /// + /// The parent comment this blocker is attached to, if any. + /// + public CommentRef? Parent { get; set; } - /// - /// The permitted operations the current user can perform on this blocker comment. - /// - public Permittedoperations? PermittedOperations { get; set; } + /// + /// The permitted operations the current user can perform on this blocker comment. + /// + public Permittedoperations? PermittedOperations { get; set; } - /// - /// Additional properties associated with the blocker comment. - /// - public Properties? Properties { get; set; } - } -} + /// + /// Additional properties associated with the blocker comment. + /// + public Properties? Properties { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BlockerCommentState.cs b/src/Bitbucket.Net/Models/Core/Projects/BlockerCommentState.cs index 03fdda6..8750a90 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BlockerCommentState.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BlockerCommentState.cs @@ -1,18 +1,17 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +/// +/// Represents the state of a blocker comment (task) in Bitbucket Server 9.0+. +/// +public enum BlockerCommentState { /// - /// Represents the state of a blocker comment (task) in Bitbucket Server 9.0+. + /// The blocker comment is open and must be addressed before merging. /// - public enum BlockerCommentState - { - /// - /// The blocker comment is open and must be addressed before merging. - /// - Open, + Open, - /// - /// The blocker comment has been resolved. - /// - Resolved - } -} + /// + /// The blocker comment has been resolved. + /// + Resolved, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs index 0ec6dc6..e60ab1a 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs @@ -1,73 +1,72 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Branch : BranchBase { - public class Branch : BranchBase + private BranchMetaData? _branchMetadata; + private static readonly JsonSerializerOptions s_jsonOptions = new() { - private BranchMetaData? _branchMetadata; - private static readonly JsonSerializerOptions s_jsonOptions = new() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull - }; + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + }; - public string LatestCommit { get; set; } - public string LatestChangeset { get; set; } - public bool IsDefault { get; set; } + public string? LatestCommit { get; set; } + public string? LatestChangeset { get; set; } + public bool IsDefault { get; set; } - public BranchMetaData? BranchMetadata + public BranchMetaData? BranchMetadata + { + get { - get + if (_branchMetadata != null) + { + return _branchMetadata; + } + + if (Metadata == null || Metadata.Value.ValueKind != JsonValueKind.Array) + { + return null; + } + + _branchMetadata = new BranchMetaData(); + + foreach (var metadata in Metadata.Value.EnumerateArray()) { - if (_branchMetadata != null) + if (!metadata.TryGetProperty("Name", out var nameElement) && !metadata.TryGetProperty("name", out nameElement)) { - return _branchMetadata; + continue; } - if (Metadata == null || Metadata.Value.ValueKind != JsonValueKind.Array) + var name = nameElement.GetString(); + if (!metadata.TryGetProperty("Value", out var valueElement) && !metadata.TryGetProperty("value", out valueElement)) { - return null; + continue; } - _branchMetadata = new BranchMetaData(); + var valueJson = valueElement.GetRawText(); - foreach (var metadata in Metadata.Value.EnumerateArray()) + if (string.Equals(name, "com.atlassian.bitbucket.server.bitbucket-branch:ahead-behind-metadata-provider", System.StringComparison.Ordinal)) { - if (!metadata.TryGetProperty("Name", out var nameElement) && !metadata.TryGetProperty("name", out nameElement)) - { - continue; - } - - var name = nameElement.GetString(); - if (!metadata.TryGetProperty("Value", out var valueElement) && !metadata.TryGetProperty("value", out valueElement)) - { - continue; - } - - var valueJson = valueElement.GetRawText(); - - if (name == "com.atlassian.bitbucket.server.bitbucket-branch:ahead-behind-metadata-provider") - { - _branchMetadata.AheadBehind = JsonSerializer.Deserialize(valueJson, s_jsonOptions); - } - else if (name == "com.atlassian.bitbucket.server.bitbucket-build:build-status-metadata") - { - _branchMetadata.BuildStatus = JsonSerializer.Deserialize(valueJson, s_jsonOptions); - } - else if (name == "com.atlassian.bitbucket.server.bitbucket-ref-metadata:outgoing-pull-request-metadata") - { - _branchMetadata.OutgoingPullRequest = JsonSerializer.Deserialize(valueJson, s_jsonOptions); - } + _branchMetadata.AheadBehind = JsonSerializer.Deserialize(valueJson, s_jsonOptions); + } + else if (string.Equals(name, "com.atlassian.bitbucket.server.bitbucket-build:build-status-metadata", System.StringComparison.Ordinal)) + { + _branchMetadata.BuildStatus = JsonSerializer.Deserialize(valueJson, s_jsonOptions); + } + else if (string.Equals(name, "com.atlassian.bitbucket.server.bitbucket-ref-metadata:outgoing-pull-request-metadata", System.StringComparison.Ordinal)) + { + _branchMetadata.OutgoingPullRequest = JsonSerializer.Deserialize(valueJson, s_jsonOptions); } - - return _branchMetadata; } + + return _branchMetadata; } + } - [JsonPropertyName("metadata")] - public JsonElement? Metadata { get; set; } + [JsonPropertyName("metadata")] + public JsonElement? Metadata { get; set; } - public override string ToString() => DisplayId; - } -} + public override string ToString() => DisplayId; +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BranchBase.cs b/src/Bitbucket.Net/Models/Core/Projects/BranchBase.cs index e5e88f9..640da12 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BranchBase.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BranchBase.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class BranchBase : WithId { - public class BranchBase : WithId - { - public string DisplayId { get; set; } - public string Type { get; set; } - } -} + public string? DisplayId { get; set; } + public string? Type { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BranchInfo.cs b/src/Bitbucket.Net/Models/Core/Projects/BranchInfo.cs index 8c67348..6572976 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BranchInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BranchInfo.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class BranchInfo { - public class BranchInfo - { - public string Name { get; set; } - public string StartPoint { get; set; } - public string Message { get; set; } - } + public string? Name { get; set; } + public string? StartPoint { get; set; } + public string? Message { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BranchMetaData.cs b/src/Bitbucket.Net/Models/Core/Projects/BranchMetaData.cs index 8cd619e..930379c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BranchMetaData.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BranchMetaData.cs @@ -1,16 +1,15 @@ using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class BranchMetaData { - public class BranchMetaData - { - [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-branch:ahead-behind-metadata-provider")] - public AheadBehindMetaData? AheadBehind { get; set; } + [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-branch:ahead-behind-metadata-provider")] + public AheadBehindMetaData? AheadBehind { get; set; } - [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-build:build-status-metadata")] - public BuildStatusMetadata? BuildStatus { get; set; } + [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-build:build-status-metadata")] + public BuildStatusMetadata? BuildStatus { get; set; } - [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-ref-metadata:outgoing-pull-request-metadata")] - public PullRequestMetadata? OutgoingPullRequest { get; set; } - } -} + [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-ref-metadata:outgoing-pull-request-metadata")] + public PullRequestMetadata? OutgoingPullRequest { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BranchOrderBy.cs b/src/Bitbucket.Net/Models/Core/Projects/BranchOrderBy.cs index 6c15d8f..719108f 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BranchOrderBy.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BranchOrderBy.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum BranchOrderBy { - public enum BranchOrderBy - { - Alphabetical, - Modification - } + Alphabetical, + Modification, } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BranchRef.cs b/src/Bitbucket.Net/Models/Core/Projects/BranchRef.cs index ddbaa17..ac41fd9 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BranchRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BranchRef.cs @@ -1,5 +1,4 @@ -namespace Bitbucket.Net.Models.Core.Projects -{ - public class BranchRef : WithId - { } -} \ No newline at end of file +namespace Bitbucket.Net.Models.Core.Projects; + +public class BranchRef : WithId +{ } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BrowseItem.cs b/src/Bitbucket.Net/Models/Core/Projects/BrowseItem.cs index a970559..37143e4 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BrowseItem.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BrowseItem.cs @@ -1,11 +1,10 @@ using Bitbucket.Net.Common.Models; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class BrowseItem { - public class BrowseItem - { - public Path Path { get; set; } - public string Revision { get; set; } - public PagedResults Children { get; set; } - } -} + public Path? Path { get; set; } + public string? Revision { get; set; } + public PagedResults? Children { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BrowsePathItem.cs b/src/Bitbucket.Net/Models/Core/Projects/BrowsePathItem.cs index a142311..e0014d6 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BrowsePathItem.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BrowsePathItem.cs @@ -1,10 +1,9 @@ -using System.Collections.Generic; -using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Common.Models; +using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class BrowsePathItem : PagedResultsBase { - public class BrowsePathItem : PagedResultsBase - { - public List Lines { get; set; } - } -} + public List? Lines { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/BuildStatusMetadata.cs b/src/Bitbucket.Net/Models/Core/Projects/BuildStatusMetadata.cs index efce208..cc10510 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BuildStatusMetadata.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BuildStatusMetadata.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class BuildStatusMetadata { - public class BuildStatusMetadata - { - public int Successful { get; set; } - public int InProgress { get; set; } - public int Failed { get; set; } - } + public int Successful { get; set; } + public int InProgress { get; set; } + public int Failed { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Change.cs b/src/Bitbucket.Net/Models/Core/Projects/Change.cs index ff951f0..ffe9417 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Change.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Change.cs @@ -1,16 +1,15 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Change { - public class Change - { - public string ContentId { get; set; } - public string FromContentId { get; set; } - public Path Path { get; set; } - public bool Executable { get; set; } - public int PercentUnchanged { get; set; } - public string Type { get; set; } - public string NodeType { get; set; } - public Path SrcPath { get; set; } - public bool SrcExecutable { get; set; } - public Links Links { get; set; } - } -} + public string? ContentId { get; set; } + public string? FromContentId { get; set; } + public Path? Path { get; set; } + public bool Executable { get; set; } + public int PercentUnchanged { get; set; } + public string? Type { get; set; } + public string? NodeType { get; set; } + public Path? SrcPath { get; set; } + public bool SrcExecutable { get; set; } + public Links? Links { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/ChangeScopes.cs b/src/Bitbucket.Net/Models/Core/Projects/ChangeScopes.cs index 8d8a19f..2884747 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ChangeScopes.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ChangeScopes.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum ChangeScopes { - public enum ChangeScopes - { - All, - Unreviewed, - Range - } -} + All, + Unreviewed, + Range, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CloneLink.cs b/src/Bitbucket.Net/Models/Core/Projects/CloneLink.cs index 7e593c8..cbd3129 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CloneLink.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CloneLink.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class CloneLink : Link { - public class CloneLink : Link - { - public string Name { get; set; } - } -} + public string? Name { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CloneLinks.cs b/src/Bitbucket.Net/Models/Core/Projects/CloneLinks.cs index e6fc151..241d3ae 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CloneLinks.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CloneLinks.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class CloneLinks : Links { - public class CloneLinks : Links - { - public List Clone { get; set; } - } -} + public List? Clone { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs index 5096eae..cb77c29 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs @@ -1,52 +1,51 @@ -using System; -using System.Collections.Generic; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Tasks; using Bitbucket.Net.Models.Core.Users; +using System; +using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Comment : PullRequestInfo { - public class Comment : PullRequestInfo - { - public int Id { get; set; } - public int Version { get; set; } - public string Text { get; set; } + public int Id { get; set; } + public int Version { get; set; } + public string? Text { get; set; } - /// - /// Bitbucket Server comment state. - /// Common values: OPEN, PENDING, RESOLVED. - /// - /// - /// This intentionally hides . Although inheriting from - /// is not ideal for a comment model, using the same CLR member name avoids System.Text.Json property-name collisions. - /// - public new string? State { get; set; } + /// + /// Bitbucket Server comment state. + /// Common values: OPEN, PENDING, RESOLVED. + /// + /// + /// This intentionally hides . Although inheriting from + /// is not ideal for a comment model, using the same CLR member name avoids System.Text.Json property-name collisions. + /// + public new string? State { get; set; } - /// - /// Indicates whether the whole comment thread is resolved. - /// When true, Bitbucket UI will typically collapse/hide the thread as resolved. - /// - public bool? ThreadResolved { get; set; } + /// + /// Indicates whether the whole comment thread is resolved. + /// When true, Bitbucket UI will typically collapse/hide the thread as resolved. + /// + public bool? ThreadResolved { get; set; } - /// - /// The user who resolved the comment thread (when resolved). - /// - public User? Resolver { get; set; } + /// + /// The user who resolved the comment thread (when resolved). + /// + public User? Resolver { get; set; } - /// - /// When the comment thread was resolved (when resolved). - /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? ResolvedDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? CreatedDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? UpdatedDate { get; set; } - public User Author { get; set; } - public List Comments { get; set; } - public List Tasks { get; set; } - public List Participants { get; set; } - public Links Links { get; set; } - } -} + /// + /// When the comment thread was resolved (when resolved). + /// + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? ResolvedDate { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? CreatedDate { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? UpdatedDate { get; set; } + public User? Author { get; set; } + public List? Comments { get; set; } + public List? Tasks { get; set; } + public List? Participants { get; set; } + public Links? Links { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentAnchor.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentAnchor.cs index 394e136..1d62bd6 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentAnchor.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentAnchor.cs @@ -1,18 +1,17 @@ using Bitbucket.Net.Common.Converters; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class CommentAnchor { - public class CommentAnchor - { - public int? Line { get; set; } - [JsonConverter(typeof(LineTypesConverter))] - public LineTypes LineType { get; set; } - [JsonConverter(typeof(FileTypesConverter))] - public FileTypes FileType { get; set; } - public string FromHash { get; set; } - public string ToHash { get; set; } - public string Path { get; set; } - public string SrcPath { get; set; } - } + public int? Line { get; set; } + [JsonConverter(typeof(LineTypesConverter))] + public LineTypes LineType { get; set; } + [JsonConverter(typeof(FileTypesConverter))] + public FileTypes FileType { get; set; } + public string? FromHash { get; set; } + public string? ToHash { get; set; } + public string? Path { get; set; } + public string? SrcPath { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentId.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentId.cs index 399edaa..89db9cf 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentId.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentId.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class CommentId { - public class CommentId - { - public int? Id { get; set; } - } + public int? Id { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentInfo.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentInfo.cs index c891d20..70b1da8 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentInfo.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class CommentInfo { - public class CommentInfo - { - public string Text { get; set; } - public CommentId Parent { get; set; } - public CommentAnchor Anchor { get; set; } - } -} + public string? Text { get; set; } + public CommentId? Parent { get; set; } + public CommentAnchor? Anchor { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs index 74287cc..396db4a 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs @@ -1,25 +1,24 @@ -using System; -using System.Collections.Generic; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Tasks; using Bitbucket.Net.Models.Core.Users; +using System; +using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class CommentRef { - public class CommentRef - { - public Properties Properties { get; set; } - public int Id { get; set; } - public int Version { get; set; } - public string Text { get; set; } - public User Author { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? CreatedDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? UpdatedDate { get; set; } - public List Comments { get; set; } - public List Tasks { get; set; } - public Permittedoperations PermittedOperations { get; set; } - } + public Properties? Properties { get; set; } + public int Id { get; set; } + public int Version { get; set; } + public string? Text { get; set; } + public User? Author { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? CreatedDate { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? UpdatedDate { get; set; } + public List? Comments { get; set; } + public List? Tasks { get; set; } + public Permittedoperations? PermittedOperations { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentSeverity.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentSeverity.cs index f9f6ffa..ac05d7e 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentSeverity.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentSeverity.cs @@ -1,18 +1,17 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +/// +/// Represents the severity of a comment in Bitbucket Server 9.0+. +/// +public enum CommentSeverity { /// - /// Represents the severity of a comment in Bitbucket Server 9.0+. + /// A normal comment with no special behavior. /// - public enum CommentSeverity - { - /// - /// A normal comment with no special behavior. - /// - Normal, + Normal, - /// - /// A blocker comment (task) that must be resolved before merging. - /// - Blocker - } -} + /// + /// A blocker comment (task) that must be resolved before merging. + /// + Blocker, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentText.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentText.cs index a29d217..d1360c9 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentText.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentText.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class CommentText { - public class CommentText - { - public int Version { get; set; } - public string Text { get; set; } - } -} + public int Version { get; set; } + public string? Text { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Commit.cs b/src/Bitbucket.Net/Models/Core/Projects/Commit.cs index ff96fb8..6b16560 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Commit.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Commit.cs @@ -1,21 +1,20 @@ +using Bitbucket.Net.Common.Converters; using System; using System.Collections.Generic; -using Bitbucket.Net.Common.Converters; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Commit : CommitParent { - public class Commit : CommitParent - { - public Author Author { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset AuthorTimestamp { get; set; } - public Author Committer { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset CommitterTimestamp { get; set; } - public string Message { get; set; } - public List Parents { get; set; } - public int AuthorCount { get; set; } - public int TotalCount { get; set; } - } -} + public Author? Author { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset AuthorTimestamp { get; set; } + public Author? Committer { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset CommitterTimestamp { get; set; } + public string? Message { get; set; } + public List? Parents { get; set; } + public int AuthorCount { get; set; } + public int TotalCount { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommitParent.cs b/src/Bitbucket.Net/Models/Core/Projects/CommitParent.cs index 0d71089..463bf7c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommitParent.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommitParent.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class CommitParent { - public class CommitParent - { - public string Id { get; set; } - public string DisplayId { get; set; } - } + public string? Id { get; set; } + public string? DisplayId { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/ContentItem.cs b/src/Bitbucket.Net/Models/Core/Projects/ContentItem.cs index 0a1528c..21dc427 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ContentItem.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ContentItem.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class ContentItem { - public class ContentItem - { - public Path Path { get; set; } - public string ContentId { get; set; } - public string Type { get; set; } - public int Size { get; set; } - } + public Path? Path { get; set; } + public string? ContentId { get; set; } + public string? Type { get; set; } + public int Size { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Diff.cs b/src/Bitbucket.Net/Models/Core/Projects/Diff.cs index b963a46..571692d 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Diff.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Diff.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Diff : DiffInfo { - public class Diff : DiffInfo - { - public Path Source { get; set; } - public Path Destination { get; set; } - public List Hunks { get; set; } - } + public Path? Source { get; set; } + public Path? Destination { get; set; } + public List? Hunks { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs b/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs index c13d715..be15712 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs @@ -1,14 +1,13 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class DiffHunk { - public class DiffHunk - { - public int SourceLine { get; set; } - public int SourceSpan { get; set; } - public int DestinationLine { get; set; } - public int DestinationSpan { get; set; } - public List Segments { get; set; } - public bool Truncated { get; set; } - } + public int SourceLine { get; set; } + public int SourceSpan { get; set; } + public int DestinationLine { get; set; } + public int DestinationSpan { get; set; } + public List? Segments { get; set; } + public bool Truncated { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/DiffInfo.cs b/src/Bitbucket.Net/Models/Core/Projects/DiffInfo.cs index 1ba4235..e928bdc 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/DiffInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/DiffInfo.cs @@ -1,16 +1,15 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public abstract class DiffInfo { - public abstract class DiffInfo - { - /// - /// Indicates whether the diff was truncated by the server. - /// Note: Bitbucket Server 9.0+ returns boolean; older versions may return string. - /// - public bool Truncated { get; set; } + /// + /// Indicates whether the diff was truncated by the server. + /// Note: Bitbucket Server 9.0+ returns boolean; older versions may return string. + /// + public bool Truncated { get; set; } - public string ContextLines { get; set; } - public string FromHash { get; set; } - public string ToHash { get; set; } - public string WhiteSpace { get; set; } - } + public string? ContextLines { get; set; } + public string? FromHash { get; set; } + public string? ToHash { get; set; } + public string? WhiteSpace { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/DiffTypes.cs b/src/Bitbucket.Net/Models/Core/Projects/DiffTypes.cs index 3601ac5..6c9c1e9 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/DiffTypes.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/DiffTypes.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum DiffTypes { - public enum DiffTypes - { - Effective, - Range, - Commit - } -} + Effective, + Range, + Commit, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Differences.cs b/src/Bitbucket.Net/Models/Core/Projects/Differences.cs index ea339f1..ae9a102 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Differences.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Differences.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Differences : DiffInfo { - public class Differences : DiffInfo - { - public List Diffs { get; set; } - } -} + public List? Diffs { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/FileTypes.cs b/src/Bitbucket.Net/Models/Core/Projects/FileTypes.cs index 46429d5..8c08acf 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/FileTypes.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/FileTypes.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum FileTypes { - public enum FileTypes - { - From, - To - } -} + From, + To, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/FromToRef.cs b/src/Bitbucket.Net/Models/Core/Projects/FromToRef.cs index 189d24c..5e48c3c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/FromToRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/FromToRef.cs @@ -1,34 +1,33 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +/// +/// Represents a reference (branch/tag) in a pull request's source or target. +/// +public class FromToRef { /// - /// Represents a reference (branch/tag) in a pull request's source or target. + /// The full ref ID (e.g., "refs/heads/feature-branch"). /// - public class FromToRef - { - /// - /// The full ref ID (e.g., "refs/heads/feature-branch"). - /// - public string Id { get; set; } + public string? Id { get; set; } - /// - /// The display-friendly ref ID (e.g., "feature-branch"). - /// - public string? DisplayId { get; set; } + /// + /// The display-friendly ref ID (e.g., "feature-branch"). + /// + public string? DisplayId { get; set; } - /// - /// The SHA of the latest commit on this ref. - /// This is useful for creating line-specific comments on pull requests. - /// - public string? LatestCommit { get; set; } + /// + /// The SHA of the latest commit on this ref. + /// This is useful for creating line-specific comments on pull requests. + /// + public string? LatestCommit { get; set; } - /// - /// The type of ref (e.g., "BRANCH", "TAG"). - /// - public string? Type { get; set; } + /// + /// The type of ref (e.g., "BRANCH", "TAG"). + /// + public string? Type { get; set; } - /// - /// The repository containing this ref. - /// - public RepositoryRef Repository { get; set; } - } + /// + /// The repository containing this ref. + /// + public RepositoryRef? Repository { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Hook.cs b/src/Bitbucket.Net/Models/Core/Projects/Hook.cs index f048bc8..536de41 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Hook.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Hook.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Hook { - public class Hook - { - public HookDetails Details { get; set; } - public bool Enabled { get; set; } - public bool Configured { get; set; } - public HookScope Scope { get; set; } - } -} + public HookDetails? Details { get; set; } + public bool Enabled { get; set; } + public bool Configured { get; set; } + public HookScope? Scope { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/HookDetails.cs b/src/Bitbucket.Net/Models/Core/Projects/HookDetails.cs index cbd297e..9851963 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/HookDetails.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/HookDetails.cs @@ -1,18 +1,17 @@ -using System.Collections.Generic; using Bitbucket.Net.Common.Converters; +using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class HookDetails { - public class HookDetails - { - public string Key { get; set; } - public string Name { get; set; } - [JsonConverter(typeof(HookTypesConverter))] - public HookTypes Type { get; set; } - public string Description { get; set; } - public string Version { get; set; } - public object ConfigFormKey { get; set; } - public List ScopeTypes { get; set; } - } + public string? Key { get; set; } + public string? Name { get; set; } + [JsonConverter(typeof(HookTypesConverter))] + public HookTypes Type { get; set; } + public string? Description { get; set; } + public string? Version { get; set; } + public object? ConfigFormKey { get; set; } + public List? ScopeTypes { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/HookScope.cs b/src/Bitbucket.Net/Models/Core/Projects/HookScope.cs index 1d9ce6f..a5634ac 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/HookScope.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/HookScope.cs @@ -1,12 +1,11 @@ using Bitbucket.Net.Common.Converters; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class HookScope { - public class HookScope - { - public int ResourceId { get; set; } - [JsonConverter(typeof(ScopeTypesConverter))] - public ScopeTypes Type { get; set; } - } + public int ResourceId { get; set; } + [JsonConverter(typeof(ScopeTypesConverter))] + public ScopeTypes Type { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/HookTypes.cs b/src/Bitbucket.Net/Models/Core/Projects/HookTypes.cs index 7c74852..36396b4 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/HookTypes.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/HookTypes.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum HookTypes { - public enum HookTypes - { - PreReceive, - PostReceive, - PrePullRequestMerge - } -} + PreReceive, + PostReceive, + PrePullRequestMerge, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/LastModified.cs b/src/Bitbucket.Net/Models/Core/Projects/LastModified.cs index d997a4e..a2d581a 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/LastModified.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/LastModified.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class LastModified { - public class LastModified - { - public Dictionary Files { get; set; } - public Commit LatestCommit { get; set; } - } -} + public Dictionary? Files { get; set; } + public Commit? LatestCommit { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/LicensedUser.cs b/src/Bitbucket.Net/Models/Core/Projects/LicensedUser.cs index e3895b8..447a901 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/LicensedUser.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/LicensedUser.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class LicensedUser { - public class LicensedUser - { - public string Name { get; set; } - public bool Deletable { get; set; } - } -} + public string? Name { get; set; } + public bool Deletable { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Line.cs b/src/Bitbucket.Net/Models/Core/Projects/Line.cs index b2ad451..62bd824 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Line.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Line.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Line { - public class Line - { - public string Text { get; set; } - } + public string? Text { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/LineRef.cs b/src/Bitbucket.Net/Models/Core/Projects/LineRef.cs index 392f690..5d83201 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/LineRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/LineRef.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class LineRef { - public class LineRef - { - public int Destination { get; set; } - public int Source { get; set; } - public string Line { get; set; } - public bool Truncated { get; set; } - } + public int Destination { get; set; } + public int Source { get; set; } + public string? Line { get; set; } + public bool Truncated { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/LineTypes.cs b/src/Bitbucket.Net/Models/Core/Projects/LineTypes.cs index 49305a9..cee9aa1 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/LineTypes.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/LineTypes.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum LineTypes { - public enum LineTypes - { - Added, - Removed, - Context - } -} + Added, + Removed, + Context, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Link.cs b/src/Bitbucket.Net/Models/Core/Projects/Link.cs index c9c0743..da1b6fc 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Link.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Link.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Link { - public class Link - { - public string Href { get; set; } + public string? Href { get; set; } - public override string ToString() => Href; - } + public override string ToString() => Href; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Links.cs b/src/Bitbucket.Net/Models/Core/Projects/Links.cs index d2b50d8..95800e7 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Links.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Links.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Links { - public class Links - { - public List Self { get; set; } - } + public List? Self { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/MergeCheckRequiredBuilds.cs b/src/Bitbucket.Net/Models/Core/Projects/MergeCheckRequiredBuilds.cs index 19e0fb7..51ae255 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/MergeCheckRequiredBuilds.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/MergeCheckRequiredBuilds.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class MergeCheckRequiredBuilds { - public class MergeCheckRequiredBuilds - { - public bool Enable { get; set; } - public int Count { get; set; } - } + public bool Enable { get; set; } + public int Count { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/MergeCommits.cs b/src/Bitbucket.Net/Models/Core/Projects/MergeCommits.cs index d3b2ccd..08458a1 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/MergeCommits.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/MergeCommits.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum MergeCommits { - public enum MergeCommits - { - Exclude, - Include, - Only - } -} + Exclude, + Include, + Only, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/MergeHookRequiredApprovers.cs b/src/Bitbucket.Net/Models/Core/Projects/MergeHookRequiredApprovers.cs index a65794d..5af97a6 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/MergeHookRequiredApprovers.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/MergeHookRequiredApprovers.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class MergeHookRequiredApprovers { - public class MergeHookRequiredApprovers - { - public bool Enable { get; set; } - public int Count { get; set; } - } + public bool Enable { get; set; } + public int Count { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs index dddbc70..916a744 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs @@ -2,17 +2,16 @@ using Bitbucket.Net.Models.Core.Users; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Participant { - public class Participant - { - public User User { get; set; } - [JsonConverter(typeof(RolesConverter))] - public Roles Role { get; set; } - public bool Approved { get; set; } - [JsonConverter(typeof(ParticipantStatusConverter))] - public ParticipantStatus Status { get; set; } + public User? User { get; set; } + [JsonConverter(typeof(RolesConverter))] + public Roles Role { get; set; } + public bool Approved { get; set; } + [JsonConverter(typeof(ParticipantStatusConverter))] + public ParticipantStatus Status { get; set; } - public override string ToString() => User.DisplayName; - } -} + public override string ToString() => User.DisplayName; +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/ParticipantStatus.cs b/src/Bitbucket.Net/Models/Core/Projects/ParticipantStatus.cs index 00c85c6..9e70d2f 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ParticipantStatus.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ParticipantStatus.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum ParticipantStatus { - public enum ParticipantStatus - { - Unapproved, - NeedsWork, - Approved - } -} + Unapproved, + NeedsWork, + Approved, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Path.cs b/src/Bitbucket.Net/Models/Core/Projects/Path.cs index 6590e9f..c69a911 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Path.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Path.cs @@ -1,58 +1,57 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +/// +/// Represents a file path in a Bitbucket repository. +/// +public class Path { /// - /// Represents a file path in a Bitbucket repository. + /// The path components (directory and file name parts). + /// + public List? Components { get; set; } + + /// + /// The parent directory path. + /// + public string? Parent { get; set; } + + /// + /// The file or directory name. + /// + public string? Name { get; set; } + + /// + /// The file extension (if any). /// - public class Path + public string? Extension { get; set; } + + /// + /// The full path as a string, as returned by the Bitbucket API. + /// Note: This property name is lowercase to match the JSON response. + /// + // ReSharper disable once InconsistentNaming + public string? toString { get; set; } + + /// + /// Returns the full path string representation. + /// + /// + /// The path string from the API if available; otherwise, + /// constructs the path from Components or falls back to Name. + /// + public override string ToString() { - /// - /// The path components (directory and file name parts). - /// - public List Components { get; set; } - - /// - /// The parent directory path. - /// - public string Parent { get; set; } - - /// - /// The file or directory name. - /// - public string Name { get; set; } - - /// - /// The file extension (if any). - /// - public string Extension { get; set; } - - /// - /// The full path as a string, as returned by the Bitbucket API. - /// Note: This property name is lowercase to match the JSON response. - /// - // ReSharper disable once InconsistentNaming - public string toString { get; set; } - - /// - /// Returns the full path string representation. - /// - /// - /// The path string from the API if available; otherwise, - /// constructs the path from Components or falls back to Name. - /// - public override string ToString() - { - // Prefer the API-provided toString property - if (!string.IsNullOrEmpty(toString)) - return toString; - - // Build from components if available - if (Components is { Count: > 0 }) - return string.Join("/", Components); - - // Fallback to name, then type name (shouldn't happen in practice) - return Name ?? "(unknown path)"; - } + // Prefer the API-provided toString property + if (!string.IsNullOrEmpty(toString)) + return toString; + + // Build from components if available + if (Components is { Count: > 0 }) + return string.Join('/', Components); + + // Fallback to name, then type name (shouldn't happen in practice) + return Name ?? "(unknown path)"; } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Permittedoperations.cs b/src/Bitbucket.Net/Models/Core/Projects/Permittedoperations.cs index 07e8017..4444410 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Permittedoperations.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Permittedoperations.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Permittedoperations { - public class Permittedoperations - { - public bool Editable { get; set; } - public bool Deletable { get; set; } - public bool Transitionable { get; set; } - } + public bool Editable { get; set; } + public bool Deletable { get; set; } + public bool Transitionable { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Project.cs b/src/Bitbucket.Net/Models/Core/Projects/Project.cs index 72e474e..de8308b 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Project.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Project.cs @@ -1,12 +1,11 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Project : ProjectDefinition { - public class Project : ProjectDefinition - { - public int Id { get; set; } - public bool Public { get; set; } - public string Type { get; set; } - public Links Links { get; set; } + public int Id { get; set; } + public bool Public { get; set; } + public string? Type { get; set; } + public Links? Links { get; set; } - public override string ToString() => Name; - } -} + public override string ToString() => Name; +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs b/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs index 7ecaef3..3dc5785 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class ProjectDefinition : ProjectRef { - public class ProjectDefinition : ProjectRef - { - public string Name { get; set; } - public string Description { get; set; } - //public string Avatar { get; set; } - } -} + public string? Name { get; set; } + public string? Description { get; set; } + //public string Avatar { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/ProjectRef.cs b/src/Bitbucket.Net/Models/Core/Projects/ProjectRef.cs index 2dbe271..5757caa 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ProjectRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ProjectRef.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class ProjectRef { - public class ProjectRef - { - public string Key { get; set; } - } -} + public string? Key { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Properties.cs b/src/Bitbucket.Net/Models/Core/Projects/Properties.cs index a5fd1b5..820eb56 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Properties.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Properties.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Properties { - public class Properties - { - public string Key { get; set; } - } + public string? Key { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs index 2c85328..4869439 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs @@ -1,22 +1,21 @@ +using Bitbucket.Net.Common.Converters; using System; using System.Collections.Generic; -using Bitbucket.Net.Common.Converters; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class PullRequest : PullRequestInfo { - public class PullRequest : PullRequestInfo - { - public int Id { get; set; } - public int Version { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? CreatedDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? UpdatedDate { get; set; } - public Participant Author { get; set; } - public List Participants { get; set; } - public Links Links { get; set; } + public int Id { get; set; } + public int Version { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? CreatedDate { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? UpdatedDate { get; set; } + public Participant? Author { get; set; } + public List? Participants { get; set; } + public Links? Links { get; set; } - public override string ToString() => $"{Author.User.DisplayName}: {Title ?? "(untitled)"}"; - } -} + public override string ToString() => $"{Author.User.DisplayName}: {Title ?? "(untitled)"}"; +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs index 1c5fe0e..80c1420 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs @@ -1,20 +1,19 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Users; -using System.Text.Json.Serialization; using System; +using System.Text.Json.Serialization; + +namespace Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Models.Core.Projects +public class PullRequestActivity { - public class PullRequestActivity - { - public int Id { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? CreatedDate { get; set; } - public User User { get; set; } - public string Action { get; set; } - public string CommentAction { get; set; } - public Comment Comment { get; set; } - public CommentAnchor CommentAnchor { get; set; } - public Commit Commit { get; set; } - } -} + public int Id { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? CreatedDate { get; set; } + public User? User { get; set; } + public string? Action { get; set; } + public string? CommentAction { get; set; } + public Comment? Comment { get; set; } + public CommentAnchor? CommentAnchor { get; set; } + public Commit? Commit { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestDirections.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestDirections.cs index 33605cd..ce22521 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestDirections.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestDirections.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum PullRequestDirections { - public enum PullRequestDirections - { - Incoming, - Outgoing - } + Incoming, + Outgoing, } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestFromTypes.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestFromTypes.cs index 49874a6..f60e796 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestFromTypes.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestFromTypes.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum PullRequestFromTypes { - public enum PullRequestFromTypes - { - Comment, - Activity - } -} + Comment, + Activity, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs index e0fc94a..a28c07a 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs @@ -1,20 +1,19 @@ -using System.Collections.Generic; using Bitbucket.Net.Common.Converters; +using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class PullRequestInfo { - public class PullRequestInfo - { - public string Title { get; set; } - public string Description { get; set; } - [JsonConverter(typeof(PullRequestStatesConverter))] - public PullRequestStates State { get; set; } - public bool Open { get; set; } - public bool Closed { get; set; } - public FromToRef FromRef { get; set; } - public FromToRef ToRef { get; set; } - public bool Locked { get; set; } - public List Reviewers { get; set; } - } -} + public string? Title { get; set; } + public string? Description { get; set; } + [JsonConverter(typeof(PullRequestStatesConverter))] + public PullRequestStates State { get; set; } + public bool Open { get; set; } + public bool Closed { get; set; } + public FromToRef? FromRef { get; set; } + public FromToRef? ToRef { get; set; } + public bool Locked { get; set; } + public List? Reviewers { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestMergeState.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestMergeState.cs index d174c20..27265bf 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestMergeState.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestMergeState.cs @@ -1,12 +1,11 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class PullRequestMergeState { - public class PullRequestMergeState - { - public bool CanMerge { get; set; } - public bool Conflicted { get; set; } - public string Outcome { get; set; } - public List Vetoes { get; set; } - } -} + public bool CanMerge { get; set; } + public bool Conflicted { get; set; } + public string? Outcome { get; set; } + public List? Vetoes { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestMetadata.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestMetadata.cs index fdea2ac..b0c1ee6 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestMetadata.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestMetadata.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class PullRequestMetadata { - public class PullRequestMetadata - { - public PullRequest PullRequest { get; set; } - } + public PullRequest? PullRequest { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestOrders.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestOrders.cs index f22e516..6ada768 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestOrders.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestOrders.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum PullRequestOrders { - public enum PullRequestOrders - { - Newest, - Oldest, - ParticipantStatus, - ClosedDate - } + Newest, + Oldest, + ParticipantStatus, + ClosedDate, } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestSettings.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestSettings.cs index 07e3cc7..c8cfe0b 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestSettings.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestSettings.cs @@ -1,18 +1,17 @@ using Bitbucket.Net.Models.Core.Admin; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class PullRequestSettings { - public class PullRequestSettings - { - public MergeStrategies MergeConfig { get; set; } - public bool RequiredAllApprovers { get; set; } - public bool RequiredAllTasksComplete { get; set; } - [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-bundled-hooks:requiredApprovers")] - public MergeHookRequiredApprovers ComatlassianbitbucketserverbundledhooksrequiredApprovers { get; set; } - public int RequiredApprovers { get; set; } - [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-build:requiredBuilds")] - public MergeCheckRequiredBuilds ComatlassianbitbucketserverbitbucketbuildrequiredBuilds { get; set; } - public int RequiredSuccessfulBuilds { get; set; } - } -} + public MergeStrategies? MergeConfig { get; set; } + public bool RequiredAllApprovers { get; set; } + public bool RequiredAllTasksComplete { get; set; } + [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-bundled-hooks:requiredApprovers")] + public MergeHookRequiredApprovers? ComatlassianbitbucketserverbundledhooksrequiredApprovers { get; set; } + public int RequiredApprovers { get; set; } + [JsonPropertyName("com.atlassian.bitbucket.server.bitbucket-build:requiredBuilds")] + public MergeCheckRequiredBuilds? ComatlassianbitbucketserverbitbucketbuildrequiredBuilds { get; set; } + public int RequiredSuccessfulBuilds { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestStates.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestStates.cs index 4091411..40d6fbe 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestStates.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestStates.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum PullRequestStates { - public enum PullRequestStates - { - Open, - Declined, - Merged, - All - } + Open, + Declined, + Merged, + All, } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestSuggestion.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestSuggestion.cs index 18638fd..3a6fa79 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestSuggestion.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestSuggestion.cs @@ -1,16 +1,15 @@ -using System; using Bitbucket.Net.Common.Converters; +using System; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class PullRequestSuggestion { - public class PullRequestSuggestion - { - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset ChangeTime { get; set; } - public RefChange RefChange { get; set; } - public Repository Repository { get; set; } - public Ref FromRef { get; set; } - public Ref ToRef { get; set; } - } -} + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset ChangeTime { get; set; } + public RefChange? RefChange { get; set; } + public Repository? Repository { get; set; } + public Ref? FromRef { get; set; } + public Ref? ToRef { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestUpdate.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestUpdate.cs index 064b172..df0aa36 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestUpdate.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestUpdate.cs @@ -1,13 +1,12 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class PullRequestUpdate { - public class PullRequestUpdate - { - public int Id { get; set; } - public int Version { get; set; } - public string Title { get; set; } - public string Description { get; set; } - public List Reviewers { get; set; } - } -} + public int Id { get; set; } + public int Version { get; set; } + public string? Title { get; set; } + public string? Description { get; set; } + public List? Reviewers { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Ref.cs b/src/Bitbucket.Net/Models/Core/Projects/Ref.cs index 49fa0e5..e8fa224 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Ref.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Ref.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Ref { - public class Ref - { - public string Id { get; set; } - public string DisplayId { get; set; } - public string Type { get; set; } - } + public string? Id { get; set; } + public string? DisplayId { get; set; } + public string? Type { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/RefChange.cs b/src/Bitbucket.Net/Models/Core/Projects/RefChange.cs index 3f04143..5e1a82f 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/RefChange.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/RefChange.cs @@ -1,11 +1,10 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class RefChange { - public class RefChange - { - public Ref Ref { get; set; } - public string RefId { get; set; } - public string FromHash { get; set; } - public string ToHash { get; set; } - public string Type { get; set; } - } + public Ref? Ref { get; set; } + public string? RefId { get; set; } + public string? FromHash { get; set; } + public string? ToHash { get; set; } + public string? Type { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Repository.cs b/src/Bitbucket.Net/Models/Core/Projects/Repository.cs index f818589..36eb550 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Repository.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Repository.cs @@ -1,15 +1,14 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Repository : RepositoryRef { - public class Repository : RepositoryRef - { - public int Id { get; set; } - public string ScmId { get; set; } - public string State { get; set; } - public string StatusMessage { get; set; } - public bool Forkable { get; set; } - public bool Public { get; set; } - public CloneLinks Links { get; set; } + public int Id { get; set; } + public string? ScmId { get; set; } + public string? State { get; set; } + public string? StatusMessage { get; set; } + public bool Forkable { get; set; } + public bool Public { get; set; } + public CloneLinks? Links { get; set; } - public override string ToString() => Name; - } -} + public override string ToString() => Name; +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/RepositoryFork.cs b/src/Bitbucket.Net/Models/Core/Projects/RepositoryFork.cs index c337473..def5f2d 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/RepositoryFork.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/RepositoryFork.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class RepositoryFork : RepositoryOrigin { - public class RepositoryFork : RepositoryOrigin - { - public RepositoryOrigin Origin { get; set; } - } -} + public RepositoryOrigin? Origin { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/RepositoryOrigin.cs b/src/Bitbucket.Net/Models/Core/Projects/RepositoryOrigin.cs index 2645250..064eec8 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/RepositoryOrigin.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/RepositoryOrigin.cs @@ -1,16 +1,15 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class RepositoryOrigin { - public class RepositoryOrigin - { - public string Slug { get; set; } - public int Id { get; set; } - public string Name { get; set; } - public string ScmId { get; set; } - public string State { get; set; } - public string StatusMessage { get; set; } - public bool Forkable { get; set; } - public Project Project { get; set; } - public bool Public { get; set; } - public Links Links { get; set; } - } + public string? Slug { get; set; } + public int Id { get; set; } + public string? Name { get; set; } + public string? ScmId { get; set; } + public string? State { get; set; } + public string? StatusMessage { get; set; } + public bool Forkable { get; set; } + public Project? Project { get; set; } + public bool Public { get; set; } + public Links? Links { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/RepositoryRef.cs b/src/Bitbucket.Net/Models/Core/Projects/RepositoryRef.cs index ddcd4a5..86f7374 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/RepositoryRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/RepositoryRef.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class RepositoryRef { - public class RepositoryRef - { - public string Slug { get; set; } - public string Name { get; set; } - public ProjectRef Project { get; set; } - } -} + public string? Slug { get; set; } + public string? Name { get; set; } + public ProjectRef? Project { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Reviewer.cs b/src/Bitbucket.Net/Models/Core/Projects/Reviewer.cs index 30fde09..8f1c3a6 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Reviewer.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Reviewer.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Reviewer : Participant { - public class Reviewer : Participant - { - public string LastReviewedCommit { get; set; } - } + public string? LastReviewedCommit { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Roles.cs b/src/Bitbucket.Net/Models/Core/Projects/Roles.cs index f2dee8a..159d792 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Roles.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Roles.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum Roles { - public enum Roles - { - Author, - Reviewer, - Participant - } -} + Author, + Reviewer, + Participant, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/ScopeTypes.cs b/src/Bitbucket.Net/Models/Core/Projects/ScopeTypes.cs index 3660a15..6c73632 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ScopeTypes.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ScopeTypes.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum ScopeTypes { - public enum ScopeTypes - { - Project, - Repository - } -} + Project, + Repository, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Segment.cs b/src/Bitbucket.Net/Models/Core/Projects/Segment.cs index c11fb88..e77a071 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Segment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Segment.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Segment { - public class Segment - { - public string Type { get; set; } - public List Lines { get; set; } - public bool Truncated { get; set; } - } + public string? Type { get; set; } + public List? Lines { get; set; } + public bool Truncated { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Tag.cs b/src/Bitbucket.Net/Models/Core/Projects/Tag.cs index 39870b1..f390624 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Tag.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Tag.cs @@ -1,12 +1,11 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Tag { - public class Tag - { - public string Id { get; set; } - public string DisplayId { get; set; } - public string Type { get; set; } - public string LatestCommit { get; set; } - public string LatestChangeset { get; set; } - public string Hash { get; set; } - } -} + public string? Id { get; set; } + public string? DisplayId { get; set; } + public string? Type { get; set; } + public string? LatestCommit { get; set; } + public string? LatestChangeset { get; set; } + public string? Hash { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/TimeWindow.cs b/src/Bitbucket.Net/Models/Core/Projects/TimeWindow.cs index 7b2729f..d15d41b 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/TimeWindow.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/TimeWindow.cs @@ -1,13 +1,12 @@ -using System; using Bitbucket.Net.Common.Converters; +using System; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class TimeWindow { - public class TimeWindow - { - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset Start { get; set; } - public long Duration { get; set; } - } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset Start { get; set; } + public long Duration { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/VersionInfo.cs b/src/Bitbucket.Net/Models/Core/Projects/VersionInfo.cs index e7f748e..7396527 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/VersionInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/VersionInfo.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class VersionInfo { - public class VersionInfo - { - public int Version { get; set; } - } -} + public int Version { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Veto.cs b/src/Bitbucket.Net/Models/Core/Projects/Veto.cs index c284d04..a9cc7df 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Veto.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Veto.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class Veto { - public class Veto - { - public string SummaryMessage { get; set; } - public string DetailedMessage { get; set; } - } + public string? SummaryMessage { get; set; } + public string? DetailedMessage { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHook.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHook.cs index 8a9a392..3f11611 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHook.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHook.cs @@ -1,21 +1,20 @@ +using Bitbucket.Net.Common.Converters; using System; using System.Collections.Generic; -using Bitbucket.Net.Common.Converters; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHook { - public class WebHook - { - public int Id { get; set; } - public string Name { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset CreatedDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset UpdatedDate { get; set; } - public List Events { get; set; } - public Dictionary Configuration { get; set; } - public string Url { get; set; } - public bool Active { get; set; } - } -} + public int Id { get; set; } + public string? Name { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset CreatedDate { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset UpdatedDate { get; set; } + public List? Events { get; set; } + public Dictionary? Configuration { get; set; } + public string? Url { get; set; } + public bool Active { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookInvocation.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookInvocation.cs index 93f5b94..c6a50e4 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookInvocation.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookInvocation.cs @@ -1,19 +1,18 @@ -using System; using Bitbucket.Net.Common.Converters; +using System; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHookInvocation { - public class WebHookInvocation - { - public int Id { get; set; } - public string Event { get; set; } - public int Duration { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset Start { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset Finish { get; set; } - public WebHookRequest Request { get; set; } - public WebHookResult Result { get; set; } - } + public int Id { get; set; } + public string? Event { get; set; } + public int Duration { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset Start { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset Finish { get; set; } + public WebHookRequest? Request { get; set; } + public WebHookResult? Result { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookOutcomes.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookOutcomes.cs index 450bd4c..279c83e 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookOutcomes.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookOutcomes.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public enum WebHookOutcomes { - public enum WebHookOutcomes - { - Success, - Failure, - Error - } -} + Success, + Failure, + Error, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookRequest.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookRequest.cs index df59095..6dd5c3e 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookRequest.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookRequest.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHookRequest { - public class WebHookRequest - { - public string Url { get; set; } - public string Method { get; set; } - } -} + public string? Url { get; set; } + public string? Method { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookResult.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookResult.cs index 875e905..e8b7399 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookResult.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookResult.cs @@ -1,12 +1,11 @@ using Bitbucket.Net.Common.Converters; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHookResult { - public class WebHookResult - { - public string Description { get; set; } - [JsonConverter(typeof(WebHookOutcomesConverter))] - public WebHookOutcomes Outcome { get; set; } - } -} + public string? Description { get; set; } + [JsonConverter(typeof(WebHookOutcomesConverter))] + public WebHookOutcomes Outcome { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookStatistics.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookStatistics.cs index afe3939..41aa17b 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookStatistics.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookStatistics.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHookStatistics { - public class WebHookStatistics - { - public WebHookStatisticsCounts Counts { get; set; } - } -} + public WebHookStatisticsCounts? Counts { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookStatisticsCounts.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookStatisticsCounts.cs index ad9a0d4..ecc6cc6 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookStatisticsCounts.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookStatisticsCounts.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHookStatisticsCounts { - public class WebHookStatisticsCounts - { - public int Errors { get; set; } - public int Failures { get; set; } - public int Successes { get; set; } - public TimeWindow Window { get; set; } - } + public int Errors { get; set; } + public int Failures { get; set; } + public int Successes { get; set; } + public TimeWindow? Window { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookStatisticsSummary.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookStatisticsSummary.cs index 79f321e..aae165f 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookStatisticsSummary.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookStatisticsSummary.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHookStatisticsSummary { - public class WebHookStatisticsSummary - { - public WebHookInvocation LastSuccess { get; set; } - public WebHookInvocation LastFailure { get; set; } - public WebHookInvocation LastError { get; set; } - public int Counts { get; set; } - } -} + public WebHookInvocation? LastSuccess { get; set; } + public WebHookInvocation? LastFailure { get; set; } + public WebHookInvocation? LastError { get; set; } + public int Counts { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequest.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequest.cs index 093bf1c..3c123fe 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequest.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequest.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHookTestRequest : WebHookRequest { - public class WebHookTestRequest : WebHookRequest - { - public string Body { get; set; } - public List Headers { get; set; } - } + public string? Body { get; set; } + public List? Headers { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequestResponse.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequestResponse.cs index cfee4f2..3588983 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequestResponse.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequestResponse.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHookTestRequestResponse { - public class WebHookTestRequestResponse - { - public WebHookTestRequest Request { get; set; } - public WebHookTestResponse Response { get; set; } - } -} + public WebHookTestRequest? Request { get; set; } + public WebHookTestResponse? Response { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestResponse.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestResponse.cs index ae17e58..fc5bb59 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestResponse.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestResponse.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WebHookTestResponse { - public class WebHookTestResponse - { - public int Status { get; set; } - public List Headers { get; set; } - public string Body { get; set; } - } + public int Status { get; set; } + public List? Headers { get; set; } + public string? Body { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WithId.cs b/src/Bitbucket.Net/Models/Core/Projects/WithId.cs index b65155d..2511472 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WithId.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WithId.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Projects +namespace Bitbucket.Net.Models.Core.Projects; + +public class WithId { - public class WithId - { - public string Id { get; set; } - } -} + public string? Id { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTask.cs b/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTask.cs index 4811029..1da05e9 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTask.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTask.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Tasks +namespace Bitbucket.Net.Models.Core.Tasks; + +public class BitbucketTask : TaskRef { - public class BitbucketTask : TaskRef - { - public TaskAnchor Anchor { get; set; } - public string State { get; set; } - } -} + public TaskAnchor? Anchor { get; set; } + public string? State { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTaskCount.cs b/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTaskCount.cs index 3492af3..1f1f749 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTaskCount.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTaskCount.cs @@ -1,9 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Tasks -{ - public class BitbucketTaskCount - { - public int Open { get; set; } - public int Resolved { get; set; } - } +namespace Bitbucket.Net.Models.Core.Tasks; -} +public class BitbucketTaskCount +{ + public int Open { get; set; } + public int Resolved { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs index 82f65fd..05f6ae7 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs @@ -1,17 +1,16 @@ -using System; -using System.Collections.Generic; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Projects; +using System; +using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Tasks +namespace Bitbucket.Net.Models.Core.Tasks; + +public class TaskAnchor : TaskRef { - public class TaskAnchor : TaskRef - { - public int Version { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? UpdatedDate { get; set; } - public List Comments { get; set; } - public List Tasks { get; set; } - } + public int Version { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? UpdatedDate { get; set; } + public List? Comments { get; set; } + public List? Tasks { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskBasicAnchor.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskBasicAnchor.cs index 29cd79c..d2edc8f 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskBasicAnchor.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskBasicAnchor.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Tasks +namespace Bitbucket.Net.Models.Core.Tasks; + +public class TaskBasicAnchor { - public class TaskBasicAnchor - { - public int Id { get; set; } - public string Type { get; set; } - } + public int Id { get; set; } + public string? Type { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskInfo.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskInfo.cs index 7fb669b..be6c45a 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskInfo.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Core.Tasks +namespace Bitbucket.Net.Models.Core.Tasks; + +public class TaskInfo { - public class TaskInfo - { - public TaskBasicAnchor Anchor { get; set; } - public string Text { get; set; } - } -} + public TaskBasicAnchor? Anchor { get; set; } + public string? Text { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs index cad5b79..566705a 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs @@ -1,19 +1,18 @@ -using System; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Core.Users; +using System; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Core.Tasks +namespace Bitbucket.Net.Models.Core.Tasks; + +public abstract class TaskRef { - public abstract class TaskRef - { - public Properties Properties { get; set; } - public int Id { get; set; } - public string Text { get; set; } - public User Author { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset? CreatedDate { get; set; } - public Permittedoperations PermittedOperations { get; set; } - } + public Properties? Properties { get; set; } + public int Id { get; set; } + public string? Text { get; set; } + public User? Author { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset? CreatedDate { get; set; } + public Permittedoperations? PermittedOperations { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Users/Identity.cs b/src/Bitbucket.Net/Models/Core/Users/Identity.cs index 12ad5b0..f08ee5e 100644 --- a/src/Bitbucket.Net/Models/Core/Users/Identity.cs +++ b/src/Bitbucket.Net/Models/Core/Users/Identity.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Core.Users +namespace Bitbucket.Net.Models.Core.Users; + +public class Identity : Named { - public class Identity : Named - { - public string EmailAddress { get; set; } - } + public string? EmailAddress { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Users/Named.cs b/src/Bitbucket.Net/Models/Core/Users/Named.cs index 1b9e1ce..51250fc 100644 --- a/src/Bitbucket.Net/Models/Core/Users/Named.cs +++ b/src/Bitbucket.Net/Models/Core/Users/Named.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.Core.Users +namespace Bitbucket.Net.Models.Core.Users; + +public class Named { - public class Named - { - public string Name { get; set; } + public string? Name { get; set; } - public override string ToString() => Name; - } -} + public override string ToString() => Name; +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Users/PasswordChange.cs b/src/Bitbucket.Net/Models/Core/Users/PasswordChange.cs index 6fd5ae8..fdfb9d1 100644 --- a/src/Bitbucket.Net/Models/Core/Users/PasswordChange.cs +++ b/src/Bitbucket.Net/Models/Core/Users/PasswordChange.cs @@ -1,9 +1,8 @@ using Bitbucket.Net.Models.Core.Admin; -namespace Bitbucket.Net.Models.Core.Users +namespace Bitbucket.Net.Models.Core.Users; + +public class PasswordChange : PasswordBasic { - public class PasswordChange : PasswordBasic - { - public string OldPassword { get; set; } - } -} + public string? OldPassword { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Users/User.cs b/src/Bitbucket.Net/Models/Core/Users/User.cs index 616ccbc..00564ef 100644 --- a/src/Bitbucket.Net/Models/Core/Users/User.cs +++ b/src/Bitbucket.Net/Models/Core/Users/User.cs @@ -1,14 +1,13 @@ -namespace Bitbucket.Net.Models.Core.Users +namespace Bitbucket.Net.Models.Core.Users; + +public class User : Identity { - public class User : Identity - { - public int Id { get; set; } - public string DisplayName { get; set; } - public bool Active { get; set; } - public string Slug { get; set; } - public string Type { get; set; } - public string AvatarUrl { get; set; } + public int Id { get; set; } + public string? DisplayName { get; set; } + public bool Active { get; set; } + public string? Slug { get; set; } + public string? Type { get; set; } + public string? AvatarUrl { get; set; } - public override string ToString() => DisplayName; - } + public override string ToString() => DisplayName; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestCondition.cs b/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestCondition.cs index b034270..bcb426b 100644 --- a/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestCondition.cs +++ b/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestCondition.cs @@ -1,15 +1,14 @@ -using System.Collections.Generic; -using Bitbucket.Net.Models.Core.Users; +using Bitbucket.Net.Models.Core.Users; +using System.Collections.Generic; -namespace Bitbucket.Net.Models.DefaultReviewers +namespace Bitbucket.Net.Models.DefaultReviewers; + +public class DefaultReviewerPullRequestCondition { - public class DefaultReviewerPullRequestCondition - { - public int Id { get; set; } - public DefaultReviewerPullRequestConditionScope Scope { get; set; } - public RefMatcher SourceRefMatcher { get; set; } - public RefMatcher TargetRefMatcher { get; set; } - public List Reviewers { get; set; } - public int RequiredApprovals { get; set; } - } -} + public int Id { get; set; } + public DefaultReviewerPullRequestConditionScope? Scope { get; set; } + public RefMatcher? SourceRefMatcher { get; set; } + public RefMatcher? TargetRefMatcher { get; set; } + public List? Reviewers { get; set; } + public int RequiredApprovals { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestConditionScope.cs b/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestConditionScope.cs index 3bce9f4..f32bee2 100644 --- a/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestConditionScope.cs +++ b/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestConditionScope.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.DefaultReviewers +namespace Bitbucket.Net.Models.DefaultReviewers; + +public class DefaultReviewerPullRequestConditionScope { - public class DefaultReviewerPullRequestConditionScope - { - public string Type { get; set; } - public int ResourceId { get; set; } - } + public string? Type { get; set; } + public int ResourceId { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestConditionType.cs b/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestConditionType.cs index cfdda3a..9267530 100644 --- a/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestConditionType.cs +++ b/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestConditionType.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.DefaultReviewers +namespace Bitbucket.Net.Models.DefaultReviewers; + +public class DefaultReviewerPullRequestConditionType { - public class DefaultReviewerPullRequestConditionType - { - public string Id { get; set; } - public string Name { get; set; } - } + public string? Id { get; set; } + public string? Name { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/DefaultReviewers/RefMatcher.cs b/src/Bitbucket.Net/Models/DefaultReviewers/RefMatcher.cs index af35739..b852734 100644 --- a/src/Bitbucket.Net/Models/DefaultReviewers/RefMatcher.cs +++ b/src/Bitbucket.Net/Models/DefaultReviewers/RefMatcher.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.DefaultReviewers +namespace Bitbucket.Net.Models.DefaultReviewers; + +public class RefMatcher { - public class RefMatcher - { - public bool Active { get; set; } - public string Id { get; set; } - public string DisplayId { get; set; } - public DefaultReviewerPullRequestConditionType Type { get; set; } - } + public bool Active { get; set; } + public string? Id { get; set; } + public string? DisplayId { get; set; } + public DefaultReviewerPullRequestConditionType? Type { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Git/RebasePullRequestCondition.cs b/src/Bitbucket.Net/Models/Git/RebasePullRequestCondition.cs index 9648ed0..b925c30 100644 --- a/src/Bitbucket.Net/Models/Git/RebasePullRequestCondition.cs +++ b/src/Bitbucket.Net/Models/Git/RebasePullRequestCondition.cs @@ -1,11 +1,10 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.Git +namespace Bitbucket.Net.Models.Git; + +public class RebasePullRequestCondition { - public class RebasePullRequestCondition - { - public bool CanRebase { get; set; } - public bool CanWrite { get; set; } - public List Vetoes { get; set; } - } -} + public bool CanRebase { get; set; } + public bool CanWrite { get; set; } + public List? Vetoes { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Git/TagTypes.cs b/src/Bitbucket.Net/Models/Git/TagTypes.cs index c73fe4e..c6fa3ea 100644 --- a/src/Bitbucket.Net/Models/Git/TagTypes.cs +++ b/src/Bitbucket.Net/Models/Git/TagTypes.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Git +namespace Bitbucket.Net.Models.Git; + +public enum TagTypes { - public enum TagTypes - { - LightWeight, - Annotated - } -} + LightWeight, + Annotated, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Git/Veto.cs b/src/Bitbucket.Net/Models/Git/Veto.cs index 8c45efc..93d271c 100644 --- a/src/Bitbucket.Net/Models/Git/Veto.cs +++ b/src/Bitbucket.Net/Models/Git/Veto.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Git +namespace Bitbucket.Net.Models.Git; + +public class Veto { - public class Veto - { - public string SummaryMessage { get; set; } - public string DetailedMessage { get; set; } - } + public string? SummaryMessage { get; set; } + public string? DetailedMessage { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Jira/ChangeSet.cs b/src/Bitbucket.Net/Models/Jira/ChangeSet.cs index a71d8cb..e6d86a8 100644 --- a/src/Bitbucket.Net/Models/Jira/ChangeSet.cs +++ b/src/Bitbucket.Net/Models/Jira/ChangeSet.cs @@ -1,13 +1,12 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Models.Jira -{ - public class ChangeSet +namespace Bitbucket.Net.Models.Jira; + +public class ChangeSet { - public CommitParent FromCommit { get; set; } - public Commit ToCommit { get; set; } - public Changes Changes { get; set; } - public Links Links { get; set; } - public Repository Repository { get; set; } - } -} + public CommitParent? FromCommit { get; set; } + public Commit? ToCommit { get; set; } + public Changes? Changes { get; set; } + public Links? Links { get; set; } + public Repository? Repository { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Jira/Changes.cs b/src/Bitbucket.Net/Models/Jira/Changes.cs index b99192b..b931d55 100644 --- a/src/Bitbucket.Net/Models/Jira/Changes.cs +++ b/src/Bitbucket.Net/Models/Jira/Changes.cs @@ -1,14 +1,13 @@ -using System.Collections.Generic; -using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Models.Core.Projects; +using System.Collections.Generic; -namespace Bitbucket.Net.Models.Jira +namespace Bitbucket.Net.Models.Jira; + +public class Changes { - public class Changes - { - public int Size { get; set; } - public int Limit { get; set; } - public bool IsLastPage { get; set; } - public List Values { get; set; } - public int Start { get; set; } - } + public int Size { get; set; } + public int Limit { get; set; } + public bool IsLastPage { get; set; } + public List? Values { get; set; } + public int Start { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Jira/JiraIssue.cs b/src/Bitbucket.Net/Models/Jira/JiraIssue.cs index 644dfc8..405b1ee 100644 --- a/src/Bitbucket.Net/Models/Jira/JiraIssue.cs +++ b/src/Bitbucket.Net/Models/Jira/JiraIssue.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Jira +namespace Bitbucket.Net.Models.Jira; + +public class JiraIssue { - public class JiraIssue - { - public int CommentId { get; set; } - public string IssueKey { get; set; } - } -} + public int CommentId { get; set; } + public string? IssueKey { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessToken.cs b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessToken.cs index c1245c7..82325a8 100644 --- a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessToken.cs +++ b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessToken.cs @@ -1,19 +1,18 @@ -using System; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Users; +using System; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.PersonalAccessTokens +namespace Bitbucket.Net.Models.PersonalAccessTokens; + +public class AccessToken : AccessTokenCreate { - public class AccessToken : AccessTokenCreate - { - public string Id { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset CreatedDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset LastAuthenticated { get; set; } - public User User { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset ExpiryDate { get; set; } - } -} + public string? Id { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset CreatedDate { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset LastAuthenticated { get; set; } + public User? User { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset ExpiryDate { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs index 9c6262d..e81df37 100644 --- a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs +++ b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs @@ -1,14 +1,13 @@ -using System.Collections.Generic; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Admin; +using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.PersonalAccessTokens +namespace Bitbucket.Net.Models.PersonalAccessTokens; + +public class AccessTokenCreate { - public class AccessTokenCreate - { - public string Name { get; set; } - [JsonConverter(typeof(PermissionsConverter))] - public List Permissions { get; set; } - } + public string? Name { get; set; } + [JsonConverter(typeof(PermissionsConverter))] + public List? Permissions { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/PersonalAccessTokens/FullAccessToken.cs b/src/Bitbucket.Net/Models/PersonalAccessTokens/FullAccessToken.cs index 8896556..e851c02 100644 --- a/src/Bitbucket.Net/Models/PersonalAccessTokens/FullAccessToken.cs +++ b/src/Bitbucket.Net/Models/PersonalAccessTokens/FullAccessToken.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.PersonalAccessTokens +namespace Bitbucket.Net.Models.PersonalAccessTokens; + +public class FullAccessToken : AccessToken { - public class FullAccessToken : AccessToken - { - public string Token { get; set; } - } + public string? Token { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefRestrictions/AccessKey.cs b/src/Bitbucket.Net/Models/RefRestrictions/AccessKey.cs index df76380..1c3a694 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/AccessKey.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/AccessKey.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.RefRestrictions +namespace Bitbucket.Net.Models.RefRestrictions; + +public class AccessKey { - public class AccessKey - { - public Key Key { get; set; } - } + public Key? Key { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefRestrictions/Key.cs b/src/Bitbucket.Net/Models/RefRestrictions/Key.cs index b7e7586..1fb6c97 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/Key.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/Key.cs @@ -1,9 +1,8 @@ -namespace Bitbucket.Net.Models.RefRestrictions +namespace Bitbucket.Net.Models.RefRestrictions; + +public class Key { - public class Key - { - public int Id { get; set; } - public string Text { get; set; } - public string Label { get; set; } - } + public int Id { get; set; } + public string? Text { get; set; } + public string? Label { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefRestrictions/RefMatcherTypes.cs b/src/Bitbucket.Net/Models/RefRestrictions/RefMatcherTypes.cs index 30d2eff..12dfe0f 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/RefMatcherTypes.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/RefMatcherTypes.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.RefRestrictions +namespace Bitbucket.Net.Models.RefRestrictions; + +public enum RefMatcherTypes { - public enum RefMatcherTypes - { - Branch, - Pattern, - ModelCategory, - ModelBranch - } -} + Branch, + Pattern, + ModelCategory, + ModelBranch, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefRestrictions/RefRestriction.cs b/src/Bitbucket.Net/Models/RefRestrictions/RefRestriction.cs index c37131b..616c355 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/RefRestriction.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/RefRestriction.cs @@ -1,14 +1,13 @@ -using System.Collections.Generic; -using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Core.Users; +using System.Collections.Generic; -namespace Bitbucket.Net.Models.RefRestrictions +namespace Bitbucket.Net.Models.RefRestrictions; + +public class RefRestriction : RefRestrictionBase { - public class RefRestriction : RefRestrictionBase - { - public int Id { get; set; } - public HookScope Scope { get; set; } - public List Users { get; set; } - public List AccessKeys { get; set; } - } -} + public int Id { get; set; } + public HookScope? Scope { get; set; } + public List? Users { get; set; } + public List? AccessKeys { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionBase.cs b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionBase.cs index 4cb9fb6..92ebada 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionBase.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionBase.cs @@ -1,15 +1,14 @@ -using System.Collections.Generic; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.DefaultReviewers; +using System.Collections.Generic; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.RefRestrictions +namespace Bitbucket.Net.Models.RefRestrictions; + +public abstract class RefRestrictionBase { - public abstract class RefRestrictionBase - { - [JsonConverter(typeof(RefRestrictionTypesConverter))] - public RefRestrictionTypes Type { get; set; } - public RefMatcher Matcher { get; set; } - public List Groups { get; set; } - } + [JsonConverter(typeof(RefRestrictionTypesConverter))] + public RefRestrictionTypes Type { get; set; } + public RefMatcher? Matcher { get; set; } + public List? Groups { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionCreate.cs b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionCreate.cs index d594fc6..beaca41 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionCreate.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionCreate.cs @@ -1,10 +1,9 @@ using System.Collections.Generic; -namespace Bitbucket.Net.Models.RefRestrictions +namespace Bitbucket.Net.Models.RefRestrictions; + +public class RefRestrictionCreate : RefRestrictionBase { - public class RefRestrictionCreate : RefRestrictionBase - { - public List Users { get; set; } - public List AccessKeys { get; set; } - } -} + public List? Users { get; set; } + public List? AccessKeys { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionTypes.cs b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionTypes.cs index 841a602..d8eef60 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionTypes.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionTypes.cs @@ -1,10 +1,9 @@ -namespace Bitbucket.Net.Models.RefRestrictions +namespace Bitbucket.Net.Models.RefRestrictions; + +public enum RefRestrictionTypes { - public enum RefRestrictionTypes - { - AllChanges, - RewritingHistory, - Deletion, - ChangesWithoutPullRequest - } -} + AllChanges, + RewritingHistory, + Deletion, + ChangesWithoutPullRequest, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefSync/FullRef.cs b/src/Bitbucket.Net/Models/RefSync/FullRef.cs index 15f3bc5..82c3c44 100644 --- a/src/Bitbucket.Net/Models/RefSync/FullRef.cs +++ b/src/Bitbucket.Net/Models/RefSync/FullRef.cs @@ -1,10 +1,9 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Models.RefSync +namespace Bitbucket.Net.Models.RefSync; + +public class FullRef : Ref { - public class FullRef : Ref - { - public string State { get; set; } - public bool Tag { get; set; } - } + public string? State { get; set; } + public bool Tag { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefSync/RepositorySynchronizationStatus.cs b/src/Bitbucket.Net/Models/RefSync/RepositorySynchronizationStatus.cs index 8ee4926..d11ce84 100644 --- a/src/Bitbucket.Net/Models/RefSync/RepositorySynchronizationStatus.cs +++ b/src/Bitbucket.Net/Models/RefSync/RepositorySynchronizationStatus.cs @@ -1,18 +1,17 @@ +using Bitbucket.Net.Common.Converters; using System; using System.Collections.Generic; -using Bitbucket.Net.Common.Converters; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.RefSync +namespace Bitbucket.Net.Models.RefSync; + +public class RepositorySynchronizationStatus { - public class RepositorySynchronizationStatus - { - public bool Available { get; set; } - public bool Enabled { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] - public DateTimeOffset LastSync { get; set; } - public List AheadRefs { get; set; } - public List DivergedRefs { get; set; } - public List OrphanedRefs { get; set; } - } -} + public bool Available { get; set; } + public bool Enabled { get; set; } + [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + public DateTimeOffset LastSync { get; set; } + public List? AheadRefs { get; set; } + public List? DivergedRefs { get; set; } + public List? OrphanedRefs { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefSync/Synchronize.cs b/src/Bitbucket.Net/Models/RefSync/Synchronize.cs index 32e04cf..180930f 100644 --- a/src/Bitbucket.Net/Models/RefSync/Synchronize.cs +++ b/src/Bitbucket.Net/Models/RefSync/Synchronize.cs @@ -1,13 +1,12 @@ using Bitbucket.Net.Common.Converters; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.RefSync +namespace Bitbucket.Net.Models.RefSync; + +public class Synchronize { - public class Synchronize - { - public string RefId { get; set; } - [JsonConverter(typeof(SynchronizeActionsConverter))] - public SynchronizeActions Action { get; set; } - public SynchronizeContext Context { get; set; } - } -} + public string? RefId { get; set; } + [JsonConverter(typeof(SynchronizeActionsConverter))] + public SynchronizeActions Action { get; set; } + public SynchronizeContext? Context { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefSync/SynchronizeActions.cs b/src/Bitbucket.Net/Models/RefSync/SynchronizeActions.cs index df83f32..9ed4b3f 100644 --- a/src/Bitbucket.Net/Models/RefSync/SynchronizeActions.cs +++ b/src/Bitbucket.Net/Models/RefSync/SynchronizeActions.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.RefSync +namespace Bitbucket.Net.Models.RefSync; + +public enum SynchronizeActions { - public enum SynchronizeActions - { - Merge, - Discard - } -} + Merge, + Discard, +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/RefSync/SynchronizeContext.cs b/src/Bitbucket.Net/Models/RefSync/SynchronizeContext.cs index 3a2fb5f..2086aa4 100644 --- a/src/Bitbucket.Net/Models/RefSync/SynchronizeContext.cs +++ b/src/Bitbucket.Net/Models/RefSync/SynchronizeContext.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.RefSync +namespace Bitbucket.Net.Models.RefSync; + +public class SynchronizeContext { - public class SynchronizeContext - { - public string CommitMessage { get; set; } - } + public string? CommitMessage { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Ssh/Accesskeys.cs b/src/Bitbucket.Net/Models/Ssh/Accesskeys.cs index d032c0e..f557845 100644 --- a/src/Bitbucket.Net/Models/Ssh/Accesskeys.cs +++ b/src/Bitbucket.Net/Models/Ssh/Accesskeys.cs @@ -1,7 +1,6 @@ -namespace Bitbucket.Net.Models.Ssh +namespace Bitbucket.Net.Models.Ssh; + +public class Accesskeys { - public class Accesskeys - { - public bool Enabled { get; set; } - } + public bool Enabled { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Ssh/Fingerprint.cs b/src/Bitbucket.Net/Models/Ssh/Fingerprint.cs index 8736000..b54158e 100644 --- a/src/Bitbucket.Net/Models/Ssh/Fingerprint.cs +++ b/src/Bitbucket.Net/Models/Ssh/Fingerprint.cs @@ -1,8 +1,7 @@ -namespace Bitbucket.Net.Models.Ssh +namespace Bitbucket.Net.Models.Ssh; + +public class Fingerprint { - public class Fingerprint - { - public string Algorithm { get; set; } - public string Value { get; set; } - } + public string? Algorithm { get; set; } + public string? Value { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Ssh/KeyBase.cs b/src/Bitbucket.Net/Models/Ssh/KeyBase.cs index fdbadf7..e477df5 100644 --- a/src/Bitbucket.Net/Models/Ssh/KeyBase.cs +++ b/src/Bitbucket.Net/Models/Ssh/KeyBase.cs @@ -3,12 +3,11 @@ using Bitbucket.Net.Models.RefRestrictions; using System.Text.Json.Serialization; -namespace Bitbucket.Net.Models.Ssh +namespace Bitbucket.Net.Models.Ssh; + +public abstract class KeyBase { - public abstract class KeyBase - { - public Key Key { get; set; } - [JsonConverter(typeof(PermissionsConverter))] - public Permissions Permission { get; set; } - } + public Key? Key { get; set; } + [JsonConverter(typeof(PermissionsConverter))] + public Permissions Permission { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Ssh/ProjectKey.cs b/src/Bitbucket.Net/Models/Ssh/ProjectKey.cs index 06e07a9..0125d4d 100644 --- a/src/Bitbucket.Net/Models/Ssh/ProjectKey.cs +++ b/src/Bitbucket.Net/Models/Ssh/ProjectKey.cs @@ -1,9 +1,8 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Models.Ssh +namespace Bitbucket.Net.Models.Ssh; + +public class ProjectKey : KeyBase { - public class ProjectKey : KeyBase - { - public Project Project { get; set; } - } -} + public Project? Project { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Ssh/RepositoryKey.cs b/src/Bitbucket.Net/Models/Ssh/RepositoryKey.cs index 4515b21..7d6fb27 100644 --- a/src/Bitbucket.Net/Models/Ssh/RepositoryKey.cs +++ b/src/Bitbucket.Net/Models/Ssh/RepositoryKey.cs @@ -1,9 +1,8 @@ using Bitbucket.Net.Models.Core.Projects; -namespace Bitbucket.Net.Models.Ssh +namespace Bitbucket.Net.Models.Ssh; + +public class RepositoryKey : KeyBase { - public class RepositoryKey : KeyBase - { - public Repository Repository { get; set; } - } + public Repository? Repository { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Ssh/SshSettings.cs b/src/Bitbucket.Net/Models/Ssh/SshSettings.cs index 4b30efa..928cc09 100644 --- a/src/Bitbucket.Net/Models/Ssh/SshSettings.cs +++ b/src/Bitbucket.Net/Models/Ssh/SshSettings.cs @@ -1,11 +1,10 @@ -namespace Bitbucket.Net.Models.Ssh +namespace Bitbucket.Net.Models.Ssh; + +public class SshSettings { - public class SshSettings - { - public Accesskeys AccessKeys { get; set; } - public string BaseUrl { get; set; } - public bool Enabled { get; set; } - public Fingerprint Fingerprint { get; set; } - public int Port { get; set; } - } -} + public Accesskeys? AccessKeys { get; set; } + public string? BaseUrl { get; set; } + public bool Enabled { get; set; } + public Fingerprint? Fingerprint { get; set; } + public int Port { get; set; } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs index a0d1876..89e18c8 100644 --- a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs +++ b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs @@ -1,74 +1,74 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.PersonalAccessTokens; using Flurl.Http; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetPatUrl() => GetBaseUrl("/access-tokens"); + private IFlurlRequest GetPatUrl() => GetBaseUrl("/access-tokens"); - private IFlurlRequest GetPatUrl(string path) => GetPatUrl() - .AppendPathSegment(path); + private IFlurlRequest GetPatUrl(string path) => GetPatUrl() + .AppendPathSegment(path); - public async Task> GetUserAccessTokensAsync(string userSlug, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) + public async Task> GetUserAccessTokensAsync(string userSlug, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize - }; + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetPatUrl($"/users/{userSlug}") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetPatUrl($"/users/{userSlug}") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - public async Task CreateAccessTokenAsync(string userSlug, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) - { - var response = await GetPatUrl($"/users/{userSlug}") - .PutJsonAsync(accessToken, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task CreateAccessTokenAsync(string userSlug, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) + { + var response = await GetPatUrl($"/users/{userSlug}") + .PutJsonAsync(accessToken, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetUserAccessTokenAsync(string userSlug, string tokenId, int? avatarSize = null, CancellationToken cancellationToken = default) - { - return await GetPatUrl($"/users/{userSlug}/{tokenId}") - .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + public async Task GetUserAccessTokenAsync(string userSlug, string tokenId, int? avatarSize = null, CancellationToken cancellationToken = default) + { + return await GetPatUrl($"/users/{userSlug}/{tokenId}") + .SetQueryParam("avatarSize", avatarSize) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - public async Task ChangeUserAccessTokenAsync(string userSlug, string tokenId, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) - { - var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") - .PostJsonAsync(accessToken, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task ChangeUserAccessTokenAsync(string userSlug, string tokenId, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) + { + var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") + .PostJsonAsync(accessToken, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteUserAccessTokenAsync(string userSlug, string tokenId, CancellationToken cancellationToken = default) - { - var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteUserAccessTokenAsync(string userSlug, string tokenId, CancellationToken cancellationToken = default) + { + var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs index aad11a0..5647cf4 100644 --- a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs @@ -1,157 +1,158 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.RefRestrictions; using Flurl.Http; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient + private IFlurlRequest GetRefRestrictionsUrl() => GetBaseUrl("/branch-permissions", "2.0"); + + private IFlurlRequest GetRefRestrictionsUrl(string path) => GetRefRestrictionsUrl() + .AppendPathSegment(path); + + public async Task> GetProjectRefRestrictionsAsync(string projectKey, + RefRestrictionTypes? type = null, + RefMatcherTypes? matcherType = null, + string? matcherId = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) { - private IFlurlRequest GetRefRestrictionsUrl() => GetBaseUrl("/branch-permissions", "2.0"); - - private IFlurlRequest GetRefRestrictionsUrl(string path) => GetRefRestrictionsUrl() - .AppendPathSegment(path); - - public async Task> GetProjectRefRestrictionsAsync(string projectKey, - RefRestrictionTypes? type = null, - RefMatcherTypes? matcherType = null, - string? matcherId = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), - ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), - ["matcherId"] = matcherId, - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task> CreateProjectRefRestrictionsAsync(string projectKey, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") - .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") - .PostJsonAsync(refRestrictions, cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), + ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), + ["matcherId"] = matcherId, + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task> CreateProjectRefRestrictionsAsync(string projectKey, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") + .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") + .PostJsonAsync(refRestrictions, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task> CreateProjectRefRestrictionsAsync(string projectKey, params RefRestrictionCreate[] refRestrictions) - { - return await CreateProjectRefRestrictionsAsync(projectKey, default, refRestrictions).ConfigureAwait(false); - } + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task CreateProjectRefRestrictionAsync(string projectKey, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") - .PostJsonAsync(refRestriction, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> CreateProjectRefRestrictionsAsync(string projectKey, params RefRestrictionCreate[] refRestrictions) + { + return await CreateProjectRefRestrictionsAsync(projectKey, default, refRestrictions).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task CreateProjectRefRestrictionAsync(string projectKey, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") + .PostJsonAsync(refRestriction, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task GetProjectRefRestrictionAsync(string projectKey, int refRestrictionId, int? avatarSize = null, CancellationToken cancellationToken = default) - { - return await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") - .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteProjectRefRestrictionAsync(string projectKey, int refRestrictionId, CancellationToken cancellationToken = default) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - public async Task> GetRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, - RefRestrictionTypes? type = null, - RefMatcherTypes? matcherType = null, - string? matcherId = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), - ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), - ["matcherId"] = matcherId, - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") - .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") - .PostJsonAsync(refRestrictions, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetProjectRefRestrictionAsync(string projectKey, int refRestrictionId, int? avatarSize = null, CancellationToken cancellationToken = default) + { + return await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") + .SetQueryParam("avatarSize", avatarSize) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task DeleteProjectRefRestrictionAsync(string projectKey, int refRestrictionId, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, params RefRestrictionCreate[] refRestrictions) - { - return await CreateRepositoryRefRestrictionsAsync(projectKey, repositorySlug, default, refRestrictions).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task CreateRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) + public async Task> GetRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, + RefRestrictionTypes? type = null, + RefMatcherTypes? matcherType = null, + string? matcherId = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") - .PostJsonAsync(refRestriction, cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), + ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), + ["matcherId"] = matcherId, + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") + .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") + .PostJsonAsync(refRestrictions, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task GetRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, - int? avatarSize = null, CancellationToken cancellationToken = default) - { - return await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") - .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, CancellationToken cancellationToken = default) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, params RefRestrictionCreate[] refRestrictions) + { + return await CreateRepositoryRefRestrictionsAsync(projectKey, repositorySlug, default, refRestrictions).ConfigureAwait(false); + } + + public async Task CreateRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") + .PostJsonAsync(refRestriction, cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + public async Task GetRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, + int? avatarSize = null, CancellationToken cancellationToken = default) + { + return await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") + .SetQueryParam("avatarSize", avatarSize) + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } + + public async Task DeleteRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/RefSync/BitbucketClient.cs b/src/Bitbucket.Net/RefSync/BitbucketClient.cs index 2f385f4..37bb192 100644 --- a/src/Bitbucket.Net/RefSync/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefSync/BitbucketClient.cs @@ -1,50 +1,49 @@ -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Models.RefSync; using Flurl.Http; +using System.Threading; +using System.Threading.Tasks; -namespace Bitbucket.Net +namespace Bitbucket.Net; + +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetRefSyncUrl() => GetBaseUrl("/sync"); + private IFlurlRequest GetRefSyncUrl() => GetBaseUrl("/sync"); - private IFlurlRequest GetRefSyncUrl(string path) => GetRefSyncUrl() - .AppendPathSegment(path); + private IFlurlRequest GetRefSyncUrl(string path) => GetRefSyncUrl() + .AppendPathSegment(path); - public async Task GetRepositorySynchronizationStatusAsync(string projectKey, string repositorySlug, - string? at = null, CancellationToken cancellationToken = default) - { - var response = await GetRefSyncUrl($"/projects/{projectKey}/repos/{repositorySlug}") - .SetQueryParam("at", at) - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetRepositorySynchronizationStatusAsync(string projectKey, string repositorySlug, + string? at = null, CancellationToken cancellationToken = default) + { + var response = await GetRefSyncUrl($"/projects/{projectKey}/repos/{repositorySlug}") + .SetQueryParam("at", at) + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task EnableRepositorySynchronizationAsync(string projectKey, string repositorySlug, bool enabled, CancellationToken cancellationToken = default) + public async Task EnableRepositorySynchronizationAsync(string projectKey, string repositorySlug, bool enabled, CancellationToken cancellationToken = default) + { + var data = new { - var data = new - { - enabled = BitbucketHelpers.BoolToString(enabled) - }; + enabled = BitbucketHelpers.BoolToString(enabled), + }; - var response = await GetRefSyncUrl($"/projects/{projectKey}/repos/{repositorySlug}") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetRefSyncUrl($"/projects/{projectKey}/repos/{repositorySlug}") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task SynchronizeRepositoryAsync(string projectKey, string repositorySlug, Synchronize synchronize, CancellationToken cancellationToken = default) - { - var response = await GetRefSyncUrl($"/projects/{projectKey}/repos/{repositorySlug}") - .PostJsonAsync(synchronize, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task SynchronizeRepositoryAsync(string projectKey, string repositorySlug, Synchronize synchronize, CancellationToken cancellationToken = default) + { + var response = await GetRefSyncUrl($"/projects/{projectKey}/repos/{repositorySlug}") + .PostJsonAsync(synchronize, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Serialization/BitbucketJsonContext.cs b/src/Bitbucket.Net/Serialization/BitbucketJsonContext.cs index 27d3444..eb6249c 100644 --- a/src/Bitbucket.Net/Serialization/BitbucketJsonContext.cs +++ b/src/Bitbucket.Net/Serialization/BitbucketJsonContext.cs @@ -1,52 +1,36 @@ -using System.Collections.Generic; -using System.Text.Json.Serialization; - using Bitbucket.Net.Common.Models; - // Audit using Bitbucket.Net.Models.Audit; - // Branches using Bitbucket.Net.Models.Branches; - // Builds using Bitbucket.Net.Models.Builds; - // Core - Admin using Bitbucket.Net.Models.Core.Admin; - // Core - Logs using Bitbucket.Net.Models.Core.Logs; - // Core - Projects using Bitbucket.Net.Models.Core.Projects; - // Core - Tasks using Bitbucket.Net.Models.Core.Tasks; - // Core - Users using Bitbucket.Net.Models.Core.Users; - // DefaultReviewers using Bitbucket.Net.Models.DefaultReviewers; - // Git using Bitbucket.Net.Models.Git; - // Jira using Bitbucket.Net.Models.Jira; - // PersonalAccessTokens using Bitbucket.Net.Models.PersonalAccessTokens; - // RefRestrictions using Bitbucket.Net.Models.RefRestrictions; - // RefSync using Bitbucket.Net.Models.RefSync; - // Ssh using Bitbucket.Net.Models.Ssh; +using System.Collections.Generic; +using System.Text.Json.Serialization; namespace Bitbucket.Net.Serialization; @@ -342,4 +326,4 @@ namespace Bitbucket.Net.Serialization; /// public partial class BitbucketJsonContext : JsonSerializerContext { -} +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Ssh/BitbucketClient.cs b/src/Bitbucket.Net/Ssh/BitbucketClient.cs index d51b9a1..c12d25d 100644 --- a/src/Bitbucket.Net/Ssh/BitbucketClient.cs +++ b/src/Bitbucket.Net/Ssh/BitbucketClient.cs @@ -1,275 +1,279 @@ -using System.Collections.Generic; -using System.Net.Http; -using System.Text; -using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.RefRestrictions; using Bitbucket.Net.Models.Ssh; using Flurl.Http; +using System.Collections.Generic; +using System.Net.Http; +using System.Text; +using System.Text.Json; +using System.Threading; +using System.Threading.Tasks; + +namespace Bitbucket.Net; -namespace Bitbucket.Net +public partial class BitbucketClient { - public partial class BitbucketClient - { - private IFlurlRequest GetKeysUrl() => GetBaseUrl("/keys"); + private IFlurlRequest GetKeysUrl() => GetBaseUrl("/keys"); - private IFlurlRequest GetKeysUrl(string path) => GetKeysUrl() - .AppendPathSegment(path); + private IFlurlRequest GetKeysUrl(string path) => GetKeysUrl() + .AppendPathSegment(path); - private IFlurlRequest GetSshUrl() => GetBaseUrl("/ssh"); + private IFlurlRequest GetSshUrl() => GetBaseUrl("/ssh"); - private IFlurlRequest GetSshUrl(string path) => GetSshUrl() - .AppendPathSegment(path); + private IFlurlRequest GetSshUrl(string path) => GetSshUrl() + .AppendPathSegment(path); - public async Task DeleteProjectsReposKeysAsync(int keyId, CancellationToken cancellationToken, params string[] projectsOrRepos) - { - var json = JsonSerializer.Serialize(projectsOrRepos); - var response = await GetKeysUrl($"/ssh/{keyId}") - .WithHeader("Content-Type", "application/json") - .SendAsync(HttpMethod.Delete, new StringContent(json, Encoding.UTF8, "application/json"), cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteProjectsReposKeysAsync(int keyId, CancellationToken cancellationToken, params string[] projectsOrRepos) + { + var json = JsonSerializer.Serialize(projectsOrRepos); + var response = await GetKeysUrl($"/ssh/{keyId}") + .WithHeader("Content-Type", "application/json") + .SendAsync(HttpMethod.Delete, new StringContent(json, Encoding.UTF8, "application/json"), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task DeleteProjectsReposKeysAsync(int keyId, params string[] projectsOrRepos) - { - return await DeleteProjectsReposKeysAsync(keyId, default, projectsOrRepos).ConfigureAwait(false); - } - - public async Task> GetProjectKeysAsync(int keyId, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task DeleteProjectsReposKeysAsync(int keyId, params string[] projectsOrRepos) + { + return await DeleteProjectsReposKeysAsync(keyId, default, projectsOrRepos).ConfigureAwait(false); + } + + public async Task> GetProjectKeysAsync(int keyId, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetKeysUrl($"/ssh/{keyId}/projects") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task> GetProjectKeysAsync(string projectKey, - string? filter = null, - Permissions? permission = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + ["limit"] = limit, + ["start"] = start, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetKeysUrl($"/ssh/{keyId}/projects") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task> GetProjectKeysAsync(string projectKey, + string? filter = null, + Permissions? permission = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - ["permission"] = BitbucketHelpers.PermissionToString(permission) - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetKeysUrl($"/projects/{projectKey}/ssh") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task CreateProjectKeyAsync(string projectKey, string keyText, Permissions permission, CancellationToken cancellationToken = default) + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetKeysUrl($"/projects/{projectKey}/ssh") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task CreateProjectKeyAsync(string projectKey, string keyText, Permissions permission, CancellationToken cancellationToken = default) + { + var data = new { - var data = new - { - key = new { text = keyText }, - permission = BitbucketHelpers.PermissionToString(permission) - }; + key = new { text = keyText }, + permission = BitbucketHelpers.PermissionToString(permission), + }; - var response = await GetKeysUrl($"/projects/{projectKey}/ssh") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetKeysUrl($"/projects/{projectKey}/ssh") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetProjectKeyAsync(string projectKey, int keyId, CancellationToken cancellationToken = default) - { - var response = await GetKeysUrl($"/projects/{projectKey}/ssh/{keyId}") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetProjectKeyAsync(string projectKey, int keyId, CancellationToken cancellationToken = default) + { + var response = await GetKeysUrl($"/projects/{projectKey}/ssh/{keyId}") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteProjectKeyAsync(string projectKey, int keyId, CancellationToken cancellationToken = default) - { - var response = await GetKeysUrl($"/projects/{projectKey}/ssh/{keyId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteProjectKeyAsync(string projectKey, int keyId, CancellationToken cancellationToken = default) + { + var response = await GetKeysUrl($"/projects/{projectKey}/ssh/{keyId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task UpdateProjectKeyPermissionAsync(string projectKey, int keyId, Permissions permission, CancellationToken cancellationToken = default) - { - var response = await GetKeysUrl($"/projects/{projectKey}/ssh/{keyId}/permissions/{BitbucketHelpers.PermissionToString(permission)}") - .PutAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - public async Task> GetRepoKeysAsync(int keyId, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + public async Task UpdateProjectKeyPermissionAsync(string projectKey, int keyId, Permissions permission, CancellationToken cancellationToken = default) + { + var response = await GetKeysUrl($"/projects/{projectKey}/ssh/{keyId}/permissions/{BitbucketHelpers.PermissionToString(permission)}") + .PutAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + public async Task> GetRepoKeysAsync(int keyId, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetKeysUrl($"/ssh/{keyId}/repos") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task> GetRepoKeysAsync(string projectKey, string repositorySlug, - string? filter = null, - bool? effective = null, - Permissions? permission = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) + ["limit"] = limit, + ["start"] = start, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetKeysUrl($"/ssh/{keyId}/repos") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task> GetRepoKeysAsync(string projectKey, string repositorySlug, + string? filter = null, + bool? effective = null, + Permissions? permission = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - ["effective"] = BitbucketHelpers.BoolToString(effective), - ["permission"] = BitbucketHelpers.PermissionToString(permission) - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task CreateRepoKeyAsync(string projectKey, string repositorySlug, string keyText, Permissions permission, CancellationToken cancellationToken = default) + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + ["effective"] = BitbucketHelpers.BoolToString(effective), + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } + + public async Task CreateRepoKeyAsync(string projectKey, string repositorySlug, string keyText, Permissions permission, CancellationToken cancellationToken = default) + { + var data = new { - var data = new - { - key = new { text = keyText }, - permission = BitbucketHelpers.PermissionToString(permission) - }; + key = new { text = keyText }, + permission = BitbucketHelpers.PermissionToString(permission), + }; - var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh") - .PostJsonAsync(data, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh") + .PostJsonAsync(data, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task GetRepoKeyAsync(string projectKey, string repositorySlug, int keyId, CancellationToken cancellationToken = default) - { - var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh/{keyId}") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task GetRepoKeyAsync(string projectKey, string repositorySlug, int keyId, CancellationToken cancellationToken = default) + { + var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh/{keyId}") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteRepoKeyAsync(string projectKey, string repositorySlug, int keyId, CancellationToken cancellationToken = default) - { - var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh/{keyId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task DeleteRepoKeyAsync(string projectKey, string repositorySlug, int keyId, CancellationToken cancellationToken = default) + { + var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh/{keyId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } - public async Task UpdateRepoKeyPermissionAsync(string projectKey, string repositorySlug, int keyId, Permissions permission, CancellationToken cancellationToken = default) - { - var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh/{keyId}/permissions/{BitbucketHelpers.PermissionToString(permission)}") - .PutAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - public async Task> GetUserKeysAsync(string? userSlug = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary - { - ["limit"] = limit, - ["start"] = start, - ["user"] = userSlug - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetSshUrl("/keys") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) - .ConfigureAwait(false); - } - - public async Task CreateUserKeyAsync(string keyText, string? userSlug = null, CancellationToken cancellationToken = default) - { - var response = await GetSshUrl("/keys") - .SetQueryParam("user", userSlug) - .PostJsonAsync(new { text = keyText }, cancellationToken: cancellationToken) - .ConfigureAwait(false); + public async Task UpdateRepoKeyPermissionAsync(string projectKey, string repositorySlug, int keyId, Permissions permission, CancellationToken cancellationToken = default) + { + var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh/{keyId}/permissions/{BitbucketHelpers.PermissionToString(permission)}") + .PutAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - public async Task DeleteUserKeysAsync(string? userSlug = null, CancellationToken cancellationToken = default) + public async Task> GetUserKeysAsync(string? userSlug = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(System.StringComparer.Ordinal) { - var response = await GetSshUrl("/keys") - .SetQueryParam("user", userSlug) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + ["limit"] = limit, + ["start"] = start, + ["user"] = userSlug, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + await GetSshUrl("/keys") + .SetQueryParams(qpv) + .GetJsonAsync>(cancellationToken: ct) + .ConfigureAwait(false), cancellationToken) + .ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task CreateUserKeyAsync(string keyText, string? userSlug = null, CancellationToken cancellationToken = default) + { + var response = await GetSshUrl("/keys") + .SetQueryParam("user", userSlug) + .PostJsonAsync(new { text = keyText }, cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task DeleteUserKeyAsync(int keyId, CancellationToken cancellationToken = default) - { - var response = await GetSshUrl($"/keys/{keyId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } + public async Task DeleteUserKeysAsync(string? userSlug = null, CancellationToken cancellationToken = default) + { + var response = await GetSshUrl("/keys") + .SetQueryParam("user", userSlug) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); - public async Task GetSshSettingsAsync(CancellationToken cancellationToken = default) - { - return await GetSshUrl("/settings") - .GetJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - } + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + public async Task DeleteUserKeyAsync(int keyId, CancellationToken cancellationToken = default) + { + var response = await GetSshUrl($"/keys/{keyId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + public async Task GetSshSettingsAsync(CancellationToken cancellationToken = default) + { + return await GetSshUrl("/settings") + .GetJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Common/Mcp/DiffStreamingExtensionsTests.cs b/test/Bitbucket.Net.Tests/Common/Mcp/DiffStreamingExtensionsTests.cs index 08a88c8..0558fde 100644 --- a/test/Bitbucket.Net.Tests/Common/Mcp/DiffStreamingExtensionsTests.cs +++ b/test/Bitbucket.Net.Tests/Common/Mcp/DiffStreamingExtensionsTests.cs @@ -1,283 +1,282 @@ +using Bitbucket.Net.Common.Mcp; +using Bitbucket.Net.Models.Core.Projects; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common.Mcp; -using Bitbucket.Net.Models.Core.Projects; using Xunit; -namespace Bitbucket.Net.Tests.Common.Mcp +namespace Bitbucket.Net.Tests.Common.Mcp; + +public class DiffStreamingExtensionsTests { - public class DiffStreamingExtensionsTests + #region CountDiffLines Tests + + [Fact] + public void CountDiffLines_WithNullHunks_ReturnsZero() { - #region CountDiffLines Tests + // Arrange + var diff = new Diff { Hunks = null }; - [Fact] - public void CountDiffLines_WithNullHunks_ReturnsZero() - { - // Arrange - var diff = new Diff { Hunks = null }; + // Act + var count = DiffStreamingExtensions.CountDiffLines(diff); - // Act - var count = DiffStreamingExtensions.CountDiffLines(diff); + // Assert + Assert.Equal(0, count); + } - // Assert - Assert.Equal(0, count); - } + [Fact] + public void CountDiffLines_WithEmptyHunks_ReturnsZero() + { + // Arrange + var diff = new Diff { Hunks = [] }; - [Fact] - public void CountDiffLines_WithEmptyHunks_ReturnsZero() - { - // Arrange - var diff = new Diff { Hunks = [] }; + // Act + var count = DiffStreamingExtensions.CountDiffLines(diff); - // Act - var count = DiffStreamingExtensions.CountDiffLines(diff); + // Assert + Assert.Equal(0, count); + } - // Assert - Assert.Equal(0, count); - } + [Fact] + public void CountDiffLines_WithMultipleHunksAndSegments_CountsAllLines() + { + // Arrange + var diff = CreateDiff(hunks: 2, segmentsPerHunk: 3, linesPerSegment: 5); - [Fact] - public void CountDiffLines_WithMultipleHunksAndSegments_CountsAllLines() - { - // Arrange - var diff = CreateDiff(hunks: 2, segmentsPerHunk: 3, linesPerSegment: 5); + // Act + var count = DiffStreamingExtensions.CountDiffLines(diff); - // Act - var count = DiffStreamingExtensions.CountDiffLines(diff); + // Assert + Assert.Equal(30, count); // 2 * 3 * 5 = 30 + } - // Assert - Assert.Equal(30, count); // 2 * 3 * 5 = 30 - } + #endregion - #endregion + #region TakeDiffsWithLimitsAsync Tests - #region TakeDiffsWithLimitsAsync Tests + [Fact] + public async Task TakeDiffsWithLimitsAsync_WithNoLimits_ReturnsAllDiffs() + { + // Arrange + var diffs = CreateAsyncDiffs(5, linesPerDiff: 10); + + // Act + var result = await diffs.TakeDiffsWithLimitsAsync(); + + // Assert + Assert.Equal(5, result.Diffs.Count); + Assert.Equal(50, result.TotalLines); + Assert.Equal(5, result.TotalFiles); + Assert.False(result.WasTruncated); + Assert.Null(result.TruncationReason); + } - [Fact] - public async Task TakeDiffsWithLimitsAsync_WithNoLimits_ReturnsAllDiffs() - { - // Arrange - var diffs = CreateAsyncDiffs(5, linesPerDiff: 10); - - // Act - var result = await diffs.TakeDiffsWithLimitsAsync(); - - // Assert - Assert.Equal(5, result.Diffs.Count); - Assert.Equal(50, result.TotalLines); - Assert.Equal(5, result.TotalFiles); - Assert.False(result.WasTruncated); - Assert.Null(result.TruncationReason); - } + [Fact] + public async Task TakeDiffsWithLimitsAsync_WithMaxFiles_TruncatesAtFileLimit() + { + // Arrange + var diffs = CreateAsyncDiffs(10, linesPerDiff: 5); + + // Act + var result = await diffs.TakeDiffsWithLimitsAsync(maxFiles: 3); + + // Assert + Assert.Equal(3, result.Diffs.Count); + Assert.Equal(3, result.TotalFiles); + Assert.True(result.WasTruncated); + Assert.Equal("max_files_reached", result.TruncationReason); + Assert.True(result.HasMore); + } - [Fact] - public async Task TakeDiffsWithLimitsAsync_WithMaxFiles_TruncatesAtFileLimit() - { - // Arrange - var diffs = CreateAsyncDiffs(10, linesPerDiff: 5); - - // Act - var result = await diffs.TakeDiffsWithLimitsAsync(maxFiles: 3); - - // Assert - Assert.Equal(3, result.Diffs.Count); - Assert.Equal(3, result.TotalFiles); - Assert.True(result.WasTruncated); - Assert.Equal("max_files_reached", result.TruncationReason); - Assert.True(result.HasMore); - } + [Fact] + public async Task TakeDiffsWithLimitsAsync_WithMaxLines_TruncatesAtLineLimit() + { + // Arrange + var diffs = CreateAsyncDiffs(5, linesPerDiff: 20); - [Fact] - public async Task TakeDiffsWithLimitsAsync_WithMaxLines_TruncatesAtLineLimit() - { - // Arrange - var diffs = CreateAsyncDiffs(5, linesPerDiff: 20); + // Act + var result = await diffs.TakeDiffsWithLimitsAsync(maxLines: 35); - // Act - var result = await diffs.TakeDiffsWithLimitsAsync(maxLines: 35); + // Assert + Assert.True(result.TotalLines <= 35); + Assert.True(result.WasTruncated); + Assert.Equal("max_lines_reached", result.TruncationReason); + } - // Assert - Assert.True(result.TotalLines <= 35); - Assert.True(result.WasTruncated); - Assert.Equal("max_lines_reached", result.TruncationReason); - } + [Fact] + public async Task TakeDiffsWithLimitsAsync_WithBothLimits_RespectsFirstHit() + { + // Arrange - 10 diffs with 20 lines each = 200 total lines + var diffs = CreateAsyncDiffs(10, linesPerDiff: 20); - [Fact] - public async Task TakeDiffsWithLimitsAsync_WithBothLimits_RespectsFirstHit() - { - // Arrange - 10 diffs with 20 lines each = 200 total lines - var diffs = CreateAsyncDiffs(10, linesPerDiff: 20); + // Act - max 3 files OR max 100 lines (file limit should hit first) + var result = await diffs.TakeDiffsWithLimitsAsync(maxLines: 100, maxFiles: 3); - // Act - max 3 files OR max 100 lines (file limit should hit first) - var result = await diffs.TakeDiffsWithLimitsAsync(maxLines: 100, maxFiles: 3); + // Assert + Assert.Equal(3, result.TotalFiles); + Assert.Equal("max_files_reached", result.TruncationReason); + } - // Assert - Assert.Equal(3, result.TotalFiles); - Assert.Equal("max_files_reached", result.TruncationReason); - } + [Fact] + public async Task TakeDiffsWithLimitsAsync_SupportsDeconstruction() + { + // Arrange + var diffs = CreateAsyncDiffs(3, linesPerDiff: 10); - [Fact] - public async Task TakeDiffsWithLimitsAsync_SupportsDeconstruction() - { - // Arrange - var diffs = CreateAsyncDiffs(3, linesPerDiff: 10); + // Act + var (diffList, hasMore, totalLines, totalFiles) = await diffs.TakeDiffsWithLimitsAsync(maxFiles: 2); - // Act - var (diffList, hasMore, totalLines, totalFiles) = await diffs.TakeDiffsWithLimitsAsync(maxFiles: 2); + // Assert + Assert.Equal(2, diffList.Count); + Assert.True(hasMore); + Assert.Equal(20, totalLines); + Assert.Equal(2, totalFiles); + } - // Assert - Assert.Equal(2, diffList.Count); - Assert.True(hasMore); - Assert.Equal(20, totalLines); - Assert.Equal(2, totalFiles); - } + #endregion - #endregion + #region StreamDiffsWithLimitsAsync Tests - #region StreamDiffsWithLimitsAsync Tests + [Fact] + public async Task StreamDiffsWithLimitsAsync_WithNoLimits_YieldsAllDiffs() + { + // Arrange + var diffs = CreateAsyncDiffs(5, linesPerDiff: 10); - [Fact] - public async Task StreamDiffsWithLimitsAsync_WithNoLimits_YieldsAllDiffs() + // Act + var results = new List(); + await foreach (var result in diffs.StreamDiffsWithLimitsAsync()) { - // Arrange - var diffs = CreateAsyncDiffs(5, linesPerDiff: 10); + results.Add(result); + } - // Act - var results = new List(); - await foreach (var result in diffs.StreamDiffsWithLimitsAsync()) - { - results.Add(result); - } + // Assert + Assert.Equal(5, results.Count); + Assert.All(results, r => Assert.NotNull(r.Diff)); + Assert.All(results, r => Assert.False(r.IsTruncated)); + } - // Assert - Assert.Equal(5, results.Count); - Assert.All(results, r => Assert.NotNull(r.Diff)); - Assert.All(results, r => Assert.False(r.IsTruncated)); - } + [Fact] + public async Task StreamDiffsWithLimitsAsync_WithMaxFiles_YieldsTruncationMarker() + { + // Arrange + var diffs = CreateAsyncDiffs(10, linesPerDiff: 5); - [Fact] - public async Task StreamDiffsWithLimitsAsync_WithMaxFiles_YieldsTruncationMarker() + // Act + var results = new List(); + await foreach (var result in diffs.StreamDiffsWithLimitsAsync(maxFiles: 3)) { - // Arrange - var diffs = CreateAsyncDiffs(10, linesPerDiff: 5); + results.Add(result); + } - // Act - var results = new List(); - await foreach (var result in diffs.StreamDiffsWithLimitsAsync(maxFiles: 3)) - { - results.Add(result); - } + // Assert + Assert.Equal(4, results.Count); // 3 diffs + 1 truncation marker + Assert.True(results.Last().IsTruncated); + Assert.Equal("max_files_reached", results.Last().TruncationReason); + } - // Assert - Assert.Equal(4, results.Count); // 3 diffs + 1 truncation marker - Assert.True(results.Last().IsTruncated); - Assert.Equal("max_files_reached", results.Last().TruncationReason); - } + [Fact] + public async Task StreamDiffsWithLimitsAsync_TracksRunningTotals() + { + // Arrange - 3 diffs with 10 lines each + var diffs = CreateAsyncDiffs(3, linesPerDiff: 10); - [Fact] - public async Task StreamDiffsWithLimitsAsync_TracksRunningTotals() + // Act + var results = new List(); + await foreach (var result in diffs.StreamDiffsWithLimitsAsync()) { - // Arrange - 3 diffs with 10 lines each - var diffs = CreateAsyncDiffs(3, linesPerDiff: 10); + results.Add(result); + } - // Act - var results = new List(); - await foreach (var result in diffs.StreamDiffsWithLimitsAsync()) - { - results.Add(result); - } + // Assert + Assert.Equal(10, results[0].TotalLines); + Assert.Equal(1, results[0].TotalFiles); - // Assert - Assert.Equal(10, results[0].TotalLines); - Assert.Equal(1, results[0].TotalFiles); + Assert.Equal(20, results[1].TotalLines); + Assert.Equal(2, results[1].TotalFiles); - Assert.Equal(20, results[1].TotalLines); - Assert.Equal(2, results[1].TotalFiles); + Assert.Equal(30, results[2].TotalLines); + Assert.Equal(3, results[2].TotalFiles); + } - Assert.Equal(30, results[2].TotalLines); - Assert.Equal(3, results[2].TotalFiles); - } + #endregion - #endregion + #region Cancellation Tests - #region Cancellation Tests + [Fact] + public async Task TakeDiffsWithLimitsAsync_RespectsCancellation() + { + // Arrange + using var cts = new CancellationTokenSource(); + var diffs = CreateCancellableAsyncDiffs(100, linesPerDiff: 10, cts.Token); - [Fact] - public async Task TakeDiffsWithLimitsAsync_RespectsCancellation() + // Cancel after a short delay + _ = Task.Run(async () => { - // Arrange - using var cts = new CancellationTokenSource(); - var diffs = CreateCancellableAsyncDiffs(100, linesPerDiff: 10, cts.Token); - - // Cancel after a short delay - _ = Task.Run(async () => - { - await Task.Delay(10); - cts.Cancel(); - }); + await Task.Delay(10); + cts.Cancel(); + }); - // Act & Assert - TaskCanceledException inherits from OperationCanceledException - await Assert.ThrowsAnyAsync( - () => diffs.TakeDiffsWithLimitsAsync(cancellationToken: cts.Token)); - } + // Act & Assert - TaskCanceledException inherits from OperationCanceledException + await Assert.ThrowsAnyAsync( + () => diffs.TakeDiffsWithLimitsAsync(cancellationToken: cts.Token)); + } - #endregion + #endregion - #region Helper Methods + #region Helper Methods - private static Diff CreateDiff(int hunks = 1, int segmentsPerHunk = 1, int linesPerSegment = 10) + private static Diff CreateDiff(int hunks = 1, int segmentsPerHunk = 1, int linesPerSegment = 10) + { + return new Diff { - return new Diff + Source = new Path { toString = "source.cs" }, + Destination = new Path { toString = "dest.cs" }, + Hunks = [.. Enumerable.Range(0, hunks).Select(_ => new DiffHunk { - Source = new Path { toString = "source.cs" }, - Destination = new Path { toString = "dest.cs" }, - Hunks = Enumerable.Range(0, hunks).Select(_ => new DiffHunk + SourceLine = 1, + SourceSpan = linesPerSegment * segmentsPerHunk, + DestinationLine = 1, + DestinationSpan = linesPerSegment * segmentsPerHunk, + Segments = [.. Enumerable.Range(0, segmentsPerHunk).Select(_ => new Segment { - SourceLine = 1, - SourceSpan = linesPerSegment * segmentsPerHunk, - DestinationLine = 1, - DestinationSpan = linesPerSegment * segmentsPerHunk, - Segments = Enumerable.Range(0, segmentsPerHunk).Select(_ => new Segment + Type = "CONTEXT", + Lines = [.. Enumerable.Range(0, linesPerSegment).Select(i => new LineRef { - Type = "CONTEXT", - Lines = Enumerable.Range(0, linesPerSegment).Select(i => new LineRef - { - Source = i + 1, - Destination = i + 1, - Line = $"Line {i + 1}" - }).ToList() - }).ToList() - }).ToList() - }; - } + Source = i + 1, + Destination = i + 1, + Line = $"Line {i + 1}" + })] + })] + })] + }; + } - private static async IAsyncEnumerable CreateAsyncDiffs(int count, int linesPerDiff) + private static async IAsyncEnumerable CreateAsyncDiffs(int count, int linesPerDiff) + { + // Calculate how to distribute lines: single hunk, single segment + for (int i = 0; i < count; i++) { - // Calculate how to distribute lines: single hunk, single segment - for (int i = 0; i < count; i++) - { - await Task.Yield(); - yield return CreateDiff(hunks: 1, segmentsPerHunk: 1, linesPerSegment: linesPerDiff); - } + await Task.Yield(); + yield return CreateDiff(hunks: 1, segmentsPerHunk: 1, linesPerSegment: linesPerDiff); } + } - private static async IAsyncEnumerable CreateCancellableAsyncDiffs( - int count, - int linesPerDiff, - [EnumeratorCancellation] CancellationToken cancellationToken = default) + private static async IAsyncEnumerable CreateCancellableAsyncDiffs( + int count, + int linesPerDiff, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - await Task.Delay(5, cancellationToken); - yield return CreateDiff(hunks: 1, segmentsPerHunk: 1, linesPerSegment: linesPerDiff); - } + cancellationToken.ThrowIfCancellationRequested(); + await Task.Delay(5, cancellationToken); + yield return CreateDiff(hunks: 1, segmentsPerHunk: 1, linesPerSegment: linesPerDiff); } - - #endregion } -} + + #endregion +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs b/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs index 2e26821..db28e61 100644 --- a/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs +++ b/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs @@ -1,339 +1,342 @@ +using Bitbucket.Net.Common.Mcp; using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common.Mcp; using Xunit; -namespace Bitbucket.Net.Tests.Common.Mcp +namespace Bitbucket.Net.Tests.Common.Mcp; + +public class McpExtensionsTests { - public class McpExtensionsTests + #region TakeWithPaginationAsync Tests + + [Fact] + public async Task TakeWithPaginationAsync_WithFewerItemsThanLimit_ReturnsAllItems() { - #region TakeWithPaginationAsync Tests + // Arrange + var source = CreateAsyncEnumerable(5); - [Fact] - public async Task TakeWithPaginationAsync_WithFewerItemsThanLimit_ReturnsAllItems() - { - // Arrange - var source = CreateAsyncEnumerable(5); + // Act + var result = await source.TakeWithPaginationAsync(10); - // Act - var result = await source.TakeWithPaginationAsync(10); + // Assert + Assert.Equal(5, result.Items.Count); + Assert.False(result.HasMore); + Assert.Null(result.NextOffset); + } - // Assert - Assert.Equal(5, result.Items.Count); - Assert.False(result.HasMore); - Assert.Null(result.NextOffset); - } + [Fact] + public async Task TakeWithPaginationAsync_WithExactlyLimitItems_ReturnsAllItemsNoMore() + { + // Arrange + var source = CreateAsyncEnumerable(10); - [Fact] - public async Task TakeWithPaginationAsync_WithExactlyLimitItems_ReturnsAllItemsNoMore() - { - // Arrange - var source = CreateAsyncEnumerable(10); + // Act + var result = await source.TakeWithPaginationAsync(10); - // Act - var result = await source.TakeWithPaginationAsync(10); + // Assert + Assert.Equal(10, result.Items.Count); + Assert.False(result.HasMore); + Assert.Null(result.NextOffset); + } - // Assert - Assert.Equal(10, result.Items.Count); - Assert.False(result.HasMore); - Assert.Null(result.NextOffset); - } + [Fact] + public async Task TakeWithPaginationAsync_WithMoreItemsThanLimit_ReturnsLimitWithHasMore() + { + // Arrange + var source = CreateAsyncEnumerable(20); - [Fact] - public async Task TakeWithPaginationAsync_WithMoreItemsThanLimit_ReturnsLimitWithHasMore() - { - // Arrange - var source = CreateAsyncEnumerable(20); + // Act + var result = await source.TakeWithPaginationAsync(10); - // Act - var result = await source.TakeWithPaginationAsync(10); + // Assert + Assert.Equal(10, result.Items.Count); + Assert.True(result.HasMore); + Assert.Equal(10, result.NextOffset); + } - // Assert - Assert.Equal(10, result.Items.Count); - Assert.True(result.HasMore); - Assert.Equal(10, result.NextOffset); - } + [Fact] + public async Task TakeWithPaginationAsync_WithEmptySource_ReturnsEmptyResult() + { + // Arrange + var source = CreateAsyncEnumerable(0); - [Fact] - public async Task TakeWithPaginationAsync_WithEmptySource_ReturnsEmptyResult() - { - // Arrange - var source = CreateAsyncEnumerable(0); + // Act + var result = await source.TakeWithPaginationAsync(10); - // Act - var result = await source.TakeWithPaginationAsync(10); + // Assert + Assert.Empty(result.Items); + Assert.False(result.HasMore); + Assert.Null(result.NextOffset); + } - // Assert - Assert.Empty(result.Items); - Assert.False(result.HasMore); - Assert.Null(result.NextOffset); - } + [Fact] + public async Task TakeWithPaginationAsync_SupportsDeconstruction() + { + // Arrange + var source = CreateAsyncEnumerable(15); - [Fact] - public async Task TakeWithPaginationAsync_SupportsDeconstruction() - { - // Arrange - var source = CreateAsyncEnumerable(15); + // Act + var (items, hasMore, nextOffset) = await source.TakeWithPaginationAsync(10); - // Act - var (items, hasMore, nextOffset) = await source.TakeWithPaginationAsync(10); + // Assert + Assert.Equal(10, items.Count); + Assert.True(hasMore); + Assert.Equal(10, nextOffset); + } - // Assert - Assert.Equal(10, items.Count); - Assert.True(hasMore); - Assert.Equal(10, nextOffset); - } + private static readonly int[] expected = new[] { 0, 1, 2, 3, 4 }; - [Fact] - public async Task TakeWithPaginationAsync_RespectsItemOrder() - { - // Arrange - var source = CreateAsyncEnumerable(5); + [Fact] + public async Task TakeWithPaginationAsync_RespectsItemOrder() + { + // Arrange + var source = CreateAsyncEnumerable(5); - // Act - var result = await source.TakeWithPaginationAsync(5); + // Act + var result = await source.TakeWithPaginationAsync(5); - // Assert - Assert.Equal(new[] { 0, 1, 2, 3, 4 }, result.Items); - } + // Assert + Assert.Equal(expected, result.Items); + } - #endregion + #endregion - #region TakeAsync Tests + #region TakeAsync Tests - [Fact] - public async Task TakeAsync_WithFewerItemsThanLimit_ReturnsAllItems() - { - // Arrange - var source = CreateAsyncEnumerable(5); + [Fact] + public async Task TakeAsync_WithFewerItemsThanLimit_ReturnsAllItems() + { + // Arrange + var source = CreateAsyncEnumerable(5); - // Act - var result = await source.TakeAsync(10).ToListAsync(); + // Act + var result = await source.TakeAsync(10).ToListAsync(); - // Assert - Assert.Equal(5, result.Count); - } + // Assert + Assert.Equal(5, result.Count); + } - [Fact] - public async Task TakeAsync_WithMoreItemsThanLimit_ReturnsExactlyLimit() - { - // Arrange - var source = CreateAsyncEnumerable(100); + [Fact] + public async Task TakeAsync_WithMoreItemsThanLimit_ReturnsExactlyLimit() + { + // Arrange + var source = CreateAsyncEnumerable(100); - // Act - var result = await source.TakeAsync(10).ToListAsync(); + // Act + var result = await source.TakeAsync(10).ToListAsync(); - // Assert - Assert.Equal(10, result.Count); - Assert.Equal(Enumerable.Range(0, 10).ToList(), result); - } + // Assert + Assert.Equal(10, result.Count); + Assert.Equal([.. Enumerable.Range(0, 10)], result); + } - [Fact] - public async Task TakeAsync_StopsEnumerationAfterLimit() - { - // Arrange - int itemsGenerated = 0; - var source = CreateCountingAsyncEnumerable(100, () => itemsGenerated++); - - // Act - var result = await source.TakeAsync(10).ToListAsync(); - - // Assert - Assert.Equal(10, result.Count); - // Iterator may request one more item to check if enumeration should continue - // The important thing is we don't enumerate all 100 items - Assert.True(itemsGenerated <= 11, $"Expected at most 11 items generated, but got {itemsGenerated}"); - } + [Fact] + public async Task TakeAsync_StopsEnumerationAfterLimit() + { + // Arrange + int itemsGenerated = 0; + var source = CreateCountingAsyncEnumerable(100, () => itemsGenerated++); + + // Act + var result = await source.TakeAsync(10).ToListAsync(); + + // Assert + Assert.Equal(10, result.Count); + // Iterator may request one more item to check if enumeration should continue + // The important thing is we don't enumerate all 100 items + Assert.True(itemsGenerated <= 11, $"Expected at most 11 items generated, but got {itemsGenerated}"); + } - #endregion + private static readonly int[] expected = new[] { 5, 6, 7, 8, 9 }; - #region SkipAsync Tests + #endregion - [Fact] - public async Task SkipAsync_WithValidOffset_SkipsCorrectItems() - { - // Arrange - var source = CreateAsyncEnumerable(10); + #region SkipAsync Tests - // Act - var result = await source.SkipAsync(5).ToListAsync(); + [Fact] + public async Task SkipAsync_WithValidOffset_SkipsCorrectItems() + { + // Arrange + var source = CreateAsyncEnumerable(10); - // Assert - Assert.Equal(5, result.Count); - Assert.Equal(new[] { 5, 6, 7, 8, 9 }, result); - } + // Act + var result = await source.SkipAsync(5).ToListAsync(); - [Fact] - public async Task SkipAsync_WithOffsetGreaterThanCount_ReturnsEmpty() - { - // Arrange - var source = CreateAsyncEnumerable(5); + // Assert + Assert.Equal(5, result.Count); + Assert.Equal(expected, result); + } - // Act - var result = await source.SkipAsync(10).ToListAsync(); + [Fact] + public async Task SkipAsync_WithOffsetGreaterThanCount_ReturnsEmpty() + { + // Arrange + var source = CreateAsyncEnumerable(5); - // Assert - Assert.Empty(result); - } + // Act + var result = await source.SkipAsync(10).ToListAsync(); - [Fact] - public async Task SkipAsync_WithZeroOffset_ReturnsAllItems() - { - // Arrange - var source = CreateAsyncEnumerable(5); + // Assert + Assert.Empty(result); + } - // Act - var result = await source.SkipAsync(0).ToListAsync(); + [Fact] + public async Task SkipAsync_WithZeroOffset_ReturnsAllItems() + { + // Arrange + var source = CreateAsyncEnumerable(5); - // Assert - Assert.Equal(5, result.Count); - } + // Act + var result = await source.SkipAsync(0).ToListAsync(); - #endregion + // Assert + Assert.Equal(5, result.Count); + } - #region PageAsync Tests + #endregion - [Fact] - public async Task PageAsync_ReturnsCorrectPage() - { - // Arrange - var source = CreateAsyncEnumerable(100); + #region PageAsync Tests - // Act - Get page 3 (offset 20, limit 10) - var result = await source.PageAsync(offset: 20, limit: 10); + [Fact] + public async Task PageAsync_ReturnsCorrectPage() + { + // Arrange + var source = CreateAsyncEnumerable(100); - // Assert - Assert.Equal(10, result.Items.Count); - Assert.Equal(Enumerable.Range(20, 10).ToList(), result.Items); - Assert.True(result.HasMore); - Assert.Equal(10, result.NextOffset); // Relative to the page, not absolute - } + // Act - Get page 3 (offset 20, limit 10) + var result = await source.PageAsync(offset: 20, limit: 10); - [Fact] - public async Task PageAsync_LastPage_HasMoreIsFalse() - { - // Arrange - var source = CreateAsyncEnumerable(25); + // Assert + Assert.Equal(10, result.Items.Count); + Assert.Equal(Enumerable.Range(20, 10).ToList(), result.Items); + Assert.True(result.HasMore); + Assert.Equal(10, result.NextOffset); // Relative to the page, not absolute + } - // Act - Get last page (offset 20, limit 10 but only 5 items left) - var result = await source.PageAsync(offset: 20, limit: 10); + [Fact] + public async Task PageAsync_LastPage_HasMoreIsFalse() + { + // Arrange + var source = CreateAsyncEnumerable(25); - // Assert - Assert.Equal(5, result.Items.Count); - Assert.False(result.HasMore); - Assert.Null(result.NextOffset); - } + // Act - Get last page (offset 20, limit 10 but only 5 items left) + var result = await source.PageAsync(offset: 20, limit: 10); - [Fact] - public async Task PageAsync_BeyondData_ReturnsEmpty() - { - // Arrange - var source = CreateAsyncEnumerable(10); + // Assert + Assert.Equal(5, result.Items.Count); + Assert.False(result.HasMore); + Assert.Null(result.NextOffset); + } - // Act - var result = await source.PageAsync(offset: 20, limit: 10); + [Fact] + public async Task PageAsync_BeyondData_ReturnsEmpty() + { + // Arrange + var source = CreateAsyncEnumerable(10); - // Assert - Assert.Empty(result.Items); - Assert.False(result.HasMore); - } + // Act + var result = await source.PageAsync(offset: 20, limit: 10); + + // Assert + Assert.Empty(result.Items); + Assert.False(result.HasMore); + } + + #endregion - #endregion + #region Cancellation Tests - #region Cancellation Tests + [Fact] + public async Task TakeWithPaginationAsync_RespecsCancellation() + { + // Arrange - create source that checks cancellation + using var cts = new CancellationTokenSource(); + var source = CreateCancellableAsyncEnumerable(100, cts); - [Fact] - public async Task TakeWithPaginationAsync_RespecsCancellation() + // Cancel after a short delay + _ = Task.Run(async () => { - // Arrange - create source that checks cancellation - using var cts = new CancellationTokenSource(); - var source = CreateCancellableAsyncEnumerable(100, cts); - - // Cancel after a short delay - _ = Task.Run(async () => - { - await Task.Delay(10); - cts.Cancel(); - }); - - // Act & Assert - await Assert.ThrowsAnyAsync( - () => source.TakeWithPaginationAsync(100, cts.Token)); - } + await Task.Delay(10); + cts.Cancel(); + }); + + // Act & Assert + await Assert.ThrowsAnyAsync( + () => source.TakeWithPaginationAsync(100, cts.Token)); + } - [Fact] - public async Task TakeAsync_RespectsCancellation() + [Fact] + public async Task TakeAsync_RespectsCancellation() + { + // Arrange - create source that checks cancellation + using var cts = new CancellationTokenSource(); + var source = CreateCancellableAsyncEnumerable(100, cts); + + // Cancel after a short delay + _ = Task.Run(async () => { - // Arrange - create source that checks cancellation - using var cts = new CancellationTokenSource(); - var source = CreateCancellableAsyncEnumerable(100, cts); - - // Cancel after a short delay - _ = Task.Run(async () => - { - await Task.Delay(10); - cts.Cancel(); - }); - - // Act & Assert - await Assert.ThrowsAnyAsync( - async () => await source.TakeAsync(100, cts.Token).ToListAsync()); - } + await Task.Delay(10); + cts.Cancel(); + }); + + // Act & Assert + await Assert.ThrowsAnyAsync( + async () => await source.TakeAsync(100, cts.Token).ToListAsync()); + } - #endregion + #endregion - #region Helper Methods + #region Helper Methods - private static async IAsyncEnumerable CreateAsyncEnumerable(int count) + private static async IAsyncEnumerable CreateAsyncEnumerable(int count) + { + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) - { - await Task.Yield(); // Simulate async operation - yield return i; - } + await Task.Yield(); // Simulate async operation + yield return i; } + } - private static async IAsyncEnumerable CreateCountingAsyncEnumerable(int count, System.Action onGenerate) + private static async IAsyncEnumerable CreateCountingAsyncEnumerable(int count, System.Action onGenerate) + { + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) - { - onGenerate(); - await Task.Yield(); - yield return i; - } + onGenerate(); + await Task.Yield(); + yield return i; } + } - private static async IAsyncEnumerable CreateCancellableAsyncEnumerable( - int count, - CancellationTokenSource cts, - [EnumeratorCancellation] CancellationToken cancellationToken = default) + private static async IAsyncEnumerable CreateCancellableAsyncEnumerable( + int count, + CancellationTokenSource cts, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + for (int i = 0; i < count; i++) { - for (int i = 0; i < count; i++) - { - cancellationToken.ThrowIfCancellationRequested(); - await Task.Delay(5, cancellationToken); // Small delay to allow cancellation to propagate - yield return i; - } + cancellationToken.ThrowIfCancellationRequested(); + await Task.Delay(5, cancellationToken); // Small delay to allow cancellation to propagate + yield return i; } - - #endregion } - // Helper extension for tests - internal static class AsyncEnumerableExtensions + #endregion +} + +// Helper extension for tests +internal static class AsyncEnumerableExtensions +{ + public static async Task> ToListAsync(this IAsyncEnumerable source) { - public static async Task> ToListAsync(this IAsyncEnumerable source) + var list = new List(); + await foreach (var item in source) { - var list = new List(); - await foreach (var item in source) - { - list.Add(item); - } - return list; + list.Add(item); } + return list; } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs b/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs index 2ce568d..9b0b2c4 100644 --- a/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs +++ b/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs @@ -30,4 +30,4 @@ public void Reset() { Server.Reset(); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs b/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs index 2e88ecc..4412591 100644 --- a/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs +++ b/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs @@ -2994,7 +2994,7 @@ public static WireMockServer SetupGetProjectHooksAvatar(this WireMockServer serv .RespondWith(Response.Create() .WithStatusCode(HttpStatusCode.OK) .WithHeader("Content-Type", "image/png") - .WithBody(new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A })); + .WithBody([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A])); return server; } @@ -3037,7 +3037,7 @@ public static WireMockServer SetupGetProjectRepositoryArchive(this WireMockServe .RespondWith(Response.Create() .WithStatusCode(HttpStatusCode.OK) .WithHeader("Content-Type", "application/zip") - .WithBody(new byte[] { 0x50, 0x4B, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00 })); + .WithBody([0x50, 0x4B, 0x03, 0x04, 0x14, 0x00, 0x00, 0x00])); return server; } @@ -3220,7 +3220,4 @@ private static string GetFixturePath(string category, string fileName) { return Path.Combine(FixturesBasePath, category, fileName); } -} - - - +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Infrastructure/TestConstants.cs b/test/Bitbucket.Net.Tests/Infrastructure/TestConstants.cs index 75434b5..e37c8c0 100644 --- a/test/Bitbucket.Net.Tests/Infrastructure/TestConstants.cs +++ b/test/Bitbucket.Net.Tests/Infrastructure/TestConstants.cs @@ -24,4 +24,4 @@ public static class TestConstants public const string TestFileName = "hello.txt"; public const string TestWebHookId = "1"; public const long TestCommentId = 1; -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/AdminExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/AdminExtendedMockTests.cs index 4379e9e..812417d 100644 --- a/test/Bitbucket.Net.Tests/MockTests/AdminExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/AdminExtendedMockTests.cs @@ -1,374 +1,368 @@ -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class AdminExtendedMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class AdminExtendedMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; + private readonly BitbucketMockFixture _fixture = fixture; - public AdminExtendedMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task CreateAdminUserAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupCreateAdminUser(); + var client = _fixture.CreateClient(); - [Fact] - public async Task CreateAdminUserAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupCreateAdminUser(); - var client = _fixture.CreateClient(); + var result = await client.CreateAdminUserAsync( + "newuser", + "password123", + "New User", + "newuser@example.com"); - var result = await client.CreateAdminUserAsync( - "newuser", - "password123", - "New User", - "newuser@example.com"); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task UpdateAdminUserAsync_ReturnsUserInfo() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateAdminUser(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateAdminUserAsync_ReturnsUserInfo() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateAdminUser(); - var client = _fixture.CreateClient(); + var user = await client.UpdateAdminUserAsync( + name: "updateduser", + displayName: "Updated User", + emailAddress: "updated@example.com"); - var user = await client.UpdateAdminUserAsync( - name: "updateduser", - displayName: "Updated User", - emailAddress: "updated@example.com"); + Assert.NotNull(user); + } - Assert.NotNull(user); - } + [Fact] + public async Task AddAdminGroupUsersAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupAddAdminGroupUsers(); + var client = _fixture.CreateClient(); - [Fact] - public async Task AddAdminGroupUsersAsync_ReturnsTrue() + var groupUsers = new GroupUsers { - _fixture.Reset(); - _fixture.Server.SetupAddAdminGroupUsers(); - var client = _fixture.CreateClient(); + Group = "developers", + Users = ["user1", "user2"] + }; - var groupUsers = new GroupUsers - { - Group = "developers", - Users = ["user1", "user2"] - }; + var result = await client.AddAdminGroupUsersAsync(groupUsers); - var result = await client.AddAdminGroupUsersAsync(groupUsers); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task AddAdminUserGroupsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupAddAdminUserGroups(); + var client = _fixture.CreateClient(); - [Fact] - public async Task AddAdminUserGroupsAsync_ReturnsTrue() + var userGroups = new UserGroups { - _fixture.Reset(); - _fixture.Server.SetupAddAdminUserGroups(); - var client = _fixture.CreateClient(); + User = "testuser", + Groups = ["developers", "reviewers"] + }; - var userGroups = new UserGroups - { - User = "testuser", - Groups = ["developers", "reviewers"] - }; + var result = await client.AddAdminUserGroupsAsync(userGroups); - var result = await client.AddAdminUserGroupsAsync(userGroups); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task RemoveAdminUserFromGroupAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupRemoveAdminUserFromGroup(); + var client = _fixture.CreateClient(); - [Fact] - public async Task RemoveAdminUserFromGroupAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupRemoveAdminUserFromGroup(); - var client = _fixture.CreateClient(); + var result = await client.RemoveAdminUserFromGroupAsync("testuser", "old-group"); - var result = await client.RemoveAdminUserFromGroupAsync("testuser", "old-group"); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task DeleteAdminUserCaptchaAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteAdminUserCaptcha(); + var client = _fixture.CreateClient(); - [Fact] - public async Task DeleteAdminUserCaptchaAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteAdminUserCaptcha(); - var client = _fixture.CreateClient(); + var result = await client.DeleteAdminUserCaptcha("testuser"); - var result = await client.DeleteAdminUserCaptcha("testuser"); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task UpdateAdminUserCredentialsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateAdminUserCredentials(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateAdminUserCredentialsAsync_ReturnsTrue() + var passwordChange = new PasswordChange { - _fixture.Reset(); - _fixture.Server.SetupUpdateAdminUserCredentials(); - var client = _fixture.CreateClient(); + Name = "testuser", + Password = "oldpass", + PasswordConfirm = "newpass" + }; - var passwordChange = new PasswordChange - { - Name = "testuser", - Password = "oldpass", - PasswordConfirm = "newpass" - }; + var result = await client.UpdateAdminUserCredentialsAsync(passwordChange); - var result = await client.UpdateAdminUserCredentialsAsync(passwordChange); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task GetAdminMailServerAsync_ReturnsConfiguration() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminMailServer(); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetAdminMailServerAsync_ReturnsConfiguration() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminMailServer(); - var client = _fixture.CreateClient(); + var config = await client.GetAdminMailServerAsync(); - var config = await client.GetAdminMailServerAsync(); + Assert.NotNull(config); + Assert.Equal("mail.example.com", config.HostName); + Assert.Equal(587, config.Port); + } - Assert.NotNull(config); - Assert.Equal("mail.example.com", config.HostName); - Assert.Equal(587, config.Port); - } + [Fact] + public async Task UpdateAdminMailServerAsync_ReturnsConfiguration() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateAdminMailServer(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateAdminMailServerAsync_ReturnsConfiguration() + var config = new MailServerConfiguration { - _fixture.Reset(); - _fixture.Server.SetupUpdateAdminMailServer(); - var client = _fixture.CreateClient(); + HostName = "newmail.example.com", + Port = 465, + Protocol = "SMTP" + }; - var config = new MailServerConfiguration - { - HostName = "newmail.example.com", - Port = 465, - Protocol = "SMTP" - }; + var result = await client.UpdateAdminMailServerAsync(config); - var result = await client.UpdateAdminMailServerAsync(config); + Assert.NotNull(result); + } - Assert.NotNull(result); - } + [Fact] + public async Task DeleteAdminMailServerAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteAdminMailServer(); + var client = _fixture.CreateClient(); - [Fact] - public async Task DeleteAdminMailServerAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteAdminMailServer(); - var client = _fixture.CreateClient(); + var result = await client.DeleteAdminMailServerAsync(); - var result = await client.DeleteAdminMailServerAsync(); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task GetAdminMailServerSenderAddressAsync_ReturnsAddress() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminMailServerSenderAddress(); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetAdminMailServerSenderAddressAsync_ReturnsAddress() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminMailServerSenderAddress(); - var client = _fixture.CreateClient(); + var address = await client.GetAdminMailServerSenderAddressAsync(); - var address = await client.GetAdminMailServerSenderAddressAsync(); + Assert.Equal("bitbucket@example.com", address); + } - Assert.Equal("bitbucket@example.com", address); - } + [Fact] + public async Task UpdateAdminMailServerSenderAddressAsync_ReturnsAddress() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateAdminMailServerSenderAddress(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateAdminMailServerSenderAddressAsync_ReturnsAddress() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateAdminMailServerSenderAddress(); - var client = _fixture.CreateClient(); + var address = await client.UpdateAdminMailServerSenderAddressAsync("new-sender@example.com"); - var address = await client.UpdateAdminMailServerSenderAddressAsync("new-sender@example.com"); + Assert.Equal("new-sender@example.com", address); + } - Assert.Equal("new-sender@example.com", address); - } + [Fact] + public async Task DeleteAdminMailServerSenderAddressAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteAdminMailServerSenderAddress(); + var client = _fixture.CreateClient(); - [Fact] - public async Task DeleteAdminMailServerSenderAddressAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteAdminMailServerSenderAddress(); - var client = _fixture.CreateClient(); + var result = await client.DeleteAdminMailServerSenderAddressAsync(); - var result = await client.DeleteAdminMailServerSenderAddressAsync(); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task UpdateAdminGroupPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateAdminGroupPermissions(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateAdminGroupPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateAdminGroupPermissions(); - var client = _fixture.CreateClient(); + var result = await client.UpdateAdminGroupPermissionsAsync( + Permissions.Admin, + "developers"); - var result = await client.UpdateAdminGroupPermissionsAsync( - Permissions.Admin, - "developers"); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task DeleteAdminGroupPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteAdminGroupPermissions(); + var client = _fixture.CreateClient(); - [Fact] - public async Task DeleteAdminGroupPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteAdminGroupPermissions(); - var client = _fixture.CreateClient(); + var result = await client.DeleteAdminGroupPermissionsAsync("developers"); - var result = await client.DeleteAdminGroupPermissionsAsync("developers"); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task UpdateAdminUserPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateAdminUserPermissions(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateAdminUserPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateAdminUserPermissions(); - var client = _fixture.CreateClient(); + var result = await client.UpdateAdminUserPermissionsAsync( + Permissions.Admin, + "testuser"); - var result = await client.UpdateAdminUserPermissionsAsync( - Permissions.Admin, - "testuser"); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task DeleteAdminUserPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteAdminUserPermissions(); + var client = _fixture.CreateClient(); - [Fact] - public async Task DeleteAdminUserPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteAdminUserPermissions(); - var client = _fixture.CreateClient(); + var result = await client.DeleteAdminUserPermissionsAsync("testuser"); - var result = await client.DeleteAdminUserPermissionsAsync("testuser"); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task GetAdminPullRequestsMergeStrategiesAsync_ReturnsStrategies() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminMergeStrategies("git"); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetAdminPullRequestsMergeStrategiesAsync_ReturnsStrategies() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminMergeStrategies("git"); - var client = _fixture.CreateClient(); + var strategies = await client.GetAdminPullRequestsMergeStrategiesAsync("git"); - var strategies = await client.GetAdminPullRequestsMergeStrategiesAsync("git"); + Assert.NotNull(strategies); + Assert.NotNull(strategies.DefaultStrategy); + } - Assert.NotNull(strategies); - Assert.NotNull(strategies.DefaultStrategy); - } + [Fact] + public async Task UpdateAdminPullRequestsMergeStrategiesAsync_ReturnsStrategies() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateAdminMergeStrategies("git"); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateAdminPullRequestsMergeStrategiesAsync_ReturnsStrategies() + var strategies = new MergeStrategies { - _fixture.Reset(); - _fixture.Server.SetupUpdateAdminMergeStrategies("git"); - var client = _fixture.CreateClient(); + DefaultStrategy = new MergeStrategy { Id = "ff", Name = "Fast-forward", Enabled = true } + }; - var strategies = new MergeStrategies - { - DefaultStrategy = new MergeStrategy { Id = "ff", Name = "Fast-forward", Enabled = true } - }; + var result = await client.UpdateAdminPullRequestsMergeStrategiesAsync("git", strategies); - var result = await client.UpdateAdminPullRequestsMergeStrategiesAsync("git", strategies); + Assert.NotNull(result); + } - Assert.NotNull(result); - } + [Fact] + public async Task UpdateAdminLicenseAsync_ReturnsLicenseDetails() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateAdminLicense(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateAdminLicenseAsync_ReturnsLicenseDetails() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateAdminLicense(); - var client = _fixture.CreateClient(); + var licenseInfo = new LicenseInfo { License = "NEW-LICENSE-KEY" }; - var licenseInfo = new LicenseInfo { License = "NEW-LICENSE-KEY" }; + var result = await client.UpdateAdminLicenseAsync(licenseInfo); - var result = await client.UpdateAdminLicenseAsync(licenseInfo); + Assert.NotNull(result); + } - Assert.NotNull(result); - } + [Fact] + public async Task RenameAdminUserAsync_ReturnsUserInfo() + { + _fixture.Reset(); + _fixture.Server.SetupRenameAdminUser(); + var client = _fixture.CreateClient(); - [Fact] - public async Task RenameAdminUserAsync_ReturnsUserInfo() + var userRename = new UserRename { - _fixture.Reset(); - _fixture.Server.SetupRenameAdminUser(); - var client = _fixture.CreateClient(); + Name = "olduser", + NewName = "newuser" + }; - var userRename = new UserRename - { - Name = "olduser", - NewName = "newuser" - }; + var result = await client.RenameAdminUserAsync(userRename); - var result = await client.RenameAdminUserAsync(userRename); - - Assert.NotNull(result); - } + Assert.NotNull(result); + } - [Fact] - public async Task GetAdminGroupMoreMembersAsync_ReturnsUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminGroupMoreMembers(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetAdminGroupMoreMembersAsync_ReturnsUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminGroupMoreMembers(); + var client = _fixture.CreateClient(); - var result = await client.GetAdminGroupMoreMembersAsync("test-group"); + var result = await client.GetAdminGroupMoreMembersAsync("test-group"); - Assert.NotNull(result); - Assert.NotEmpty(result); - } + Assert.NotNull(result); + Assert.NotEmpty(result); + } - [Fact] - public async Task GetAdminGroupMoreNonMembersAsync_ReturnsUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminGroupMoreNonMembers(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetAdminGroupMoreNonMembersAsync_ReturnsUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminGroupMoreNonMembers(); + var client = _fixture.CreateClient(); - var result = await client.GetAdminGroupMoreNonMembersAsync("test-group"); + var result = await client.GetAdminGroupMoreNonMembersAsync("test-group"); - Assert.NotNull(result); - Assert.NotEmpty(result); - } + Assert.NotNull(result); + Assert.NotEmpty(result); + } - [Fact] - public async Task GetAdminUserMoreMembersAsync_ReturnsGroupsOrUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminUserMoreMembers(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetAdminUserMoreMembersAsync_ReturnsGroupsOrUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminUserMoreMembers(); + var client = _fixture.CreateClient(); - var result = await client.GetAdminUserMoreMembersAsync("testuser"); + var result = await client.GetAdminUserMoreMembersAsync("testuser"); - Assert.NotNull(result); - Assert.NotEmpty(result); - } + Assert.NotNull(result); + Assert.NotEmpty(result); + } - [Fact] - public async Task GetAdminUserMoreNonMembersAsync_ReturnsGroupsOrUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminUserMoreNonMembers(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetAdminUserMoreNonMembersAsync_ReturnsGroupsOrUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminUserMoreNonMembers(); + var client = _fixture.CreateClient(); - var result = await client.GetAdminUserMoreNonMembersAsync("testuser"); + var result = await client.GetAdminUserMoreNonMembersAsync("testuser"); - Assert.NotNull(result); - Assert.NotEmpty(result); - } + Assert.NotNull(result); + Assert.NotEmpty(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/AdminMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/AdminMockTests.cs index 5b2a155..373de83 100644 --- a/test/Bitbucket.Net.Tests/MockTests/AdminMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/AdminMockTests.cs @@ -1,160 +1,154 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class AdminMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class AdminMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetAdminGroupsAsync_ReturnsGroups() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminGroups(); + var client = _fixture.CreateClient(); + + var groups = await client.GetAdminGroupsAsync(); + + var groupList = groups.ToList(); + Assert.NotEmpty(groupList); + Assert.Equal(2, groupList.Count); + Assert.Equal("developers", groupList[0].Name); + Assert.True(groupList[0].Deletable); + Assert.Equal("administrators", groupList[1].Name); + Assert.False(groupList[1].Deletable); + } + + [Fact] + public async Task CreateAdminGroupAsync_ReturnsCreatedGroup() + { + _fixture.Reset(); + _fixture.Server.SetupCreateAdminGroup("new-group"); + var client = _fixture.CreateClient(); + + var group = await client.CreateAdminGroupAsync("new-group"); + + Assert.NotNull(group); + Assert.Equal("new-group", group.Name); + Assert.True(group.Deletable); + } + + [Fact] + public async Task DeleteAdminGroupAsync_ReturnsDeletedGroup() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteAdminGroup("old-group"); + var client = _fixture.CreateClient(); + + var group = await client.DeleteAdminGroupAsync("old-group"); + + Assert.NotNull(group); + Assert.Equal("new-group", group.Name); + } + + [Fact] + public async Task GetAdminUsersAsync_ReturnsUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminUsers(); + var client = _fixture.CreateClient(); + + var users = await client.GetAdminUsersAsync(); + + var userList = users.ToList(); + Assert.NotEmpty(userList); + Assert.Equal(2, userList.Count); + Assert.Equal("admin", userList[0].Name); + Assert.Equal("admin@example.com", userList[0].EmailAddress); + Assert.Equal("jsmith", userList[1].Name); + } + + [Fact] + public async Task DeleteAdminUserAsync_ReturnsDeletedUser() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteAdminUser("olduser"); + var client = _fixture.CreateClient(); + + var user = await client.DeleteAdminUserAsync("olduser"); + + Assert.NotNull(user); + Assert.Equal("newuser", user.Name); + Assert.Equal("newuser@example.com", user.EmailAddress); + } + + [Fact] + public async Task GetAdminClusterAsync_ReturnsClusterInfo() { - private readonly BitbucketMockFixture _fixture; - - public AdminMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetAdminGroupsAsync_ReturnsGroups() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminGroups(); - var client = _fixture.CreateClient(); - - var groups = await client.GetAdminGroupsAsync(); - - var groupList = groups.ToList(); - Assert.NotEmpty(groupList); - Assert.Equal(2, groupList.Count); - Assert.Equal("developers", groupList[0].Name); - Assert.True(groupList[0].Deletable); - Assert.Equal("administrators", groupList[1].Name); - Assert.False(groupList[1].Deletable); - } - - [Fact] - public async Task CreateAdminGroupAsync_ReturnsCreatedGroup() - { - _fixture.Reset(); - _fixture.Server.SetupCreateAdminGroup("new-group"); - var client = _fixture.CreateClient(); - - var group = await client.CreateAdminGroupAsync("new-group"); - - Assert.NotNull(group); - Assert.Equal("new-group", group.Name); - Assert.True(group.Deletable); - } - - [Fact] - public async Task DeleteAdminGroupAsync_ReturnsDeletedGroup() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteAdminGroup("old-group"); - var client = _fixture.CreateClient(); - - var group = await client.DeleteAdminGroupAsync("old-group"); - - Assert.NotNull(group); - Assert.Equal("new-group", group.Name); - } - - [Fact] - public async Task GetAdminUsersAsync_ReturnsUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminUsers(); - var client = _fixture.CreateClient(); - - var users = await client.GetAdminUsersAsync(); - - var userList = users.ToList(); - Assert.NotEmpty(userList); - Assert.Equal(2, userList.Count); - Assert.Equal("admin", userList[0].Name); - Assert.Equal("admin@example.com", userList[0].EmailAddress); - Assert.Equal("jsmith", userList[1].Name); - } - - [Fact] - public async Task DeleteAdminUserAsync_ReturnsDeletedUser() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteAdminUser("olduser"); - var client = _fixture.CreateClient(); - - var user = await client.DeleteAdminUserAsync("olduser"); - - Assert.NotNull(user); - Assert.Equal("newuser", user.Name); - Assert.Equal("newuser@example.com", user.EmailAddress); - } - - [Fact] - public async Task GetAdminClusterAsync_ReturnsClusterInfo() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminCluster(); - var client = _fixture.CreateClient(); - - var cluster = await client.GetAdminClusterAsync(); - - Assert.NotNull(cluster); - Assert.True(cluster.Running); - Assert.NotNull(cluster.LocalNode); - Assert.Equal("node-1", cluster.LocalNode.Id); - Assert.Equal("bitbucket-node-1", cluster.LocalNode.Name); - Assert.True(cluster.LocalNode.Local); - Assert.Equal(2, cluster.Nodes.Count); - } - - [Fact] - public async Task GetAdminLicenseAsync_ReturnsLicenseDetails() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminLicense(); - var client = _fixture.CreateClient(); - - var license = await client.GetAdminLicenseAsync(); - - Assert.NotNull(license); - Assert.Equal("SERV-1234-5678", license.ServerId); - Assert.Equal("SEN-12345", license.SupportEntitlementNumber); - Assert.Equal(500, license.MaximumNumberOfUsers); - Assert.False(license.UnlimitedNumberOfUsers); - Assert.Equal(365, license.NumberOfDaysBeforeExpiry); - } - - [Fact] - public async Task GetAdminGroupPermissionsAsync_ReturnsPermissions() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminGroupPermissions(); - var client = _fixture.CreateClient(); - - var permissions = await client.GetAdminGroupPermissionsAsync(); - - var permList = permissions.ToList(); - Assert.NotEmpty(permList); - Assert.Equal(2, permList.Count); - Assert.Equal("administrators", permList[0].Group.Name); - Assert.Equal("developers", permList[1].Group.Name); - } - - [Fact] - public async Task GetAdminUserPermissionsAsync_ReturnsPermissions() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminUserPermissions(); - var client = _fixture.CreateClient(); - - var permissions = await client.GetAdminUserPermissionsAsync(); - - var permList = permissions.ToList(); - Assert.NotEmpty(permList); - Assert.Equal(2, permList.Count); - Assert.Equal("admin", permList[0].User.Name); - Assert.Equal("jsmith", permList[1].User.Name); - } + _fixture.Reset(); + _fixture.Server.SetupGetAdminCluster(); + var client = _fixture.CreateClient(); + + var cluster = await client.GetAdminClusterAsync(); + + Assert.NotNull(cluster); + Assert.True(cluster.Running); + Assert.NotNull(cluster.LocalNode); + Assert.Equal("node-1", cluster.LocalNode.Id); + Assert.Equal("bitbucket-node-1", cluster.LocalNode.Name); + Assert.True(cluster.LocalNode.Local); + Assert.Equal(2, cluster.Nodes.Count); + } + + [Fact] + public async Task GetAdminLicenseAsync_ReturnsLicenseDetails() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminLicense(); + var client = _fixture.CreateClient(); + + var license = await client.GetAdminLicenseAsync(); + + Assert.NotNull(license); + Assert.Equal("SERV-1234-5678", license.ServerId); + Assert.Equal("SEN-12345", license.SupportEntitlementNumber); + Assert.Equal(500, license.MaximumNumberOfUsers); + Assert.False(license.UnlimitedNumberOfUsers); + Assert.Equal(365, license.NumberOfDaysBeforeExpiry); + } + + [Fact] + public async Task GetAdminGroupPermissionsAsync_ReturnsPermissions() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminGroupPermissions(); + var client = _fixture.CreateClient(); + + var permissions = await client.GetAdminGroupPermissionsAsync(); + + var permList = permissions.ToList(); + Assert.NotEmpty(permList); + Assert.Equal(2, permList.Count); + Assert.Equal("administrators", permList[0].Group.Name); + Assert.Equal("developers", permList[1].Group.Name); + } + + [Fact] + public async Task GetAdminUserPermissionsAsync_ReturnsPermissions() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminUserPermissions(); + var client = _fixture.CreateClient(); + + var permissions = await client.GetAdminUserPermissionsAsync(); + + var permList = permissions.ToList(); + Assert.NotEmpty(permList); + Assert.Equal(2, permList.Count); + Assert.Equal("admin", permList[0].User.Name); + Assert.Equal("jsmith", permList[1].User.Name); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/AdminPermissionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/AdminPermissionsMockTests.cs index 4968b7c..89060bb 100644 --- a/test/Bitbucket.Net.Tests/MockTests/AdminPermissionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/AdminPermissionsMockTests.cs @@ -1,70 +1,64 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class AdminPermissionsMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public AdminPermissionsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class AdminPermissionsMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetAdminGroupPermissionsNoneAsync_ReturnsGroups() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminGroupPermissionsNone(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetAdminGroupPermissionsNoneAsync_ReturnsGroups() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminGroupPermissionsNone(); + var client = _fixture.CreateClient(); - var groups = await client.GetAdminGroupPermissionsNoneAsync(); + var groups = await client.GetAdminGroupPermissionsNoneAsync(); - Assert.NotNull(groups); - var groupList = groups.ToList(); - Assert.NotEmpty(groupList); - } + Assert.NotNull(groups); + var groupList = groups.ToList(); + Assert.NotEmpty(groupList); + } - [Fact] - public async Task GetAdminUserPermissionsNoneAsync_ReturnsUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminUserPermissionsNone(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetAdminUserPermissionsNoneAsync_ReturnsUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminUserPermissionsNone(); + var client = _fixture.CreateClient(); - var users = await client.GetAdminUserPermissionsNoneAsync(); + var users = await client.GetAdminUserPermissionsNoneAsync(); - Assert.NotNull(users); - var userList = users.ToList(); - Assert.NotEmpty(userList); - } + Assert.NotNull(users); + var userList = users.ToList(); + Assert.NotEmpty(userList); + } - [Fact] - public async Task GetAdminClusterAsync_ReturnsClusterInfo() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminCluster(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetAdminClusterAsync_ReturnsClusterInfo() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminCluster(); + var client = _fixture.CreateClient(); - var cluster = await client.GetAdminClusterAsync(); + var cluster = await client.GetAdminClusterAsync(); - Assert.NotNull(cluster); - Assert.True(cluster.Running); - } + Assert.NotNull(cluster); + Assert.True(cluster.Running); + } - [Fact] - public async Task GetAdminLicenseAsync_ReturnsLicenseDetails() - { - _fixture.Reset(); - _fixture.Server.SetupGetAdminLicense(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetAdminLicenseAsync_ReturnsLicenseDetails() + { + _fixture.Reset(); + _fixture.Server.SetupGetAdminLicense(); + var client = _fixture.CreateClient(); - var license = await client.GetAdminLicenseAsync(); + var license = await client.GetAdminLicenseAsync(); - Assert.NotNull(license); - } + Assert.NotNull(license); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/ApplicationPropertiesMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ApplicationPropertiesMockTests.cs index 9794af3..cb60a5c 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ApplicationPropertiesMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ApplicationPropertiesMockTests.cs @@ -1,32 +1,26 @@ -using System.Threading.Tasks; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class ApplicationPropertiesMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public ApplicationPropertiesMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class ApplicationPropertiesMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetApplicationPropertiesAsync_ReturnsProperties() - { - _fixture.Reset(); - _fixture.Server.SetupGetApplicationProperties(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetApplicationPropertiesAsync_ReturnsProperties() + { + _fixture.Reset(); + _fixture.Server.SetupGetApplicationProperties(); + var client = _fixture.CreateClient(); - var result = await client.GetApplicationPropertiesAsync(); + var result = await client.GetApplicationPropertiesAsync(); - Assert.NotNull(result); - Assert.True(result.ContainsKey("version")); - Assert.True(result.ContainsKey("buildNumber")); - Assert.True(result.ContainsKey("displayName")); - Assert.Equal("8.14.0", result["version"]?.ToString()); - } + Assert.NotNull(result); + Assert.True(result.ContainsKey("version")); + Assert.True(result.ContainsKey("buildNumber")); + Assert.True(result.ContainsKey("displayName")); + Assert.Equal("8.14.0", result["version"]?.ToString()); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/AuditMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/AuditMockTests.cs index 7466613..a8133f0 100644 --- a/test/Bitbucket.Net.Tests/MockTests/AuditMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/AuditMockTests.cs @@ -1,49 +1,43 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class AuditMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class AuditMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + private const string ProjectKey = "PROJ"; + private const string RepoSlug = "repo"; + + [Fact] + public async Task GetProjectAuditEventsAsync_ReturnsEvents() { - private readonly BitbucketMockFixture _fixture; - private const string ProjectKey = "PROJ"; - private const string RepoSlug = "repo"; - - public AuditMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetProjectAuditEventsAsync_ReturnsEvents() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectAuditEvents(ProjectKey); - var client = _fixture.CreateClient(); - - var result = await client.GetProjectAuditEventsAsync(ProjectKey); - - Assert.NotNull(result); - var events = result.ToList(); - Assert.Equal(2, events.Count); - Assert.Equal("PROJECT_CREATED", events[0].Action); - } - - [Fact] - public async Task GetProjectRepoAuditEventsAsync_ReturnsEvents() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectRepoAuditEvents(ProjectKey, RepoSlug); - var client = _fixture.CreateClient(); - - var result = await client.GetProjectRepoAuditEventsAsync(ProjectKey, RepoSlug); - - Assert.NotNull(result); - var events = result.ToList(); - Assert.Equal(2, events.Count); - Assert.Equal("PROJECT_CREATED", events[0].Action); - } + _fixture.Reset(); + _fixture.Server.SetupGetProjectAuditEvents(ProjectKey); + var client = _fixture.CreateClient(); + + var result = await client.GetProjectAuditEventsAsync(ProjectKey); + + Assert.NotNull(result); + var events = result.ToList(); + Assert.Equal(2, events.Count); + Assert.Equal("PROJECT_CREATED", events[0].Action); + } + + [Fact] + public async Task GetProjectRepoAuditEventsAsync_ReturnsEvents() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectRepoAuditEvents(ProjectKey, RepoSlug); + var client = _fixture.CreateClient(); + + var result = await client.GetProjectRepoAuditEventsAsync(ProjectKey, RepoSlug); + + Assert.NotNull(result); + var events = result.ToList(); + Assert.Equal(2, events.Count); + Assert.Equal("PROJECT_CREATED", events[0].Action); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/BranchExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/BranchExtendedMockTests.cs index 684c88d..615b026 100644 --- a/test/Bitbucket.Net.Tests/MockTests/BranchExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/BranchExtendedMockTests.cs @@ -1,89 +1,83 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class BranchExtendedMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public BranchExtendedMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class BranchExtendedMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetCommitBranchInfoAsync_ReturnsBranches() - { - _fixture.Reset(); - _fixture.Server.SetupGetCommitBranchInfo( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetCommitBranchInfoAsync_ReturnsBranches() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommitBranchInfo( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + var client = _fixture.CreateClient(); - var branches = await client.GetCommitBranchInfoAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); + var branches = await client.GetCommitBranchInfoAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); - Assert.NotNull(branches); - var branchList = branches.ToList(); - Assert.Equal(2, branchList.Count); - } + Assert.NotNull(branches); + var branchList = branches.ToList(); + Assert.Equal(2, branchList.Count); + } - [Fact] - public async Task GetRepoBranchModelAsync_ReturnsBranchModel() - { - _fixture.Reset(); - _fixture.Server.SetupGetBranchModel( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetRepoBranchModelAsync_ReturnsBranchModel() + { + _fixture.Reset(); + _fixture.Server.SetupGetBranchModel( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - var model = await client.GetRepoBranchModelAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); + var model = await client.GetRepoBranchModelAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); - Assert.NotNull(model); - } + Assert.NotNull(model); + } - [Fact] - public async Task CreateRepoBranchAsync_ReturnsBranch() - { - _fixture.Reset(); - _fixture.Server.SetupCreateRepoBranch( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task CreateRepoBranchAsync_ReturnsBranch() + { + _fixture.Reset(); + _fixture.Server.SetupCreateRepoBranch( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - var branch = await client.CreateRepoBranchAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "feature/new-branch", - "refs/heads/master"); + var branch = await client.CreateRepoBranchAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "feature/new-branch", + "refs/heads/master"); - Assert.NotNull(branch); - } + Assert.NotNull(branch); + } - [Fact] - public async Task DeleteRepoBranchAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteRepoBranch( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeleteRepoBranchAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteRepoBranch( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - var result = await client.DeleteRepoBranchAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "feature/old-branch", - dryRun: false); + var result = await client.DeleteRepoBranchAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "feature/old-branch", + dryRun: false); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/BranchMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/BranchMockTests.cs index 9fc578a..f7db308 100644 --- a/test/Bitbucket.Net.Tests/MockTests/BranchMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/BranchMockTests.cs @@ -1,96 +1,90 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class BranchMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class BranchMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetBranchesAsync_ReturnsBranches() { - private readonly BitbucketMockFixture _fixture; + _fixture.Reset(); + _fixture.Server.SetupGetBranches(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var branches = await client.GetBranchesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(branches); + var branchList = branches.ToList(); + Assert.Equal(2, branchList.Count); + Assert.Contains(branchList, b => b.DisplayId == "master"); + Assert.Contains(branchList, b => b.DisplayId == "feature-test"); + } - public BranchMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task GetDefaultBranchAsync_ReturnsMasterBranch() + { + _fixture.Reset(); + _fixture.Server.SetupGetDefaultBranch(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetBranchesAsync_ReturnsBranches() - { - _fixture.Reset(); - _fixture.Server.SetupGetBranches(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var branches = await client.GetBranchesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(branches); - var branchList = branches.ToList(); - Assert.Equal(2, branchList.Count); - Assert.Contains(branchList, b => b.DisplayId == "master"); - Assert.Contains(branchList, b => b.DisplayId == "feature-test"); - } - - [Fact] - public async Task GetDefaultBranchAsync_ReturnsMasterBranch() - { - _fixture.Reset(); - _fixture.Server.SetupGetDefaultBranch(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + var branch = await client.GetDefaultBranchAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); - var branch = await client.GetDefaultBranchAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); + Assert.NotNull(branch); + Assert.Equal("master", branch.DisplayId); + Assert.True(branch.IsDefault); + } - Assert.NotNull(branch); - Assert.Equal("master", branch.DisplayId); - Assert.True(branch.IsDefault); - } + [Fact] + public async Task CreateBranchAsync_ReturnsBranch() + { + _fixture.Reset(); + _fixture.Server.SetupCreateBranch(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - [Fact] - public async Task CreateBranchAsync_ReturnsBranch() + var branchInfo = new BranchInfo { - _fixture.Reset(); - _fixture.Server.SetupCreateBranch(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var branchInfo = new BranchInfo - { - Name = "feature-test", - StartPoint = "refs/heads/master" - }; - - var branch = await client.CreateBranchAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - branchInfo); - - Assert.NotNull(branch); - Assert.Equal("refs/heads/feature-test", branch.Id); - Assert.Equal("feature-test", branch.DisplayId); - Assert.False(branch.IsDefault); - } - - [Fact] - public async Task SetDefaultBranchAsync_ReturnsTrue() + Name = "feature-test", + StartPoint = "refs/heads/master" + }; + + var branch = await client.CreateBranchAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + branchInfo); + + Assert.NotNull(branch); + Assert.Equal("refs/heads/feature-test", branch.Id); + Assert.Equal("feature-test", branch.DisplayId); + Assert.False(branch.IsDefault); + } + + [Fact] + public async Task SetDefaultBranchAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupSetDefaultBranch(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var branchRef = new BranchRef { - _fixture.Reset(); - _fixture.Server.SetupSetDefaultBranch(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var branchRef = new BranchRef - { - Id = "refs/heads/develop" - }; - - var result = await client.SetDefaultBranchAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - branchRef); - - Assert.True(result); - } + Id = "refs/heads/develop" + }; + + var result = await client.SetDefaultBranchAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + branchRef); + + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/BuildExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/BuildExtendedMockTests.cs index fa69e04..6970eb3 100644 --- a/test/Bitbucket.Net.Tests/MockTests/BuildExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/BuildExtendedMockTests.cs @@ -1,51 +1,45 @@ +using Bitbucket.Net.Models.Builds; +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Models.Builds; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class BuildExtendedMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class BuildExtendedMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetBuildStatsForCommitsAsync_WithCancellationToken_ReturnsDictionaryWithStats() { - private readonly BitbucketMockFixture _fixture; - - public BuildExtendedMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetBuildStatsForCommitsAsync_WithCancellationToken_ReturnsDictionaryWithStats() - { - _fixture.Reset(); - _fixture.Server.SetupGetBuildStatsForMultipleCommits(); - var client = _fixture.CreateClient(); - - var stats = await client.GetBuildStatsForCommitsAsync( - cancellationToken: CancellationToken.None, - commitIds: ["abc123def456", "def456ghi789"]); - - Assert.NotNull(stats); - Assert.Equal(2, stats.Count); - Assert.True(stats.ContainsKey("abc123def456")); - Assert.True(stats.ContainsKey("def456ghi789")); - Assert.Equal(2, stats["abc123def456"].Successful); - Assert.Equal(1, stats["def456ghi789"].Failed); - } - - [Fact] - public async Task GetBuildStatsForCommitsAsync_WithoutCancellationToken_ReturnsDictionaryWithStats() - { - _fixture.Reset(); - _fixture.Server.SetupGetBuildStatsForMultipleCommits(); - var client = _fixture.CreateClient(); - - var stats = await client.GetBuildStatsForCommitsAsync(commitIds: ["abc123def456", "def456ghi789"]); - - Assert.NotNull(stats); - Assert.Equal(2, stats.Count); - } + _fixture.Reset(); + _fixture.Server.SetupGetBuildStatsForMultipleCommits(); + var client = _fixture.CreateClient(); + + var stats = await client.GetBuildStatsForCommitsAsync( + cancellationToken: CancellationToken.None, + commitIds: ["abc123def456", "def456ghi789"]); + + Assert.NotNull(stats); + Assert.Equal(2, stats.Count); + Assert.True(stats.ContainsKey("abc123def456")); + Assert.True(stats.ContainsKey("def456ghi789")); + Assert.Equal(2, stats["abc123def456"].Successful); + Assert.Equal(1, stats["def456ghi789"].Failed); + } + + [Fact] + public async Task GetBuildStatsForCommitsAsync_WithoutCancellationToken_ReturnsDictionaryWithStats() + { + _fixture.Reset(); + _fixture.Server.SetupGetBuildStatsForMultipleCommits(); + var client = _fixture.CreateClient(); + + var stats = await client.GetBuildStatsForCommitsAsync(commitIds: ["abc123def456", "def456ghi789"]); + + Assert.NotNull(stats); + Assert.Equal(2, stats.Count); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/BuildMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/BuildMockTests.cs index 015d3ed..0544561 100644 --- a/test/Bitbucket.Net.Tests/MockTests/BuildMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/BuildMockTests.cs @@ -1,72 +1,66 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Builds; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class BuildMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class BuildMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetBuildStatsForCommitAsync_ReturnsStats() { - private readonly BitbucketMockFixture _fixture; + _fixture.Reset(); + _fixture.Server.SetupGetBuildStatsForCommit(TestConstants.TestCommitId); + var client = _fixture.CreateClient(); - public BuildMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + var stats = await client.GetBuildStatsForCommitAsync(TestConstants.TestCommitId); - [Fact] - public async Task GetBuildStatsForCommitAsync_ReturnsStats() - { - _fixture.Reset(); - _fixture.Server.SetupGetBuildStatsForCommit(TestConstants.TestCommitId); - var client = _fixture.CreateClient(); - - var stats = await client.GetBuildStatsForCommitAsync(TestConstants.TestCommitId); + Assert.NotNull(stats); + Assert.Equal(3, stats.Successful); + Assert.Equal(1, stats.InProgress); + Assert.Equal(0, stats.Failed); + } - Assert.NotNull(stats); - Assert.Equal(3, stats.Successful); - Assert.Equal(1, stats.InProgress); - Assert.Equal(0, stats.Failed); - } + [Fact] + public async Task GetBuildStatusForCommitAsync_ReturnsStatuses() + { + _fixture.Reset(); + _fixture.Server.SetupGetBuildStatusForCommit(TestConstants.TestCommitId); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetBuildStatusForCommitAsync_ReturnsStatuses() - { - _fixture.Reset(); - _fixture.Server.SetupGetBuildStatusForCommit(TestConstants.TestCommitId); - var client = _fixture.CreateClient(); + var statuses = await client.GetBuildStatusForCommitAsync(TestConstants.TestCommitId); - var statuses = await client.GetBuildStatusForCommitAsync(TestConstants.TestCommitId); + Assert.NotNull(statuses); + var statusList = statuses.ToList(); + Assert.Equal(2, statusList.Count); + Assert.Equal("build-123", statusList[0].Key); + Assert.Equal("SUCCESSFUL", statusList[0].State); + } - Assert.NotNull(statuses); - var statusList = statuses.ToList(); - Assert.Equal(2, statusList.Count); - Assert.Equal("build-123", statusList[0].Key); - Assert.Equal("SUCCESSFUL", statusList[0].State); - } + [Fact] + public async Task AssociateBuildStatusWithCommitAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupAssociateBuildStatus(TestConstants.TestCommitId); + var client = _fixture.CreateClient(); - [Fact] - public async Task AssociateBuildStatusWithCommitAsync_ReturnsTrue() + var buildStatus = new BuildStatus { - _fixture.Reset(); - _fixture.Server.SetupAssociateBuildStatus(TestConstants.TestCommitId); - var client = _fixture.CreateClient(); - - var buildStatus = new BuildStatus - { - Key = "build-125", - State = "SUCCESSFUL", - Name = "Test Build", - Description = "Build completed", - Url = "https://build-server/builds/125" - }; + Key = "build-125", + State = "SUCCESSFUL", + Name = "Test Build", + Description = "Build completed", + Url = "https://build-server/builds/125" + }; - var result = await client.AssociateBuildStatusWithCommitAsync( - TestConstants.TestCommitId, - buildStatus); + var result = await client.AssociateBuildStatusWithCommitAsync( + TestConstants.TestCommitId, + buildStatus); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/ChangesAndFilesMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ChangesAndFilesMockTests.cs index 7a7ee64..fc8cf77 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ChangesAndFilesMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ChangesAndFilesMockTests.cs @@ -1,112 +1,106 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class ChangesAndFilesMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class ChangesAndFilesMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetChangesAsync_ReturnsChanges() + { + _fixture.Reset(); + _fixture.Server.SetupGetChanges(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var changes = await client.GetChangesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + until: "HEAD"); + + Assert.NotNull(changes); + var changeList = changes.ToList(); + Assert.Single(changeList); + Assert.Equal("MODIFY", changeList[0].Type); + } + + [Fact] + public async Task GetCommitChangesAsync_ReturnsChanges() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommitChanges( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + var client = _fixture.CreateClient(); + + var changes = await client.GetCommitChangesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + + Assert.NotNull(changes); + var changeList = changes.ToList(); + Assert.Single(changeList); + } + + [Fact] + public async Task GetRepositoryFilesAsync_ReturnsFiles() + { + _fixture.Reset(); + _fixture.Server.SetupGetFiles(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var files = await client.GetRepositoryFilesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(files); + var fileList = files.ToList(); + Assert.Equal(3, fileList.Count); + Assert.Contains("README.md", fileList); + } + + [Fact] + public async Task GetPullRequestChangesAsync_ReturnsChanges() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestChanges( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var changes = await client.GetPullRequestChangesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(changes); + var changeList = changes.ToList(); + Assert.Single(changeList); + } + + [Fact] + public async Task GetPullRequestCommitsAsync_ReturnsCommits() { - private readonly BitbucketMockFixture _fixture; - - public ChangesAndFilesMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetChangesAsync_ReturnsChanges() - { - _fixture.Reset(); - _fixture.Server.SetupGetChanges(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var changes = await client.GetChangesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - until: "HEAD"); - - Assert.NotNull(changes); - var changeList = changes.ToList(); - Assert.Single(changeList); - Assert.Equal("MODIFY", changeList[0].Type); - } - - [Fact] - public async Task GetCommitChangesAsync_ReturnsChanges() - { - _fixture.Reset(); - _fixture.Server.SetupGetCommitChanges( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - var client = _fixture.CreateClient(); - - var changes = await client.GetCommitChangesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - - Assert.NotNull(changes); - var changeList = changes.ToList(); - Assert.Single(changeList); - } - - [Fact] - public async Task GetRepositoryFilesAsync_ReturnsFiles() - { - _fixture.Reset(); - _fixture.Server.SetupGetFiles(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var files = await client.GetRepositoryFilesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(files); - var fileList = files.ToList(); - Assert.Equal(3, fileList.Count); - Assert.Contains("README.md", fileList); - } - - [Fact] - public async Task GetPullRequestChangesAsync_ReturnsChanges() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestChanges( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var changes = await client.GetPullRequestChangesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(changes); - var changeList = changes.ToList(); - Assert.Single(changeList); - } - - [Fact] - public async Task GetPullRequestCommitsAsync_ReturnsCommits() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestCommits( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var commits = await client.GetPullRequestCommitsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(commits); - var commitList = commits.ToList(); - Assert.Equal(2, commitList.Count); - } + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestCommits( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var commits = await client.GetPullRequestCommitsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(commits); + var commitList = commits.ToList(); + Assert.Equal(2, commitList.Count); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/CommentLikesMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/CommentLikesMockTests.cs index ac48818..a87532e 100644 --- a/test/Bitbucket.Net.Tests/MockTests/CommentLikesMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/CommentLikesMockTests.cs @@ -1,100 +1,94 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class CommentLikesMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class CommentLikesMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + private const string ProjectKey = "PROJ"; + private const string RepoSlug = "repo"; + private const string CommitId = "abc123"; + private const string CommentId = "100"; + private const string PullRequestId = "1"; + + [Fact] + public async Task GetCommitCommentLikesAsync_ReturnsUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommitCommentLikes(ProjectKey, RepoSlug, CommitId, CommentId); + var client = _fixture.CreateClient(); + + var result = await client.GetCommitCommentLikesAsync(ProjectKey, RepoSlug, CommitId, CommentId); + + Assert.NotNull(result); + var users = result.ToList(); + Assert.Equal(2, users.Count); + Assert.Equal("jsmith", users[0].Name); + Assert.Equal("jdoe", users[1].Name); + } + + [Fact] + public async Task LikeCommitCommentAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupLikeCommitComment(ProjectKey, RepoSlug, CommitId, CommentId); + var client = _fixture.CreateClient(); + + var result = await client.LikeCommitCommentAsync(ProjectKey, RepoSlug, CommitId, CommentId); + + Assert.True(result); + } + + [Fact] + public async Task UnlikeCommitCommentAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUnlikeCommitComment(ProjectKey, RepoSlug, CommitId, CommentId); + var client = _fixture.CreateClient(); + + var result = await client.UnlikeCommitCommentAsync(ProjectKey, RepoSlug, CommitId, CommentId); + + Assert.True(result); + } + + [Fact] + public async Task GetPullRequestCommentLikesAsync_ReturnsUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestCommentLikes(ProjectKey, RepoSlug, PullRequestId, CommentId); + var client = _fixture.CreateClient(); + + var result = await client.GetPullRequestCommentLikesAsync(ProjectKey, RepoSlug, PullRequestId, CommentId); + + Assert.NotNull(result); + var users = result.ToList(); + Assert.Equal(2, users.Count); + } + + [Fact] + public async Task LikePullRequestCommentAsync_ReturnsTrue() { - private readonly BitbucketMockFixture _fixture; - private const string ProjectKey = "PROJ"; - private const string RepoSlug = "repo"; - private const string CommitId = "abc123"; - private const string CommentId = "100"; - private const string PullRequestId = "1"; - - public CommentLikesMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetCommitCommentLikesAsync_ReturnsUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetCommitCommentLikes(ProjectKey, RepoSlug, CommitId, CommentId); - var client = _fixture.CreateClient(); - - var result = await client.GetCommitCommentLikesAsync(ProjectKey, RepoSlug, CommitId, CommentId); - - Assert.NotNull(result); - var users = result.ToList(); - Assert.Equal(2, users.Count); - Assert.Equal("jsmith", users[0].Name); - Assert.Equal("jdoe", users[1].Name); - } - - [Fact] - public async Task LikeCommitCommentAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupLikeCommitComment(ProjectKey, RepoSlug, CommitId, CommentId); - var client = _fixture.CreateClient(); - - var result = await client.LikeCommitCommentAsync(ProjectKey, RepoSlug, CommitId, CommentId); - - Assert.True(result); - } - - [Fact] - public async Task UnlikeCommitCommentAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUnlikeCommitComment(ProjectKey, RepoSlug, CommitId, CommentId); - var client = _fixture.CreateClient(); - - var result = await client.UnlikeCommitCommentAsync(ProjectKey, RepoSlug, CommitId, CommentId); - - Assert.True(result); - } - - [Fact] - public async Task GetPullRequestCommentLikesAsync_ReturnsUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestCommentLikes(ProjectKey, RepoSlug, PullRequestId, CommentId); - var client = _fixture.CreateClient(); - - var result = await client.GetPullRequestCommentLikesAsync(ProjectKey, RepoSlug, PullRequestId, CommentId); - - Assert.NotNull(result); - var users = result.ToList(); - Assert.Equal(2, users.Count); - } - - [Fact] - public async Task LikePullRequestCommentAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupLikePullRequestComment(ProjectKey, RepoSlug, PullRequestId, CommentId); - var client = _fixture.CreateClient(); - - var result = await client.LikePullRequestCommentAsync(ProjectKey, RepoSlug, PullRequestId, CommentId); - - Assert.True(result); - } - - [Fact] - public async Task UnlikePullRequestCommentAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUnlikePullRequestComment(ProjectKey, RepoSlug, PullRequestId, CommentId); - var client = _fixture.CreateClient(); - - var result = await client.UnlikePullRequestCommentAsync(ProjectKey, RepoSlug, PullRequestId, CommentId); - - Assert.True(result); - } + _fixture.Reset(); + _fixture.Server.SetupLikePullRequestComment(ProjectKey, RepoSlug, PullRequestId, CommentId); + var client = _fixture.CreateClient(); + + var result = await client.LikePullRequestCommentAsync(ProjectKey, RepoSlug, PullRequestId, CommentId); + + Assert.True(result); + } + + [Fact] + public async Task UnlikePullRequestCommentAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUnlikePullRequestComment(ProjectKey, RepoSlug, PullRequestId, CommentId); + var client = _fixture.CreateClient(); + + var result = await client.UnlikePullRequestCommentAsync(ProjectKey, RepoSlug, PullRequestId, CommentId); + + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/CommitMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/CommitMockTests.cs index a123fc8..b77cb96 100644 --- a/test/Bitbucket.Net.Tests/MockTests/CommitMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/CommitMockTests.cs @@ -1,56 +1,50 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class CommitMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class CommitMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetCommitsAsync_ReturnsCommits() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommits(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var commits = await client.GetCommitsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + until: "HEAD"); + + Assert.NotNull(commits); + var commitList = commits.ToList(); + Assert.Equal(2, commitList.Count); + Assert.Contains(commitList, c => c.Message == "Initial commit"); + Assert.Contains(commitList, c => c.Message == "Add feature"); + } + + [Fact] + public async Task GetCommitAsync_ReturnsCommit() { - private readonly BitbucketMockFixture _fixture; - - public CommitMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetCommitsAsync_ReturnsCommits() - { - _fixture.Reset(); - _fixture.Server.SetupGetCommits(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var commits = await client.GetCommitsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - until: "HEAD"); - - Assert.NotNull(commits); - var commitList = commits.ToList(); - Assert.Equal(2, commitList.Count); - Assert.Contains(commitList, c => c.Message == "Initial commit"); - Assert.Contains(commitList, c => c.Message == "Add feature"); - } - - [Fact] - public async Task GetCommitAsync_ReturnsCommit() - { - _fixture.Reset(); - _fixture.Server.SetupGetCommit( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - var client = _fixture.CreateClient(); - - var commit = await client.GetCommitAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - - Assert.NotNull(commit); - Assert.Equal(TestConstants.TestCommitId, commit.Id); - Assert.Equal("Initial commit", commit.Message); - } + _fixture.Reset(); + _fixture.Server.SetupGetCommit( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + var client = _fixture.CreateClient(); + + var commit = await client.GetCommitAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + + Assert.NotNull(commit); + Assert.Equal(TestConstants.TestCommitId, commit.Id); + Assert.Equal("Initial commit", commit.Message); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/CoreExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/CoreExtendedMockTests.cs index c0b126f..aaa5cf4 100644 --- a/test/Bitbucket.Net.Tests/MockTests/CoreExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/CoreExtendedMockTests.cs @@ -1,231 +1,225 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class CoreExtendedMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class CoreExtendedMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task BrowseProjectRepositoryAsync_ReturnsBrowseItem() + { + _fixture.Reset(); + _fixture.Server.SetupBrowseRepository( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var browseItem = await client.BrowseProjectRepositoryAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + at: "refs/heads/master"); + + Assert.NotNull(browseItem); + } + + [Fact] + public async Task GetProjectRepositoryLastModifiedAsync_ReturnsLastModified() + { + _fixture.Reset(); + _fixture.Server.SetupGetLastModified( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var lastModified = await client.GetProjectRepositoryLastModifiedAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + at: "refs/heads/master"); + + Assert.NotNull(lastModified); + } + + [Fact] + public async Task GetRepositoryCompareChangesAsync_ReturnsChanges() + { + _fixture.Reset(); + _fixture.Server.SetupGetCompareChanges( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var changes = await client.GetRepositoryCompareChangesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + from: "refs/heads/feature", + to: "refs/heads/master"); + + Assert.NotNull(changes); + var changeList = changes.ToList(); + Assert.Single(changeList); + } + + [Fact] + public async Task GetCommitDiffAsync_ReturnsDifferences() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommitDiff( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + var client = _fixture.CreateClient(); + + var diff = await client.GetCommitDiffAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + + Assert.NotNull(diff); + Assert.NotNull(diff.Diffs); + } + + [Fact] + public async Task GetPullRequestMergeBaseAsync_ReturnsCommit() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestMergeBase( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var commit = await client.GetPullRequestMergeBaseAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(commit); + Assert.Equal(TestConstants.TestCommitId, commit.Id); + } + + [Fact] + public async Task CreateCommitWatchAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupCreateCommitWatch( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + var client = _fixture.CreateClient(); + + var result = await client.CreateCommitWatchAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + + Assert.True(result); + } + + [Fact] + public async Task DeleteCommitWatchAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteCommitWatch( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + var client = _fixture.CreateClient(); + + var result = await client.DeleteCommitWatchAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + + Assert.True(result); + } + + [Fact] + public async Task CreateCommitCommentAsync_ReturnsComment() + { + _fixture.Reset(); + _fixture.Server.SetupCreateCommitComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + var client = _fixture.CreateClient(); + + var commentInfo = new CommentInfo { Text = "Test comment" }; + + var comment = await client.CreateCommitCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId, + commentInfo); + + Assert.NotNull(comment); + } + + [Fact] + public async Task GetCommitCommentAsync_ReturnsComment() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommitComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId, + 1); + var client = _fixture.CreateClient(); + + var comment = await client.GetCommitCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId, + 1); + + Assert.NotNull(comment); + } + + [Fact] + public async Task UpdateCommitCommentAsync_ReturnsComment() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateCommitComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId, + 1); + var client = _fixture.CreateClient(); + + var commentText = new CommentText { Text = "Updated comment", Version = 0 }; + + var comment = await client.UpdateCommitCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId, + 1, + commentText); + + Assert.NotNull(comment); + } + + [Fact] + public async Task DeleteCommitCommentAsync_ReturnsTrue() { - private readonly BitbucketMockFixture _fixture; - - public CoreExtendedMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task BrowseProjectRepositoryAsync_ReturnsBrowseItem() - { - _fixture.Reset(); - _fixture.Server.SetupBrowseRepository( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var browseItem = await client.BrowseProjectRepositoryAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - at: "refs/heads/master"); - - Assert.NotNull(browseItem); - } - - [Fact] - public async Task GetProjectRepositoryLastModifiedAsync_ReturnsLastModified() - { - _fixture.Reset(); - _fixture.Server.SetupGetLastModified( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var lastModified = await client.GetProjectRepositoryLastModifiedAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - at: "refs/heads/master"); - - Assert.NotNull(lastModified); - } - - [Fact] - public async Task GetRepositoryCompareChangesAsync_ReturnsChanges() - { - _fixture.Reset(); - _fixture.Server.SetupGetCompareChanges( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var changes = await client.GetRepositoryCompareChangesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - from: "refs/heads/feature", - to: "refs/heads/master"); - - Assert.NotNull(changes); - var changeList = changes.ToList(); - Assert.Single(changeList); - } - - [Fact] - public async Task GetCommitDiffAsync_ReturnsDifferences() - { - _fixture.Reset(); - _fixture.Server.SetupGetCommitDiff( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - var client = _fixture.CreateClient(); - - var diff = await client.GetCommitDiffAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - - Assert.NotNull(diff); - Assert.NotNull(diff.Diffs); - } - - [Fact] - public async Task GetPullRequestMergeBaseAsync_ReturnsCommit() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestMergeBase( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var commit = await client.GetPullRequestMergeBaseAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(commit); - Assert.Equal(TestConstants.TestCommitId, commit.Id); - } - - [Fact] - public async Task CreateCommitWatchAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupCreateCommitWatch( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - var client = _fixture.CreateClient(); - - var result = await client.CreateCommitWatchAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - - Assert.True(result); - } - - [Fact] - public async Task DeleteCommitWatchAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteCommitWatch( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - var client = _fixture.CreateClient(); - - var result = await client.DeleteCommitWatchAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - - Assert.True(result); - } - - [Fact] - public async Task CreateCommitCommentAsync_ReturnsComment() - { - _fixture.Reset(); - _fixture.Server.SetupCreateCommitComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - var client = _fixture.CreateClient(); - - var commentInfo = new CommentInfo { Text = "Test comment" }; - - var comment = await client.CreateCommitCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId, - commentInfo); - - Assert.NotNull(comment); - } - - [Fact] - public async Task GetCommitCommentAsync_ReturnsComment() - { - _fixture.Reset(); - _fixture.Server.SetupGetCommitComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId, - 1); - var client = _fixture.CreateClient(); - - var comment = await client.GetCommitCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId, - 1); - - Assert.NotNull(comment); - } - - [Fact] - public async Task UpdateCommitCommentAsync_ReturnsComment() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateCommitComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId, - 1); - var client = _fixture.CreateClient(); - - var commentText = new CommentText { Text = "Updated comment", Version = 0 }; - - var comment = await client.UpdateCommitCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId, - 1, - commentText); - - Assert.NotNull(comment); - } - - [Fact] - public async Task DeleteCommitCommentAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteCommitComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId, - 1); - var client = _fixture.CreateClient(); - - var result = await client.DeleteCommitCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId, - 1, - version: 0); - - Assert.True(result); - } + _fixture.Reset(); + _fixture.Server.SetupDeleteCommitComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId, + 1); + var client = _fixture.CreateClient(); + + var result = await client.DeleteCommitCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId, + 1, + version: 0); + + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/DashboardMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DashboardMockTests.cs index d40484e..0b1ac86 100644 --- a/test/Bitbucket.Net.Tests/MockTests/DashboardMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/DashboardMockTests.cs @@ -1,50 +1,44 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class DashboardMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class DashboardMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetDashboardPullRequestsAsync_ReturnsPullRequests() { - private readonly BitbucketMockFixture _fixture; - - public DashboardMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetDashboardPullRequestsAsync_ReturnsPullRequests() - { - _fixture.Reset(); - _fixture.Server.SetupGetDashboardPullRequests(); - var client = _fixture.CreateClient(); - - var result = await client.GetDashboardPullRequestsAsync(); - - Assert.NotNull(result); - var pullRequests = result.ToList(); - Assert.Single(pullRequests); - Assert.Equal("PR Title", pullRequests[0].Title); - Assert.Equal(PullRequestStates.Open, pullRequests[0].State); - } - - [Fact] - public async Task GetDashboardPullRequestSuggestionsAsync_ReturnsSuggestions() - { - _fixture.Reset(); - _fixture.Server.SetupGetDashboardPullRequestSuggestions(); - var client = _fixture.CreateClient(); - - var result = await client.GetDashboardPullRequestSuggestionsAsync(); - - Assert.NotNull(result); - var suggestions = result.ToList(); - Assert.Single(suggestions); - Assert.NotNull(suggestions[0].FromRef); - Assert.Equal("feature/branch", suggestions[0].FromRef.DisplayId); - } + _fixture.Reset(); + _fixture.Server.SetupGetDashboardPullRequests(); + var client = _fixture.CreateClient(); + + var result = await client.GetDashboardPullRequestsAsync(); + + Assert.NotNull(result); + var pullRequests = result.ToList(); + Assert.Single(pullRequests); + Assert.Equal("PR Title", pullRequests[0].Title); + Assert.Equal(PullRequestStates.Open, pullRequests[0].State); + } + + [Fact] + public async Task GetDashboardPullRequestSuggestionsAsync_ReturnsSuggestions() + { + _fixture.Reset(); + _fixture.Server.SetupGetDashboardPullRequestSuggestions(); + var client = _fixture.CreateClient(); + + var result = await client.GetDashboardPullRequestSuggestionsAsync(); + + Assert.NotNull(result); + var suggestions = result.ToList(); + Assert.Single(suggestions); + Assert.NotNull(suggestions[0].FromRef); + Assert.Equal("feature/branch", suggestions[0].FromRef.DisplayId); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersExtendedMockTests.cs index 5d30d3d..b7ca864 100644 --- a/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersExtendedMockTests.cs @@ -1,157 +1,151 @@ -using System.Threading.Tasks; using Bitbucket.Net.Models.DefaultReviewers; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class DefaultReviewersExtendedMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class DefaultReviewersExtendedMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task CreateDefaultReviewerConditionAsync_ByProject_ReturnsCondition() { - private readonly BitbucketMockFixture _fixture; + _fixture.Reset(); + _fixture.Server.SetupCreateDefaultReviewerCondition(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); - public DefaultReviewersExtendedMockTests(BitbucketMockFixture fixture) + var condition = new DefaultReviewerPullRequestCondition { - _fixture = fixture; - } + SourceRefMatcher = new RefMatcher + { + Id = "refs/heads/feature/**", + Type = new DefaultReviewerPullRequestConditionType { Id = "PATTERN", Name = "Pattern" } + }, + TargetRefMatcher = new RefMatcher + { + Id = "refs/heads/main", + Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } + }, + RequiredApprovals = 1 + }; + + var result = await client.CreateDefaultReviewerConditionAsync( + TestConstants.TestProjectKey, + condition); + + Assert.NotNull(result); + Assert.Equal(1, result.Id); + } - [Fact] - public async Task CreateDefaultReviewerConditionAsync_ByProject_ReturnsCondition() - { - _fixture.Reset(); - _fixture.Server.SetupCreateDefaultReviewerCondition(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); + [Fact] + public async Task UpdateDefaultReviewerConditionAsync_ByProject_ReturnsCondition() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateDefaultReviewerCondition(TestConstants.TestProjectKey, "1"); + var client = _fixture.CreateClient(); - var condition = new DefaultReviewerPullRequestCondition - { - SourceRefMatcher = new RefMatcher - { - Id = "refs/heads/feature/**", - Type = new DefaultReviewerPullRequestConditionType { Id = "PATTERN", Name = "Pattern" } - }, - TargetRefMatcher = new RefMatcher - { - Id = "refs/heads/main", - Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } - }, - RequiredApprovals = 1 - }; - - var result = await client.CreateDefaultReviewerConditionAsync( - TestConstants.TestProjectKey, - condition); - - Assert.NotNull(result); - Assert.Equal(1, result.Id); - } - - [Fact] - public async Task UpdateDefaultReviewerConditionAsync_ByProject_ReturnsCondition() + var condition = new DefaultReviewerPullRequestCondition { - _fixture.Reset(); - _fixture.Server.SetupUpdateDefaultReviewerCondition(TestConstants.TestProjectKey, "1"); - var client = _fixture.CreateClient(); + Id = 1, + RequiredApprovals = 2 + }; - var condition = new DefaultReviewerPullRequestCondition - { - Id = 1, - RequiredApprovals = 2 - }; + var result = await client.UpdateDefaultReviewerConditionAsync( + TestConstants.TestProjectKey, + "1", + condition); - var result = await client.UpdateDefaultReviewerConditionAsync( - TestConstants.TestProjectKey, - "1", - condition); + Assert.NotNull(result); + } - Assert.NotNull(result); - } + [Fact] + public async Task DeleteDefaultReviewerConditionAsync_ByProject_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteDefaultReviewerCondition(TestConstants.TestProjectKey, "1"); + var client = _fixture.CreateClient(); - [Fact] - public async Task DeleteDefaultReviewerConditionAsync_ByProject_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteDefaultReviewerCondition(TestConstants.TestProjectKey, "1"); - var client = _fixture.CreateClient(); + var result = await client.DeleteDefaultReviewerConditionAsync( + TestConstants.TestProjectKey, + "1"); - var result = await client.DeleteDefaultReviewerConditionAsync( - TestConstants.TestProjectKey, - "1"); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task CreateDefaultReviewerConditionAsync_ByRepo_ReturnsCondition() + { + _fixture.Reset(); + _fixture.Server.SetupCreateRepoDefaultReviewerCondition( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - [Fact] - public async Task CreateDefaultReviewerConditionAsync_ByRepo_ReturnsCondition() + var condition = new DefaultReviewerPullRequestCondition { - _fixture.Reset(); - _fixture.Server.SetupCreateRepoDefaultReviewerCondition( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var condition = new DefaultReviewerPullRequestCondition + SourceRefMatcher = new RefMatcher { - SourceRefMatcher = new RefMatcher - { - Id = "refs/heads/feature/**", - Type = new DefaultReviewerPullRequestConditionType { Id = "PATTERN", Name = "Pattern" } - }, - TargetRefMatcher = new RefMatcher - { - Id = "refs/heads/main", - Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } - }, - RequiredApprovals = 1 - }; - - var result = await client.CreateDefaultReviewerConditionAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - condition); - - Assert.NotNull(result); - } - - [Fact] - public async Task UpdateDefaultReviewerConditionAsync_ByRepo_ReturnsCondition() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateRepoDefaultReviewerCondition( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "1"); - var client = _fixture.CreateClient(); - - var condition = new DefaultReviewerPullRequestCondition + Id = "refs/heads/feature/**", + Type = new DefaultReviewerPullRequestConditionType { Id = "PATTERN", Name = "Pattern" } + }, + TargetRefMatcher = new RefMatcher { - Id = 1, - RequiredApprovals = 2 - }; + Id = "refs/heads/main", + Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } + }, + RequiredApprovals = 1 + }; + + var result = await client.CreateDefaultReviewerConditionAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + condition); + + Assert.NotNull(result); + } - var result = await client.UpdateDefaultReviewerConditionAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "1", - condition); + [Fact] + public async Task UpdateDefaultReviewerConditionAsync_ByRepo_ReturnsCondition() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateRepoDefaultReviewerCondition( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "1"); + var client = _fixture.CreateClient(); + + var condition = new DefaultReviewerPullRequestCondition + { + Id = 1, + RequiredApprovals = 2 + }; - Assert.NotNull(result); - } + var result = await client.UpdateDefaultReviewerConditionAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "1", + condition); - [Fact] - public async Task DeleteDefaultReviewerConditionAsync_ByRepo_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteRepoDefaultReviewerCondition( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "1"); - var client = _fixture.CreateClient(); - - var result = await client.DeleteDefaultReviewerConditionAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "1"); - - Assert.True(result); - } + Assert.NotNull(result); + } + + [Fact] + public async Task DeleteDefaultReviewerConditionAsync_ByRepo_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteRepoDefaultReviewerCondition( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "1"); + var client = _fixture.CreateClient(); + + var result = await client.DeleteDefaultReviewerConditionAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "1"); + + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersMockTests.cs index 87211f9..74df521 100644 --- a/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersMockTests.cs @@ -1,65 +1,59 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class DefaultReviewersMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; - - public DefaultReviewersMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetDefaultReviewerConditionsAsync_ByProjectKey_ReturnsConditions() - { - _fixture.Reset(); - _fixture.Server.SetupGetDefaultReviewerConditions(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); +namespace Bitbucket.Net.Tests.MockTests; - var conditions = await client.GetDefaultReviewerConditionsAsync(TestConstants.TestProjectKey); +public class DefaultReviewersMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - Assert.NotNull(conditions); - } + [Fact] + public async Task GetDefaultReviewerConditionsAsync_ByProjectKey_ReturnsConditions() + { + _fixture.Reset(); + _fixture.Server.SetupGetDefaultReviewerConditions(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetDefaultReviewerConditionsAsync_ByProjectAndRepo_ReturnsConditions() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepoDefaultReviewerConditions( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + var conditions = await client.GetDefaultReviewerConditionsAsync(TestConstants.TestProjectKey); - var conditions = await client.GetDefaultReviewerConditionsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); + Assert.NotNull(conditions); + } - Assert.NotNull(conditions); - } + [Fact] + public async Task GetDefaultReviewerConditionsAsync_ByProjectAndRepo_ReturnsConditions() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepoDefaultReviewerConditions( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetDefaultReviewersAsync_ReturnsReviewers() - { - _fixture.Reset(); - _fixture.Server.SetupGetDefaultReviewers( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + var conditions = await client.GetDefaultReviewerConditionsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); - var reviewers = await client.GetDefaultReviewersAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - sourceRepoId: 1, - targetRepoId: 1, - sourceRefId: "refs/heads/feature", - targetRefId: "refs/heads/main"); + Assert.NotNull(conditions); + } - Assert.NotNull(reviewers); - } + [Fact] + public async Task GetDefaultReviewersAsync_ReturnsReviewers() + { + _fixture.Reset(); + _fixture.Server.SetupGetDefaultReviewers( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var reviewers = await client.GetDefaultReviewersAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + sourceRepoId: 1, + targetRepoId: 1, + sourceRefId: "refs/heads/feature", + targetRefId: "refs/heads/main"); + + Assert.NotNull(reviewers); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/DiffAndTagMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DiffAndTagMockTests.cs index 11c3adf..84b6892 100644 --- a/test/Bitbucket.Net.Tests/MockTests/DiffAndTagMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/DiffAndTagMockTests.cs @@ -1,97 +1,91 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class DiffAndTagMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public DiffAndTagMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class DiffAndTagMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetRepositoryDiffAsync_ReturnsDiff() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositoryDiff( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetRepositoryDiffAsync_ReturnsDiff() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryDiff( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - var diff = await client.GetRepositoryDiffAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "HEAD"); + var diff = await client.GetRepositoryDiffAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "HEAD"); - Assert.NotNull(diff); - Assert.NotNull(diff.Diffs); - Assert.NotEmpty(diff.Diffs); - } + Assert.NotNull(diff); + Assert.NotNull(diff.Diffs); + Assert.NotEmpty(diff.Diffs); + } - [Fact] - public async Task GetPullRequestDiffAsync_ReturnsDiff() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestDiff( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetPullRequestDiffAsync_ReturnsDiff() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestDiff( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var diff = await client.GetPullRequestDiffAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); + var diff = await client.GetPullRequestDiffAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); - Assert.NotNull(diff); - Assert.NotNull(diff.Diffs); - } + Assert.NotNull(diff); + Assert.NotNull(diff.Diffs); + } - [Fact] - public async Task GetCommitCommentsAsync_ReturnsComments() - { - _fixture.Reset(); - _fixture.Server.SetupGetCommentsOnFile( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetCommitCommentsAsync_ReturnsComments() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommentsOnFile( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId); + var client = _fixture.CreateClient(); - var comments = await client.GetCommitCommentsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestCommitId, - "src/main.cs"); + var comments = await client.GetCommitCommentsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestCommitId, + "src/main.cs"); - Assert.NotNull(comments); - var commentList = comments.ToList(); - Assert.Single(commentList); - Assert.Equal("This is a test comment", commentList[0].Text); - } + Assert.NotNull(comments); + var commentList = comments.ToList(); + Assert.Single(commentList); + Assert.Equal("This is a test comment", commentList[0].Text); + } - [Fact] - public async Task GetPullRequestActivitiesAsync_ReturnsActivities() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestActivities( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetPullRequestActivitiesAsync_ReturnsActivities() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestActivities( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var activities = await client.GetPullRequestActivitiesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); + var activities = await client.GetPullRequestActivitiesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); - Assert.NotNull(activities); - var activityList = activities.ToList(); - Assert.Equal(2, activityList.Count); - } + Assert.NotNull(activities); + var activityList = activities.ToList(); + Assert.Equal(2, activityList.Count); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs index d6e469b..c020f18 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs @@ -1,112 +1,106 @@ -using System.Net; -using System.Threading.Tasks; using Bitbucket.Net.Tests.Infrastructure; using Flurl.Http; +using System.Net; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +/// +/// Unit tests for error handling using WireMock. +/// Verifies that appropriate exceptions are thrown for HTTP error responses. +/// +/// +/// NOTE: The current library implementation throws FlurlHttpException directly +/// rather than the documented BitbucketApiException types. This is because +/// Flurl throws before the custom error handling can intercept the response. +/// These tests verify the actual current behavior. +/// +public class ErrorHandlingMockTests(BitbucketMockFixture fixture) : IClassFixture { - /// - /// Unit tests for error handling using WireMock. - /// Verifies that appropriate exceptions are thrown for HTTP error responses. - /// - /// - /// NOTE: The current library implementation throws FlurlHttpException directly - /// rather than the documented BitbucketApiException types. This is because - /// Flurl throws before the custom error handling can intercept the response. - /// These tests verify the actual current behavior. - /// - public class ErrorHandlingMockTests : IClassFixture + private const string ApiBasePath = "/rest/api/1.0"; + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetProjectAsync_WhenNotFound_ThrowsException() + { + // Arrange + _fixture.Reset(); + var projectKey = "NONEXISTENT"; + _fixture.Server.SetupNotFound($"{ApiBasePath}/projects/{projectKey}"); + var client = _fixture.CreateClient(); + + // Act & Assert + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal((int)HttpStatusCode.NotFound, exception.StatusCode); + } + + [Fact] + public async Task GetProjectAsync_WhenUnauthorized_ThrowsException() + { + // Arrange + _fixture.Reset(); + var projectKey = "TEST"; + _fixture.Server.SetupUnauthorized($"{ApiBasePath}/projects/{projectKey}"); + var client = _fixture.CreateClient(); + + // Act & Assert + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal((int)HttpStatusCode.Unauthorized, exception.StatusCode); + } + + [Fact] + public async Task GetProjectAsync_WhenServerError_ThrowsException() + { + // Arrange + _fixture.Reset(); + var projectKey = "TEST"; + _fixture.Server.SetupInternalServerError($"{ApiBasePath}/projects/{projectKey}"); + var client = _fixture.CreateClient(); + + // Act & Assert + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal((int)HttpStatusCode.InternalServerError, exception.StatusCode); + } + + [Fact] + public async Task GetProjectRepositoryAsync_WhenNotFound_ThrowsException() + { + // Arrange + _fixture.Reset(); + var projectKey = "TEST"; + var repoSlug = "nonexistent-repo"; + _fixture.Server.SetupNotFound($"{ApiBasePath}/projects/{projectKey}/repos/{repoSlug}"); + var client = _fixture.CreateClient(); + + // Act & Assert + var exception = await Assert.ThrowsAsync( + () => client.GetProjectRepositoryAsync(projectKey, repoSlug)); + + Assert.Equal((int)HttpStatusCode.NotFound, exception.StatusCode); + } + + [Fact] + public async Task GetPullRequestAsync_WhenNotFound_ThrowsException() { - private const string ApiBasePath = "/rest/api/1.0"; - private readonly BitbucketMockFixture _fixture; - - public ErrorHandlingMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetProjectAsync_WhenNotFound_ThrowsException() - { - // Arrange - _fixture.Reset(); - var projectKey = "NONEXISTENT"; - _fixture.Server.SetupNotFound($"{ApiBasePath}/projects/{projectKey}"); - var client = _fixture.CreateClient(); - - // Act & Assert - var exception = await Assert.ThrowsAsync( - () => client.GetProjectAsync(projectKey)); - - Assert.Equal((int)HttpStatusCode.NotFound, exception.StatusCode); - } - - [Fact] - public async Task GetProjectAsync_WhenUnauthorized_ThrowsException() - { - // Arrange - _fixture.Reset(); - var projectKey = "TEST"; - _fixture.Server.SetupUnauthorized($"{ApiBasePath}/projects/{projectKey}"); - var client = _fixture.CreateClient(); - - // Act & Assert - var exception = await Assert.ThrowsAsync( - () => client.GetProjectAsync(projectKey)); - - Assert.Equal((int)HttpStatusCode.Unauthorized, exception.StatusCode); - } - - [Fact] - public async Task GetProjectAsync_WhenServerError_ThrowsException() - { - // Arrange - _fixture.Reset(); - var projectKey = "TEST"; - _fixture.Server.SetupInternalServerError($"{ApiBasePath}/projects/{projectKey}"); - var client = _fixture.CreateClient(); - - // Act & Assert - var exception = await Assert.ThrowsAsync( - () => client.GetProjectAsync(projectKey)); - - Assert.Equal((int)HttpStatusCode.InternalServerError, exception.StatusCode); - } - - [Fact] - public async Task GetProjectRepositoryAsync_WhenNotFound_ThrowsException() - { - // Arrange - _fixture.Reset(); - var projectKey = "TEST"; - var repoSlug = "nonexistent-repo"; - _fixture.Server.SetupNotFound($"{ApiBasePath}/projects/{projectKey}/repos/{repoSlug}"); - var client = _fixture.CreateClient(); - - // Act & Assert - var exception = await Assert.ThrowsAsync( - () => client.GetProjectRepositoryAsync(projectKey, repoSlug)); - - Assert.Equal((int)HttpStatusCode.NotFound, exception.StatusCode); - } - - [Fact] - public async Task GetPullRequestAsync_WhenNotFound_ThrowsException() - { - // Arrange - _fixture.Reset(); - var projectKey = "TEST"; - var repoSlug = "test-repo"; - var pullRequestId = 99999L; - _fixture.Server.SetupNotFound($"{ApiBasePath}/projects/{projectKey}/repos/{repoSlug}/pull-requests/{pullRequestId}"); - var client = _fixture.CreateClient(); - - // Act & Assert - var exception = await Assert.ThrowsAsync( - () => client.GetPullRequestAsync(projectKey, repoSlug, pullRequestId)); - - Assert.Equal((int)HttpStatusCode.NotFound, exception.StatusCode); - } + // Arrange + _fixture.Reset(); + var projectKey = "TEST"; + var repoSlug = "test-repo"; + var pullRequestId = 99999L; + _fixture.Server.SetupNotFound($"{ApiBasePath}/projects/{projectKey}/repos/{repoSlug}/pull-requests/{pullRequestId}"); + var client = _fixture.CreateClient(); + + // Act & Assert + var exception = await Assert.ThrowsAsync( + () => client.GetPullRequestAsync(projectKey, repoSlug, pullRequestId)); + + Assert.Equal((int)HttpStatusCode.NotFound, exception.StatusCode); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs index 91b7340..a8752dc 100644 --- a/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs @@ -1,90 +1,84 @@ -using System.Threading.Tasks; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class GitAndTagMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public GitAndTagMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class GitAndTagMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetCanRebasePullRequestAsync_ReturnsCondition() - { - _fixture.Reset(); - _fixture.Server.SetupGetCanRebasePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetCanRebasePullRequestAsync_ReturnsCondition() + { + _fixture.Reset(); + _fixture.Server.SetupGetCanRebasePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var condition = await client.GetCanRebasePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); + var condition = await client.GetCanRebasePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); - Assert.NotNull(condition); - } + Assert.NotNull(condition); + } - [Fact] - public async Task RebasePullRequestAsync_RebasesSucessfully() - { - _fixture.Reset(); - _fixture.Server.SetupRebasePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task RebasePullRequestAsync_RebasesSucessfully() + { + _fixture.Reset(); + _fixture.Server.SetupRebasePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var result = await client.RebasePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - version: 1); + var result = await client.RebasePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + version: 1); - Assert.NotNull(result); - } + Assert.NotNull(result); + } - [Fact] - public async Task CreateTagAsync_CreatesTag() - { - _fixture.Reset(); - _fixture.Server.SetupCreateTag( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task CreateTagAsync_CreatesTag() + { + _fixture.Reset(); + _fixture.Server.SetupCreateTag( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - var tag = await client.CreateTagAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - Bitbucket.Net.Models.Git.TagTypes.LightWeight, - "v1.0.0", - "abc123"); + var tag = await client.CreateTagAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + Bitbucket.Net.Models.Git.TagTypes.LightWeight, + "v1.0.0", + "abc123"); - Assert.NotNull(tag); - } + Assert.NotNull(tag); + } - [Fact] - public async Task DeleteTagAsync_DeletesTag() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteTag( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "v1.0.0"); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeleteTagAsync_DeletesTag() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteTag( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "v1.0.0"); + var client = _fixture.CreateClient(); - var result = await client.DeleteTagAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "v1.0.0"); + var result = await client.DeleteTagAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "v1.0.0"); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/GitMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/GitMockTests.cs index 4c2e10d..4f0847d 100644 --- a/test/Bitbucket.Net.Tests/MockTests/GitMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/GitMockTests.cs @@ -1,74 +1,68 @@ -using System.Threading.Tasks; using Bitbucket.Net.Models.Git; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class GitMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; - private const string ProjectKey = "TEST"; - private const string RepoSlug = "test-repo"; - private const long PullRequestId = 1; +namespace Bitbucket.Net.Tests.MockTests; - public GitMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class GitMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; + private const string ProjectKey = "TEST"; + private const string RepoSlug = "test-repo"; + private const long PullRequestId = 1; - [Fact] - public async Task GetCanRebasePullRequestAsync_ReturnsCondition() - { - _fixture.Reset(); - _fixture.Server.SetupGetCanRebasePullRequest(ProjectKey, RepoSlug, PullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetCanRebasePullRequestAsync_ReturnsCondition() + { + _fixture.Reset(); + _fixture.Server.SetupGetCanRebasePullRequest(ProjectKey, RepoSlug, PullRequestId); + var client = _fixture.CreateClient(); - var result = await client.GetCanRebasePullRequestAsync(ProjectKey, RepoSlug, PullRequestId); + var result = await client.GetCanRebasePullRequestAsync(ProjectKey, RepoSlug, PullRequestId); - Assert.NotNull(result); - Assert.True(result.CanRebase); - Assert.NotNull(result.Vetoes); - Assert.Empty(result.Vetoes); - } + Assert.NotNull(result); + Assert.True(result.CanRebase); + Assert.NotNull(result.Vetoes); + Assert.Empty(result.Vetoes); + } - [Fact] - public async Task RebasePullRequestAsync_ReturnsUpdatedPullRequest() - { - _fixture.Reset(); - _fixture.Server.SetupRebasePullRequest(ProjectKey, RepoSlug, PullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task RebasePullRequestAsync_ReturnsUpdatedPullRequest() + { + _fixture.Reset(); + _fixture.Server.SetupRebasePullRequest(ProjectKey, RepoSlug, PullRequestId); + var client = _fixture.CreateClient(); - var result = await client.RebasePullRequestAsync(ProjectKey, RepoSlug, PullRequestId, version: 1); + var result = await client.RebasePullRequestAsync(ProjectKey, RepoSlug, PullRequestId, version: 1); - Assert.NotNull(result); - Assert.Equal(1, result.Id); - } + Assert.NotNull(result); + Assert.Equal(1, result.Id); + } - [Fact] - public async Task CreateTagAsync_ReturnsCreatedTag() - { - _fixture.Reset(); - _fixture.Server.SetupCreateTag(ProjectKey, RepoSlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task CreateTagAsync_ReturnsCreatedTag() + { + _fixture.Reset(); + _fixture.Server.SetupCreateTag(ProjectKey, RepoSlug); + var client = _fixture.CreateClient(); - var result = await client.CreateTagAsync(ProjectKey, RepoSlug, TagTypes.Annotated, "v1.0.0", "abc123"); + var result = await client.CreateTagAsync(ProjectKey, RepoSlug, TagTypes.Annotated, "v1.0.0", "abc123"); - Assert.NotNull(result); - Assert.Equal("v1.0.0", result.DisplayId); - Assert.Equal("refs/tags/v1.0.0", result.Id); - } + Assert.NotNull(result); + Assert.Equal("v1.0.0", result.DisplayId); + Assert.Equal("refs/tags/v1.0.0", result.Id); + } - [Fact] - public async Task DeleteTagAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteTag(ProjectKey, RepoSlug, "v1.0.0"); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeleteTagAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteTag(ProjectKey, RepoSlug, "v1.0.0"); + var client = _fixture.CreateClient(); - var result = await client.DeleteTagAsync(ProjectKey, RepoSlug, "v1.0.0"); + var result = await client.DeleteTagAsync(ProjectKey, RepoSlug, "v1.0.0"); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/GroupsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/GroupsMockTests.cs index 76f327a..b7bf1f2 100644 --- a/test/Bitbucket.Net.Tests/MockTests/GroupsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/GroupsMockTests.cs @@ -1,34 +1,28 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class GroupsMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public GroupsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class GroupsMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetGroupNamesAsync_ReturnsGroupNames() - { - _fixture.Reset(); - _fixture.Server.SetupGetGroupNames(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetGroupNamesAsync_ReturnsGroupNames() + { + _fixture.Reset(); + _fixture.Server.SetupGetGroupNames(); + var client = _fixture.CreateClient(); - var groups = await client.GetGroupNamesAsync(); + var groups = await client.GetGroupNamesAsync(); - var groupList = groups.ToList(); - Assert.NotEmpty(groupList); - Assert.Equal(3, groupList.Count); - Assert.Equal("developers", groupList[0]); - Assert.Equal("administrators", groupList[1]); - Assert.Equal("testers", groupList[2]); - } + var groupList = groups.ToList(); + Assert.NotEmpty(groupList); + Assert.Equal(3, groupList.Count); + Assert.Equal("developers", groupList[0]); + Assert.Equal("administrators", groupList[1]); + Assert.Equal("testers", groupList[2]); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/HooksMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/HooksMockTests.cs index 9e28ada..a9cd18e 100644 --- a/test/Bitbucket.Net.Tests/MockTests/HooksMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/HooksMockTests.cs @@ -1,42 +1,36 @@ -using System.Threading.Tasks; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class HooksMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class HooksMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetProjectHooksAvatarAsync_ReturnsBytes() { - private readonly BitbucketMockFixture _fixture; - - public HooksMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetProjectHooksAvatarAsync_ReturnsBytes() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectHooksAvatar("com.example.myhook"); - var client = _fixture.CreateClient(); - - var avatar = await client.GetProjectHooksAvatarAsync("com.example.myhook"); - - Assert.NotNull(avatar); - Assert.True(avatar.Length > 0); - } - - [Fact] - public async Task GetProjectHooksAvatarAsync_WithVersion_ReturnsBytes() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectHooksAvatar("com.example.myhook"); - var client = _fixture.CreateClient(); - - var avatar = await client.GetProjectHooksAvatarAsync("com.example.myhook", version: "1.0.0"); - - Assert.NotNull(avatar); - Assert.True(avatar.Length > 0); - } + _fixture.Reset(); + _fixture.Server.SetupGetProjectHooksAvatar("com.example.myhook"); + var client = _fixture.CreateClient(); + + var avatar = await client.GetProjectHooksAvatarAsync("com.example.myhook"); + + Assert.NotNull(avatar); + Assert.True(avatar.Length > 0); + } + + [Fact] + public async Task GetProjectHooksAvatarAsync_WithVersion_ReturnsBytes() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectHooksAvatar("com.example.myhook"); + var client = _fixture.CreateClient(); + + var avatar = await client.GetProjectHooksAvatarAsync("com.example.myhook", version: "1.0.0"); + + Assert.NotNull(avatar); + Assert.True(avatar.Length > 0); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/InboxMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/InboxMockTests.cs index 74caf65..e636875 100644 --- a/test/Bitbucket.Net.Tests/MockTests/InboxMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/InboxMockTests.cs @@ -1,44 +1,38 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class InboxMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class InboxMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetInboxPullRequestsAsync_ReturnsPullRequests() { - private readonly BitbucketMockFixture _fixture; - - public InboxMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetInboxPullRequestsAsync_ReturnsPullRequests() - { - _fixture.Reset(); - _fixture.Server.SetupGetInboxPullRequests(); - var client = _fixture.CreateClient(); - - var result = await client.GetInboxPullRequestsAsync(); - - Assert.NotNull(result); - var pullRequests = result.ToList(); - Assert.Single(pullRequests); - Assert.Equal("Inbox PR Title", pullRequests[0].Title); - } - - [Fact] - public async Task GetInboxPullRequestsCountAsync_ReturnsCount() - { - _fixture.Reset(); - _fixture.Server.SetupGetInboxPullRequestsCount(); - var client = _fixture.CreateClient(); - - var result = await client.GetInboxPullRequestsCountAsync(); - - Assert.Equal(5, result); - } + _fixture.Reset(); + _fixture.Server.SetupGetInboxPullRequests(); + var client = _fixture.CreateClient(); + + var result = await client.GetInboxPullRequestsAsync(); + + Assert.NotNull(result); + var pullRequests = result.ToList(); + Assert.Single(pullRequests); + Assert.Equal("Inbox PR Title", pullRequests[0].Title); + } + + [Fact] + public async Task GetInboxPullRequestsCountAsync_ReturnsCount() + { + _fixture.Reset(); + _fixture.Server.SetupGetInboxPullRequestsCount(); + var client = _fixture.CreateClient(); + + var result = await client.GetInboxPullRequestsCountAsync(); + + Assert.Equal(5, result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/JiraMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/JiraMockTests.cs index ccbd2cb..5c39c93 100644 --- a/test/Bitbucket.Net.Tests/MockTests/JiraMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/JiraMockTests.cs @@ -1,68 +1,62 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class JiraMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; - private const string ProjectKey = "PROJ"; - private const string RepoSlug = "repo"; - - public JiraMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetJiraIssuesAsync_ReturnsIssues() - { - _fixture.Reset(); - _fixture.Server.SetupGetJiraIssues(ProjectKey, RepoSlug, 1); - var client = _fixture.CreateClient(); - - var result = await client.GetJiraIssuesAsync(ProjectKey, RepoSlug, 1); +namespace Bitbucket.Net.Tests.MockTests; - Assert.NotNull(result); - var issues = result.ToList(); - Assert.Equal(2, issues.Count); - Assert.Equal("PROJ-123", issues[0].Key); - Assert.Equal("https://jira.example.com/browse/PROJ-123", issues[0].Url); - Assert.Equal("PROJ-456", issues[1].Key); - } +public class JiraMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; + private const string ProjectKey = "PROJ"; + private const string RepoSlug = "repo"; - [Fact] - public async Task CreateJiraIssueAsync_ReturnsCreatedIssue() - { - _fixture.Reset(); - _fixture.Server.SetupCreateJiraIssue(CommentId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetJiraIssuesAsync_ReturnsIssues() + { + _fixture.Reset(); + _fixture.Server.SetupGetJiraIssues(ProjectKey, RepoSlug, 1); + var client = _fixture.CreateClient(); + + var result = await client.GetJiraIssuesAsync(ProjectKey, RepoSlug, 1); + + Assert.NotNull(result); + var issues = result.ToList(); + Assert.Equal(2, issues.Count); + Assert.Equal("PROJ-123", issues[0].Key); + Assert.Equal("https://jira.example.com/browse/PROJ-123", issues[0].Url); + Assert.Equal("PROJ-456", issues[1].Key); + } - var result = await client.CreateJiraIssueAsync(CommentId, "app-id", "Test Issue", "Bug"); + [Fact] + public async Task CreateJiraIssueAsync_ReturnsCreatedIssue() + { + _fixture.Reset(); + _fixture.Server.SetupCreateJiraIssue(CommentId); + var client = _fixture.CreateClient(); - Assert.NotNull(result); - Assert.Equal(100, result.CommentId); - Assert.Equal("PROJ-789", result.IssueKey); - } + var result = await client.CreateJiraIssueAsync(CommentId, "app-id", "Test Issue", "Bug"); - [Fact] - public async Task GetChangeSetsAsync_ReturnsChangeSets() - { - _fixture.Reset(); - _fixture.Server.SetupGetChangeSets("PROJ-123"); - var client = _fixture.CreateClient(); + Assert.NotNull(result); + Assert.Equal(100, result.CommentId); + Assert.Equal("PROJ-789", result.IssueKey); + } - var result = await client.GetChangeSetsAsync("PROJ-123"); + [Fact] + public async Task GetChangeSetsAsync_ReturnsChangeSets() + { + _fixture.Reset(); + _fixture.Server.SetupGetChangeSets("PROJ-123"); + var client = _fixture.CreateClient(); - Assert.NotNull(result); - var changeSets = result.ToList(); - Assert.Single(changeSets); - Assert.NotNull(changeSets[0].ToCommit); - Assert.Equal("def456abc789", changeSets[0].ToCommit.Id); - } + var result = await client.GetChangeSetsAsync("PROJ-123"); - private const string CommentId = "100"; + Assert.NotNull(result); + var changeSets = result.ToList(); + Assert.Single(changeSets); + Assert.NotNull(changeSets[0].ToCommit); + Assert.Equal("def456abc789", changeSets[0].ToCommit.Id); } -} + + private const string CommentId = "100"; +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/LogsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/LogsMockTests.cs index 27e2b21..18caa6e 100644 --- a/test/Bitbucket.Net.Tests/MockTests/LogsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/LogsMockTests.cs @@ -1,65 +1,59 @@ -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Logs; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class LogsMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public LogsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class LogsMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetLogLevelAsync_ReturnsLogLevel() - { - _fixture.Reset(); - _fixture.Server.SetupGetLogLevel("com.atlassian.bitbucket"); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetLogLevelAsync_ReturnsLogLevel() + { + _fixture.Reset(); + _fixture.Server.SetupGetLogLevel("com.atlassian.bitbucket"); + var client = _fixture.CreateClient(); - var logLevel = await client.GetLogLevelAsync("com.atlassian.bitbucket"); + var logLevel = await client.GetLogLevelAsync("com.atlassian.bitbucket"); - Assert.Equal(LogLevels.Debug, logLevel); - } + Assert.Equal(LogLevels.Debug, logLevel); + } - [Fact] - public async Task SetLogLevelAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupSetLogLevel("com.atlassian.bitbucket", "INFO"); - var client = _fixture.CreateClient(); + [Fact] + public async Task SetLogLevelAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupSetLogLevel("com.atlassian.bitbucket", "INFO"); + var client = _fixture.CreateClient(); - var result = await client.SetLogLevelAsync("com.atlassian.bitbucket", LogLevels.Info); + var result = await client.SetLogLevelAsync("com.atlassian.bitbucket", LogLevels.Info); - Assert.True(result); - } + Assert.True(result); + } - [Fact] - public async Task GetRootLogLevelAsync_ReturnsLogLevel() - { - _fixture.Reset(); - _fixture.Server.SetupGetRootLogLevel(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetRootLogLevelAsync_ReturnsLogLevel() + { + _fixture.Reset(); + _fixture.Server.SetupGetRootLogLevel(); + var client = _fixture.CreateClient(); - var logLevel = await client.GetRootLogLevelAsync(); + var logLevel = await client.GetRootLogLevelAsync(); - Assert.Equal(LogLevels.Debug, logLevel); - } + Assert.Equal(LogLevels.Debug, logLevel); + } - [Fact] - public async Task SetRootLogLevelAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupSetRootLogLevel("WARN"); - var client = _fixture.CreateClient(); + [Fact] + public async Task SetRootLogLevelAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupSetRootLogLevel("WARN"); + var client = _fixture.CreateClient(); - var result = await client.SetRootLogLevelAsync(LogLevels.Warn); + var result = await client.SetRootLogLevelAsync(LogLevels.Warn); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/MarkupMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/MarkupMockTests.cs index d2bd7e2..8d1bccf 100644 --- a/test/Bitbucket.Net.Tests/MockTests/MarkupMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/MarkupMockTests.cs @@ -1,29 +1,23 @@ -using System.Threading.Tasks; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class MarkupMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public MarkupMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class MarkupMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task PreviewMarkupAsync_ReturnsHtml() - { - _fixture.Reset(); - _fixture.Server.SetupPreviewMarkup(); - var client = _fixture.CreateClient(); + [Fact] + public async Task PreviewMarkupAsync_ReturnsHtml() + { + _fixture.Reset(); + _fixture.Server.SetupPreviewMarkup(); + var client = _fixture.CreateClient(); - var result = await client.PreviewMarkupAsync("**Bold** text"); + var result = await client.PreviewMarkupAsync("**Bold** text"); - Assert.NotNull(result); - Assert.Contains("markdown", result); - } + Assert.NotNull(result); + Assert.Contains("markdown", result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/PersonalAccessTokensMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PersonalAccessTokensMockTests.cs index cd68d3b..d44e916 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PersonalAccessTokensMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PersonalAccessTokensMockTests.cs @@ -1,103 +1,97 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.PersonalAccessTokens; using Bitbucket.Net.Tests.Infrastructure; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class PersonalAccessTokensMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class PersonalAccessTokensMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; - private const string UserSlug = "admin"; - private const string TokenId = "token1"; + private readonly BitbucketMockFixture _fixture = fixture; + private const string UserSlug = "admin"; + private const string TokenId = "token1"; - public PersonalAccessTokensMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task GetUserAccessTokensAsync_ReturnsTokens() + { + _fixture.Reset(); + _fixture.Server.SetupGetUserAccessTokens(UserSlug); + var client = _fixture.CreateClient(); - [Fact(Skip = "Permissions List converter mismatch - uses JsonEnumConverter instead of JsonEnumListConverter")] - public async Task GetUserAccessTokensAsync_ReturnsTokens() - { - _fixture.Reset(); - _fixture.Server.SetupGetUserAccessTokens(UserSlug); - var client = _fixture.CreateClient(); + var result = await client.GetUserAccessTokensAsync(UserSlug); - var result = await client.GetUserAccessTokensAsync(UserSlug); + Assert.NotNull(result); + var tokens = result.ToList(); + Assert.Equal(2, tokens.Count); + Assert.Equal("token1", tokens[0].Id); + Assert.Equal("API Token", tokens[0].Name); + } - Assert.NotNull(result); - var tokens = result.ToList(); - Assert.Equal(2, tokens.Count); - Assert.Equal("token1", tokens[0].Id); - Assert.Equal("API Token", tokens[0].Name); - } + [Fact] + public async Task GetUserAccessTokenAsync_ReturnsToken() + { + _fixture.Reset(); + _fixture.Server.SetupGetUserAccessToken(UserSlug, TokenId); + var client = _fixture.CreateClient(); - [Fact(Skip = "Permissions List converter mismatch - uses JsonEnumConverter instead of JsonEnumListConverter")] - public async Task GetUserAccessTokenAsync_ReturnsToken() - { - _fixture.Reset(); - _fixture.Server.SetupGetUserAccessToken(UserSlug, TokenId); - var client = _fixture.CreateClient(); + var result = await client.GetUserAccessTokenAsync(UserSlug, TokenId); - var result = await client.GetUserAccessTokenAsync(UserSlug, TokenId); + Assert.NotNull(result); + Assert.Equal("token1", result.Id); + Assert.Equal("API Token", result.Name); + } - Assert.NotNull(result); - Assert.Equal("token1", result.Id); - Assert.Equal("API Token", result.Name); - } + [Fact] + public async Task CreateAccessTokenAsync_ReturnsCreatedToken() + { + _fixture.Reset(); + _fixture.Server.SetupCreateAccessToken(UserSlug); + var client = _fixture.CreateClient(); - [Fact(Skip = "Permissions enum serialization issue with source generators")] - public async Task CreateAccessTokenAsync_ReturnsCreatedToken() + var tokenCreate = new AccessTokenCreate { - _fixture.Reset(); - _fixture.Server.SetupCreateAccessToken(UserSlug); - var client = _fixture.CreateClient(); + Name = "New API Token", + Permissions = [Permissions.ProjectRead, Permissions.RepoRead] + }; - var tokenCreate = new AccessTokenCreate - { - Name = "New API Token", - Permissions = new List { Permissions.ProjectRead, Permissions.RepoRead } - }; + var result = await client.CreateAccessTokenAsync(UserSlug, tokenCreate); - var result = await client.CreateAccessTokenAsync(UserSlug, tokenCreate); + Assert.NotNull(result); + Assert.Equal("token1", result.Id); + Assert.NotNull(result.Token); + } - Assert.NotNull(result); - Assert.Equal("token1", result.Id); - Assert.NotNull(result.Token); - } + [Fact] + public async Task ChangeUserAccessTokenAsync_ReturnsUpdatedToken() + { + _fixture.Reset(); + _fixture.Server.SetupChangeUserAccessToken(UserSlug, TokenId); + var client = _fixture.CreateClient(); - [Fact(Skip = "Permissions enum serialization issue with source generators")] - public async Task ChangeUserAccessTokenAsync_ReturnsUpdatedToken() + var tokenUpdate = new AccessTokenCreate { - _fixture.Reset(); - _fixture.Server.SetupChangeUserAccessToken(UserSlug, TokenId); - var client = _fixture.CreateClient(); - - var tokenUpdate = new AccessTokenCreate - { - Name = "Updated API Token", - Permissions = new List { Permissions.ProjectAdmin, Permissions.RepoAdmin } - }; + Name = "Updated API Token", + Permissions = [Permissions.ProjectAdmin, Permissions.RepoAdmin] + }; - var result = await client.ChangeUserAccessTokenAsync(UserSlug, TokenId, tokenUpdate); + var result = await client.ChangeUserAccessTokenAsync(UserSlug, TokenId, tokenUpdate); - Assert.NotNull(result); - Assert.Equal("token1", result.Id); - } + Assert.NotNull(result); + Assert.Equal("token1", result.Id); + } - [Fact] - public async Task DeleteUserAccessTokenAsync_ReturnsSuccess() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteUserAccessToken(UserSlug, TokenId); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeleteUserAccessTokenAsync_ReturnsSuccess() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteUserAccessToken(UserSlug, TokenId); + var client = _fixture.CreateClient(); - var result = await client.DeleteUserAccessTokenAsync(UserSlug, TokenId); + var result = await client.DeleteUserAccessTokenAsync(UserSlug, TokenId); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/ProfileMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProfileMockTests.cs index b71eee3..eb1e341 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProfileMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProfileMockTests.cs @@ -1,33 +1,27 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class ProfileMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public ProfileMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class ProfileMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetRecentReposAsync_ReturnsRepositories() - { - _fixture.Reset(); - _fixture.Server.SetupGetRecentRepos(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetRecentReposAsync_ReturnsRepositories() + { + _fixture.Reset(); + _fixture.Server.SetupGetRecentRepos(); + var client = _fixture.CreateClient(); - var result = await client.GetRecentReposAsync(); + var result = await client.GetRecentReposAsync(); - Assert.NotNull(result); - var repos = result.ToList(); - Assert.Single(repos); - Assert.Equal("recent-repo", repos[0].Slug); - Assert.Equal("Recent Repository", repos[0].Name); - } + Assert.NotNull(result); + var repos = result.ToList(); + Assert.Single(repos); + Assert.Equal("recent-repo", repos[0].Slug); + Assert.Equal("Recent Repository", repos[0].Name); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/ProjectCrudMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProjectCrudMockTests.cs index d198987..b97695c 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProjectCrudMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProjectCrudMockTests.cs @@ -1,69 +1,63 @@ -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class ProjectCrudMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class ProjectCrudMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; + private readonly BitbucketMockFixture _fixture = fixture; - public ProjectCrudMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task CreateProjectAsync_ReturnsCreatedProject() + { + _fixture.Reset(); + _fixture.Server.SetupCreateProject(); + var client = _fixture.CreateClient(); - [Fact] - public async Task CreateProjectAsync_ReturnsCreatedProject() + var projectDef = new ProjectDefinition { - _fixture.Reset(); - _fixture.Server.SetupCreateProject(); - var client = _fixture.CreateClient(); + Key = TestConstants.TestProjectKey, + Name = TestConstants.TestProjectName, + Description = "Created by unit test" + }; - var projectDef = new ProjectDefinition - { - Key = TestConstants.TestProjectKey, - Name = TestConstants.TestProjectName, - Description = "Created by unit test" - }; + var project = await client.CreateProjectAsync(projectDef); - var project = await client.CreateProjectAsync(projectDef); + Assert.NotNull(project); + Assert.Equal(TestConstants.TestProjectKey, project.Key); + Assert.Equal(TestConstants.TestProjectName, project.Name); + } - Assert.NotNull(project); - Assert.Equal(TestConstants.TestProjectKey, project.Key); - Assert.Equal(TestConstants.TestProjectName, project.Name); - } + [Fact] + public async Task UpdateProjectAsync_ReturnsUpdatedProject() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateProject(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateProjectAsync_ReturnsUpdatedProject() + var projectDef = new ProjectDefinition { - _fixture.Reset(); - _fixture.Server.SetupUpdateProject(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var projectDef = new ProjectDefinition - { - Name = "Updated Name", - Description = "Updated by unit test" - }; + Name = "Updated Name", + Description = "Updated by unit test" + }; - var project = await client.UpdateProjectAsync(TestConstants.TestProjectKey, projectDef); + var project = await client.UpdateProjectAsync(TestConstants.TestProjectKey, projectDef); - Assert.NotNull(project); - Assert.Equal(TestConstants.TestProjectKey, project.Key); - } + Assert.NotNull(project); + Assert.Equal(TestConstants.TestProjectKey, project.Key); + } - [Fact] - public async Task DeleteProjectAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteProject(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeleteProjectAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteProject(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); - var result = await client.DeleteProjectAsync(TestConstants.TestProjectKey); + var result = await client.DeleteProjectAsync(TestConstants.TestProjectKey); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/ProjectMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProjectMockTests.cs index cf7de5d..c0264d8 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProjectMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProjectMockTests.cs @@ -1,78 +1,72 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - /// - /// Unit tests for project-related operations using WireMock. - /// - public class ProjectMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public ProjectMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +/// +/// Unit tests for project-related operations using WireMock. +/// +public class ProjectMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetProjectsAsync_ReturnsProjects() - { - // Arrange - _fixture.Reset(); - _fixture.Server.SetupGetProjects(); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetProjectsAsync_ReturnsProjects() + { + // Arrange + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClient(); - // Act - var projects = await client.GetProjectsAsync(); + // Act + var projects = await client.GetProjectsAsync(); - // Assert - Assert.NotNull(projects); - var projectList = projects.ToList(); - Assert.Single(projectList); - var project = projectList[0]; - Assert.Equal(TestConstants.TestProjectKey, project.Key); - Assert.Equal(TestConstants.TestProjectName, project.Name); - } + // Assert + Assert.NotNull(projects); + var projectList = projects.ToList(); + Assert.Single(projectList); + var project = projectList[0]; + Assert.Equal(TestConstants.TestProjectKey, project.Key); + Assert.Equal(TestConstants.TestProjectName, project.Name); + } - [Fact] - public async Task GetProjectAsync_WithValidKey_ReturnsProject() - { - // Arrange - _fixture.Reset(); - _fixture.Server.SetupGetProject(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetProjectAsync_WithValidKey_ReturnsProject() + { + // Arrange + _fixture.Reset(); + _fixture.Server.SetupGetProject(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); - // Act - var project = await client.GetProjectAsync(TestConstants.TestProjectKey); + // Act + var project = await client.GetProjectAsync(TestConstants.TestProjectKey); - // Assert - Assert.NotNull(project); - Assert.Equal(TestConstants.TestProjectKey, project.Key); - Assert.Equal(TestConstants.TestProjectName, project.Name); - Assert.NotNull(project.Description); - } + // Assert + Assert.NotNull(project); + Assert.Equal(TestConstants.TestProjectKey, project.Key); + Assert.Equal(TestConstants.TestProjectName, project.Name); + Assert.NotNull(project.Description); + } - [Fact] - public async Task GetProjectRepositoriesAsync_ReturnsRepositories() - { - // Arrange - _fixture.Reset(); - _fixture.Server.SetupGetRepositories(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetProjectRepositoriesAsync_ReturnsRepositories() + { + // Arrange + _fixture.Reset(); + _fixture.Server.SetupGetRepositories(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); - // Act - var repositories = await client.GetProjectRepositoriesAsync(TestConstants.TestProjectKey); + // Act + var repositories = await client.GetProjectRepositoriesAsync(TestConstants.TestProjectKey); - // Assert - Assert.NotNull(repositories); - var repoList = repositories.ToList(); - Assert.Single(repoList); - var repo = repoList[0]; - Assert.Equal(TestConstants.TestRepositorySlug, repo.Slug); - Assert.Equal(TestConstants.TestRepositoryName, repo.Name); - } + // Assert + Assert.NotNull(repositories); + var repoList = repositories.ToList(); + Assert.Single(repoList); + var repo = repoList[0]; + Assert.Equal(TestConstants.TestRepositorySlug, repo.Slug); + Assert.Equal(TestConstants.TestRepositoryName, repo.Name); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/ProjectPermissionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProjectPermissionsMockTests.cs index 2d62531..05bda12 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProjectPermissionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProjectPermissionsMockTests.cs @@ -1,171 +1,165 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class ProjectPermissionsMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class ProjectPermissionsMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetProjectUserPermissionsAsync_ReturnsUserPermissions() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectUserPermissions(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var permissions = await client.GetProjectUserPermissionsAsync(TestConstants.TestProjectKey); + + Assert.NotNull(permissions); + var permissionList = permissions.ToList(); + Assert.Single(permissionList); + Assert.NotNull(permissionList[0].User); + Assert.Equal("testuser", permissionList[0].User!.Name); + Assert.Equal(Permissions.ProjectAdmin, permissionList[0].Permission); + } + + [Fact] + public async Task DeleteProjectUserPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteProjectUserPermissions(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var result = await client.DeleteProjectUserPermissionsAsync(TestConstants.TestProjectKey, "testuser"); + + Assert.True(result); + } + + [Fact] + public async Task UpdateProjectUserPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateProjectUserPermissions(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var result = await client.UpdateProjectUserPermissionsAsync( + TestConstants.TestProjectKey, + "testuser", + Permissions.ProjectAdmin); + + Assert.True(result); + } + + [Fact] + public async Task GetProjectUserPermissionsNoneAsync_ReturnsLicensedUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectUserPermissionsNone(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var users = await client.GetProjectUserPermissionsNoneAsync(TestConstants.TestProjectKey); + + Assert.NotNull(users); + var userList = users.ToList(); + Assert.Single(userList); + } + + [Fact] + public async Task GetProjectGroupPermissionsAsync_ReturnsGroupPermissions() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectGroupPermissions(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var permissions = await client.GetProjectGroupPermissionsAsync(TestConstants.TestProjectKey); + + Assert.NotNull(permissions); + var permissionList = permissions.ToList(); + Assert.Single(permissionList); + Assert.NotNull(permissionList[0].Group); + Assert.Equal("developers", permissionList[0].Group!.Name); + Assert.Equal(Permissions.ProjectWrite, permissionList[0].Permission); + } + + [Fact] + public async Task DeleteProjectGroupPermissionsAsync_ReturnsTrue() { - private readonly BitbucketMockFixture _fixture; - - public ProjectPermissionsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetProjectUserPermissionsAsync_ReturnsUserPermissions() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectUserPermissions(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var permissions = await client.GetProjectUserPermissionsAsync(TestConstants.TestProjectKey); - - Assert.NotNull(permissions); - var permissionList = permissions.ToList(); - Assert.Single(permissionList); - Assert.NotNull(permissionList[0].User); - Assert.Equal("testuser", permissionList[0].User!.Name); - Assert.Equal(Permissions.ProjectAdmin, permissionList[0].Permission); - } - - [Fact] - public async Task DeleteProjectUserPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteProjectUserPermissions(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var result = await client.DeleteProjectUserPermissionsAsync(TestConstants.TestProjectKey, "testuser"); - - Assert.True(result); - } - - [Fact] - public async Task UpdateProjectUserPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateProjectUserPermissions(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var result = await client.UpdateProjectUserPermissionsAsync( - TestConstants.TestProjectKey, - "testuser", - Permissions.ProjectAdmin); - - Assert.True(result); - } - - [Fact] - public async Task GetProjectUserPermissionsNoneAsync_ReturnsLicensedUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectUserPermissionsNone(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var users = await client.GetProjectUserPermissionsNoneAsync(TestConstants.TestProjectKey); - - Assert.NotNull(users); - var userList = users.ToList(); - Assert.Single(userList); - } - - [Fact] - public async Task GetProjectGroupPermissionsAsync_ReturnsGroupPermissions() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectGroupPermissions(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var permissions = await client.GetProjectGroupPermissionsAsync(TestConstants.TestProjectKey); - - Assert.NotNull(permissions); - var permissionList = permissions.ToList(); - Assert.Single(permissionList); - Assert.NotNull(permissionList[0].Group); - Assert.Equal("developers", permissionList[0].Group!.Name); - Assert.Equal(Permissions.ProjectWrite, permissionList[0].Permission); - } - - [Fact] - public async Task DeleteProjectGroupPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteProjectGroupPermissions(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var result = await client.DeleteProjectGroupPermissionsAsync(TestConstants.TestProjectKey, "developers"); - - Assert.True(result); - } - - [Fact] - public async Task UpdateProjectGroupPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateProjectGroupPermissions(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var result = await client.UpdateProjectGroupPermissionsAsync( - TestConstants.TestProjectKey, - "developers", - Permissions.ProjectWrite); - - Assert.True(result); - } - - [Fact] - public async Task GetProjectGroupPermissionsNoneAsync_ReturnsLicensedUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectGroupPermissionsNone(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var users = await client.GetProjectGroupPermissionsNoneAsync(TestConstants.TestProjectKey); - - Assert.NotNull(users); - var userList = users.ToList(); - Assert.Single(userList); - } - - [Fact] - public async Task IsProjectDefaultPermissionAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectDefaultPermission(TestConstants.TestProjectKey, "PROJECT_READ"); - var client = _fixture.CreateClient(); - - var result = await client.IsProjectDefaultPermissionAsync(TestConstants.TestProjectKey, Permissions.ProjectRead); - - Assert.True(result); - } - - [Fact] - public async Task GrantProjectPermissionToAllAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupSetProjectDefaultPermission(TestConstants.TestProjectKey, "PROJECT_READ"); - var client = _fixture.CreateClient(); - - var result = await client.GrantProjectPermissionToAllAsync(TestConstants.TestProjectKey, Permissions.ProjectRead); - - Assert.True(result); - } - - [Fact] - public async Task RevokeProjectPermissionFromAllAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupSetProjectDefaultPermission(TestConstants.TestProjectKey, "PROJECT_READ"); - var client = _fixture.CreateClient(); - - var result = await client.RevokeProjectPermissionFromAllAsync(TestConstants.TestProjectKey, Permissions.ProjectRead); - - Assert.True(result); - } + _fixture.Reset(); + _fixture.Server.SetupDeleteProjectGroupPermissions(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var result = await client.DeleteProjectGroupPermissionsAsync(TestConstants.TestProjectKey, "developers"); + + Assert.True(result); + } + + [Fact] + public async Task UpdateProjectGroupPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateProjectGroupPermissions(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var result = await client.UpdateProjectGroupPermissionsAsync( + TestConstants.TestProjectKey, + "developers", + Permissions.ProjectWrite); + + Assert.True(result); + } + + [Fact] + public async Task GetProjectGroupPermissionsNoneAsync_ReturnsLicensedUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectGroupPermissionsNone(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var users = await client.GetProjectGroupPermissionsNoneAsync(TestConstants.TestProjectKey); + + Assert.NotNull(users); + var userList = users.ToList(); + Assert.Single(userList); + } + + [Fact] + public async Task IsProjectDefaultPermissionAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectDefaultPermission(TestConstants.TestProjectKey, "PROJECT_READ"); + var client = _fixture.CreateClient(); + + var result = await client.IsProjectDefaultPermissionAsync(TestConstants.TestProjectKey, Permissions.ProjectRead); + + Assert.True(result); + } + + [Fact] + public async Task GrantProjectPermissionToAllAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupSetProjectDefaultPermission(TestConstants.TestProjectKey, "PROJECT_READ"); + var client = _fixture.CreateClient(); + + var result = await client.GrantProjectPermissionToAllAsync(TestConstants.TestProjectKey, Permissions.ProjectRead); + + Assert.True(result); + } + + [Fact] + public async Task RevokeProjectPermissionFromAllAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupSetProjectDefaultPermission(TestConstants.TestProjectKey, "PROJECT_READ"); + var client = _fixture.CreateClient(); + + var result = await client.RevokeProjectPermissionFromAllAsync(TestConstants.TestProjectKey, Permissions.ProjectRead); + + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs index cb0c83c..4526a24 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs @@ -1,127 +1,121 @@ +using Bitbucket.Net.Models.Core.Admin; +using Bitbucket.Net.Tests.Infrastructure; using System.IO; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Models.Core.Admin; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class ProjectSettingsMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class ProjectSettingsMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetProjectPullRequestsMergeStrategiesAsync_ReturnsSettings() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectPullRequestsMergeStrategies(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var result = await client.GetProjectPullRequestsMergeStrategiesAsync( + TestConstants.TestProjectKey, + "git"); + + Assert.NotNull(result); + } + + [Fact] + public async Task UpdateProjectPullRequestsMergeStrategiesAsync_ReturnsStrategies() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateProjectPullRequestsMergeStrategies(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var strategies = new MergeStrategies(); + var result = await client.UpdateProjectPullRequestsMergeStrategiesAsync( + TestConstants.TestProjectKey, + "git", + strategies); + + Assert.NotNull(result); + } + + [Fact] + public async Task BrowseProjectRepositoryPathAsync_ReturnsBrowseResult() + { + _fixture.Reset(); + _fixture.Server.SetupBrowseProjectRepositoryPath( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.BrowseProjectRepositoryPathAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + path: "src", + at: "refs/heads/main"); + + Assert.NotNull(result); + } + + [Fact] + public async Task GetRawFileContentStreamAsync_ReturnsStream() + { + _fixture.Reset(); + _fixture.Server.SetupGetRawFileContentStream( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + using var stream = await client.GetRawFileContentStreamAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + path: "README.md", + at: "refs/heads/main"); + + Assert.NotNull(stream); + using var reader = new StreamReader(stream); + var content = await reader.ReadToEndAsync(); + Assert.NotEmpty(content); + } + + [Fact] + public async Task GetProjectRepositoryTagsAsync_ReturnsTags() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectRepositoryTags( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.GetProjectRepositoryTagsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + filterText: "", + orderBy: Bitbucket.Net.Models.Core.Projects.BranchOrderBy.Alphabetical); + + Assert.NotNull(result); + var tags = result.ToList(); + Assert.NotEmpty(tags); + } + + [Fact] + public async Task CreateProjectRepositoryTagAsync_CreatesTag() { - private readonly BitbucketMockFixture _fixture; - - public ProjectSettingsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetProjectPullRequestsMergeStrategiesAsync_ReturnsSettings() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectPullRequestsMergeStrategies(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var result = await client.GetProjectPullRequestsMergeStrategiesAsync( - TestConstants.TestProjectKey, - "git"); - - Assert.NotNull(result); - } - - [Fact] - public async Task UpdateProjectPullRequestsMergeStrategiesAsync_ReturnsStrategies() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateProjectPullRequestsMergeStrategies(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var strategies = new MergeStrategies(); - var result = await client.UpdateProjectPullRequestsMergeStrategiesAsync( - TestConstants.TestProjectKey, - "git", - strategies); - - Assert.NotNull(result); - } - - [Fact] - public async Task BrowseProjectRepositoryPathAsync_ReturnsBrowseResult() - { - _fixture.Reset(); - _fixture.Server.SetupBrowseProjectRepositoryPath( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.BrowseProjectRepositoryPathAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - path: "src", - at: "refs/heads/main"); - - Assert.NotNull(result); - } - - [Fact] - public async Task GetRawFileContentStreamAsync_ReturnsStream() - { - _fixture.Reset(); - _fixture.Server.SetupGetRawFileContentStream( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - using var stream = await client.GetRawFileContentStreamAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - path: "README.md", - at: "refs/heads/main"); - - Assert.NotNull(stream); - using var reader = new StreamReader(stream); - var content = await reader.ReadToEndAsync(); - Assert.NotEmpty(content); - } - - [Fact] - public async Task GetProjectRepositoryTagsAsync_ReturnsTags() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectRepositoryTags( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.GetProjectRepositoryTagsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - filterText: "", - orderBy: Bitbucket.Net.Models.Core.Projects.BranchOrderBy.Alphabetical); - - Assert.NotNull(result); - var tags = result.ToList(); - Assert.NotEmpty(tags); - } - - [Fact] - public async Task CreateProjectRepositoryTagAsync_CreatesTag() - { - _fixture.Reset(); - _fixture.Server.SetupCreateProjectRepositoryTag( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.CreateProjectRepositoryTagAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - name: "v1.0.0", - startPoint: "abc123", - message: "Release v1.0.0"); - - Assert.NotNull(result); - } + _fixture.Reset(); + _fixture.Server.SetupCreateProjectRepositoryTag( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.CreateProjectRepositoryTagAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + name: "v1.0.0", + startPoint: "abc123", + message: "Release v1.0.0"); + + Assert.NotNull(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestActionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestActionsMockTests.cs index 1dd104f..23617f7 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestActionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestActionsMockTests.cs @@ -1,94 +1,88 @@ -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class PullRequestActionsMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public PullRequestActionsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class PullRequestActionsMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task ApprovePullRequestAsync_ReturnsReviewer() - { - _fixture.Reset(); - _fixture.Server.SetupApprovePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task ApprovePullRequestAsync_ReturnsReviewer() + { + _fixture.Reset(); + _fixture.Server.SetupApprovePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var reviewer = await client.ApprovePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); + var reviewer = await client.ApprovePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); - Assert.NotNull(reviewer); - Assert.True(reviewer.Approved); - Assert.Equal(ParticipantStatus.Approved, reviewer.Status); - } + Assert.NotNull(reviewer); + Assert.True(reviewer.Approved); + Assert.Equal(ParticipantStatus.Approved, reviewer.Status); + } - [Fact] - public async Task MergePullRequestAsync_ReturnsMergedPullRequest() - { - _fixture.Reset(); - _fixture.Server.SetupMergePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task MergePullRequestAsync_ReturnsMergedPullRequest() + { + _fixture.Reset(); + _fixture.Server.SetupMergePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var pullRequest = await client.MergePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); + var pullRequest = await client.MergePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); - Assert.NotNull(pullRequest); - Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); - } + Assert.NotNull(pullRequest); + Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); + } - [Fact] - public async Task DeclinePullRequestAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeclinePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeclinePullRequestAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeclinePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var result = await client.DeclinePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); + var result = await client.DeclinePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); - Assert.True(result); - } + Assert.True(result); + } - [Fact] - public async Task GetPullRequestMergeStateAsync_ReturnsMergeState() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestMergeState( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetPullRequestMergeStateAsync_ReturnsMergeState() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestMergeState( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var mergeState = await client.GetPullRequestMergeStateAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); + var mergeState = await client.GetPullRequestMergeStateAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); - Assert.NotNull(mergeState); - Assert.True(mergeState.CanMerge); - Assert.False(mergeState.Conflicted); - } + Assert.NotNull(mergeState); + Assert.True(mergeState.CanMerge); + Assert.False(mergeState.Conflicted); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestBlockerMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestBlockerMockTests.cs index 0ab7cfb..986b8f9 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestBlockerMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestBlockerMockTests.cs @@ -1,144 +1,138 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class PullRequestBlockerMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public PullRequestBlockerMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class PullRequestBlockerMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; #pragma warning disable CS0618 // Type or member is obsolete - [Fact] - public async Task GetPullRequestTaskCountAsync_ReturnsTaskCount() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestTaskCount( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var taskCount = await client.GetPullRequestTaskCountAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(taskCount); - Assert.Equal(2, taskCount.Open); - Assert.Equal(1, taskCount.Resolved); - } + [Fact] + public async Task GetPullRequestTaskCountAsync_ReturnsTaskCount() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestTaskCount( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var taskCount = await client.GetPullRequestTaskCountAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(taskCount); + Assert.Equal(2, taskCount.Open); + Assert.Equal(1, taskCount.Resolved); + } #pragma warning restore CS0618 - [Fact] - public async Task GetPullRequestBlockerCommentsAsync_ReturnsBlockerComments() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestBlockerComments( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var blockerComments = await client.GetPullRequestBlockerCommentsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(blockerComments); - var commentList = blockerComments.ToList(); - Assert.Single(commentList); - Assert.Equal(1, commentList[0].Id); - Assert.Equal(BlockerCommentState.Open, commentList[0].State); - } - - [Fact] - public async Task GetPullRequestBlockerCommentAsync_ReturnsBlockerComment() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestBlockerComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - 1); - var client = _fixture.CreateClient(); - - var blockerComment = await client.GetPullRequestBlockerCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - 1); - - Assert.NotNull(blockerComment); - Assert.Equal(1, blockerComment.Id); - Assert.Equal("Please fix this issue before merging", blockerComment.Text); - } - - [Fact] - public async Task CreatePullRequestBlockerCommentAsync_ReturnsBlockerComment() - { - _fixture.Reset(); - _fixture.Server.SetupCreatePullRequestBlockerComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var blockerComment = await client.CreatePullRequestBlockerCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - "Please fix this issue before merging"); - - Assert.NotNull(blockerComment); - Assert.Equal(1, blockerComment.Id); - } - - [Fact] - public async Task DeletePullRequestBlockerCommentAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeletePullRequestBlockerComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - 1); - var client = _fixture.CreateClient(); - - var result = await client.DeletePullRequestBlockerCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - blockerCommentId: 1, - version: 0); - - Assert.True(result); - } - - [Fact] - public async Task GetPullRequestMergeBaseAsync_ReturnsCommit() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestMergeBase( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var commit = await client.GetPullRequestMergeBaseAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(commit); - Assert.NotNull(commit.Id); - } + [Fact] + public async Task GetPullRequestBlockerCommentsAsync_ReturnsBlockerComments() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestBlockerComments( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var blockerComments = await client.GetPullRequestBlockerCommentsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(blockerComments); + var commentList = blockerComments.ToList(); + Assert.Single(commentList); + Assert.Equal(1, commentList[0].Id); + Assert.Equal(BlockerCommentState.Open, commentList[0].State); + } + + [Fact] + public async Task GetPullRequestBlockerCommentAsync_ReturnsBlockerComment() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestBlockerComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + 1); + var client = _fixture.CreateClient(); + + var blockerComment = await client.GetPullRequestBlockerCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + 1); + + Assert.NotNull(blockerComment); + Assert.Equal(1, blockerComment.Id); + Assert.Equal("Please fix this issue before merging", blockerComment.Text); + } + + [Fact] + public async Task CreatePullRequestBlockerCommentAsync_ReturnsBlockerComment() + { + _fixture.Reset(); + _fixture.Server.SetupCreatePullRequestBlockerComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var blockerComment = await client.CreatePullRequestBlockerCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + "Please fix this issue before merging"); + + Assert.NotNull(blockerComment); + Assert.Equal(1, blockerComment.Id); + } + + [Fact] + public async Task DeletePullRequestBlockerCommentAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeletePullRequestBlockerComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + 1); + var client = _fixture.CreateClient(); + + var result = await client.DeletePullRequestBlockerCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + blockerCommentId: 1, + version: 0); + + Assert.True(result); + } + + [Fact] + public async Task GetPullRequestMergeBaseAsync_ReturnsCommit() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestMergeBase( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var commit = await client.GetPullRequestMergeBaseAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(commit); + Assert.NotNull(commit.Id); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestCommentMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestCommentMockTests.cs index 48f1618..16e2300 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestCommentMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestCommentMockTests.cs @@ -1,102 +1,96 @@ -using System.Threading.Tasks; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class PullRequestCommentMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public PullRequestCommentMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class PullRequestCommentMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task CreatePullRequestCommentAsync_ReturnsComment() - { - _fixture.Reset(); - _fixture.Server.SetupCreatePullRequestComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task CreatePullRequestCommentAsync_ReturnsComment() + { + _fixture.Reset(); + _fixture.Server.SetupCreatePullRequestComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var result = await client.CreatePullRequestCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - "This is a new comment"); + var result = await client.CreatePullRequestCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + "This is a new comment"); - Assert.NotNull(result); - Assert.Equal(101, result.Id); - Assert.Equal("This is a new comment", result.Text); - } + Assert.NotNull(result); + Assert.Equal(101, result.Id); + Assert.Equal("This is a new comment", result.Text); + } - [Fact] - public async Task GetPullRequestCommentAsync_ReturnsComment() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - 101); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetPullRequestCommentAsync_ReturnsComment() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + 101); + var client = _fixture.CreateClient(); - var result = await client.GetPullRequestCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - 101); + var result = await client.GetPullRequestCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + 101); - Assert.NotNull(result); - Assert.Equal(101, result.Id); - } + Assert.NotNull(result); + Assert.Equal(101, result.Id); + } - [Fact] - public async Task UpdatePullRequestCommentAsync_ReturnsUpdatedComment() - { - _fixture.Reset(); - _fixture.Server.SetupUpdatePullRequestComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - 101); - var client = _fixture.CreateClient(); + [Fact] + public async Task UpdatePullRequestCommentAsync_ReturnsUpdatedComment() + { + _fixture.Reset(); + _fixture.Server.SetupUpdatePullRequestComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + 101); + var client = _fixture.CreateClient(); - var result = await client.UpdatePullRequestCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - 101, - 0, - "Updated comment text"); + var result = await client.UpdatePullRequestCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + 101, + 0, + "Updated comment text"); - Assert.NotNull(result); - Assert.Equal(101, result.Id); - } + Assert.NotNull(result); + Assert.Equal(101, result.Id); + } - [Fact] - public async Task DeletePullRequestCommentAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeletePullRequestComment( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - 101); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeletePullRequestCommentAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeletePullRequestComment( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + 101); + var client = _fixture.CreateClient(); - var result = await client.DeletePullRequestCommentAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - 101, - 0); + var result = await client.DeletePullRequestCommentAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + 101, + 0); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestCrudMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestCrudMockTests.cs index 381d70f..e816a1d 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestCrudMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestCrudMockTests.cs @@ -1,83 +1,77 @@ -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class PullRequestCrudMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class PullRequestCrudMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; + private readonly BitbucketMockFixture _fixture = fixture; - public PullRequestCrudMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task CreatePullRequestAsync_ReturnsCreatedPullRequest() + { + _fixture.Reset(); + _fixture.Server.SetupCreatePullRequest(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - [Fact] - public async Task CreatePullRequestAsync_ReturnsCreatedPullRequest() + var prInfo = new PullRequestInfo { - _fixture.Reset(); - _fixture.Server.SetupCreatePullRequest(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var prInfo = new PullRequestInfo + Title = "Test PR", + Description = "Test description", + FromRef = new FromToRef { - Title = "Test PR", - Description = "Test description", - FromRef = new FromToRef + Id = "refs/heads/feature-test", + Repository = new RepositoryRef { - Id = "refs/heads/feature-test", - Repository = new RepositoryRef - { - Slug = TestConstants.TestRepositorySlug, - Project = new ProjectRef { Key = TestConstants.TestProjectKey } - } - }, - ToRef = new FromToRef + Slug = TestConstants.TestRepositorySlug, + Project = new ProjectRef { Key = TestConstants.TestProjectKey } + } + }, + ToRef = new FromToRef + { + Id = "refs/heads/master", + Repository = new RepositoryRef { - Id = "refs/heads/master", - Repository = new RepositoryRef - { - Slug = TestConstants.TestRepositorySlug, - Project = new ProjectRef { Key = TestConstants.TestProjectKey } - } + Slug = TestConstants.TestRepositorySlug, + Project = new ProjectRef { Key = TestConstants.TestProjectKey } } - }; + } + }; - var pullRequest = await client.CreatePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - prInfo); + var pullRequest = await client.CreatePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + prInfo); - Assert.NotNull(pullRequest); - Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); - } + Assert.NotNull(pullRequest); + Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); + } - [Fact] - public async Task UpdatePullRequestAsync_ReturnsUpdatedPullRequest() - { - _fixture.Reset(); - _fixture.Server.SetupUpdatePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task UpdatePullRequestAsync_ReturnsUpdatedPullRequest() + { + _fixture.Reset(); + _fixture.Server.SetupUpdatePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var prUpdate = new PullRequestUpdate - { - Title = "Updated Title", - Version = 0 - }; + var prUpdate = new PullRequestUpdate + { + Title = "Updated Title", + Version = 0 + }; - var pullRequest = await client.UpdatePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - prUpdate); + var pullRequest = await client.UpdatePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + prUpdate); - Assert.NotNull(pullRequest); - Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); - } + Assert.NotNull(pullRequest); + Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestExtendedMockTests.cs index 8925645..add9aef 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestExtendedMockTests.cs @@ -1,206 +1,200 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class PullRequestExtendedMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class PullRequestExtendedMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetPullRequestActivitiesAsync_ReturnsActivities() { - private readonly BitbucketMockFixture _fixture; + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestActivities( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var activities = await client.GetPullRequestActivitiesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(activities); + var activityList = activities.ToList(); + Assert.Equal(2, activityList.Count); + } - public PullRequestExtendedMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task GetPullRequestChangesAsync_ReturnsChanges() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestChanges( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var changes = await client.GetPullRequestChangesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(changes); + var changeList = changes.ToList(); + Assert.Single(changeList); + } - [Fact] - public async Task GetPullRequestActivitiesAsync_ReturnsActivities() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestActivities( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var activities = await client.GetPullRequestActivitiesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(activities); - var activityList = activities.ToList(); - Assert.Equal(2, activityList.Count); - } - - [Fact] - public async Task GetPullRequestChangesAsync_ReturnsChanges() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestChanges( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var changes = await client.GetPullRequestChangesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(changes); - var changeList = changes.ToList(); - Assert.Single(changeList); - } - - [Fact] - public async Task GetPullRequestCommitsAsync_ReturnsCommits() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestCommits( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var commits = await client.GetPullRequestCommitsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(commits); - var commitList = commits.ToList(); - Assert.Equal(2, commitList.Count); - Assert.Contains(commitList, c => c.Message == "Initial commit"); - } - - [Fact] - public async Task GetPullRequestMergeStateAsync_ReturnsMergeState() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestMergeState( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var mergeState = await client.GetPullRequestMergeStateAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(mergeState); - Assert.True(mergeState.CanMerge); - Assert.False(mergeState.Conflicted); - } - - [Fact] - public async Task DeclinePullRequestAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeclinePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var result = await client.DeclinePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.True(result); - } - - [Fact] - public async Task MergePullRequestAsync_ReturnsPullRequest() - { - _fixture.Reset(); - _fixture.Server.SetupMergePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var result = await client.MergePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(result); - Assert.Equal(TestConstants.TestPullRequestId, result.Id); - } - - [Fact] - public async Task ApprovePullRequestAsync_ReturnsReviewer() - { - _fixture.Reset(); - _fixture.Server.SetupApprovePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var reviewer = await client.ApprovePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(reviewer); - } - - [Fact] - public async Task CreatePullRequestAsync_ReturnsPullRequest() + [Fact] + public async Task GetPullRequestCommitsAsync_ReturnsCommits() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestCommits( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var commits = await client.GetPullRequestCommitsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(commits); + var commitList = commits.ToList(); + Assert.Equal(2, commitList.Count); + Assert.Contains(commitList, c => c.Message == "Initial commit"); + } + + [Fact] + public async Task GetPullRequestMergeStateAsync_ReturnsMergeState() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestMergeState( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var mergeState = await client.GetPullRequestMergeStateAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(mergeState); + Assert.True(mergeState.CanMerge); + Assert.False(mergeState.Conflicted); + } + + [Fact] + public async Task DeclinePullRequestAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeclinePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var result = await client.DeclinePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.True(result); + } + + [Fact] + public async Task MergePullRequestAsync_ReturnsPullRequest() + { + _fixture.Reset(); + _fixture.Server.SetupMergePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var result = await client.MergePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(result); + Assert.Equal(TestConstants.TestPullRequestId, result.Id); + } + + [Fact] + public async Task ApprovePullRequestAsync_ReturnsReviewer() + { + _fixture.Reset(); + _fixture.Server.SetupApprovePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var reviewer = await client.ApprovePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(reviewer); + } + + [Fact] + public async Task CreatePullRequestAsync_ReturnsPullRequest() + { + _fixture.Reset(); + _fixture.Server.SetupCreatePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var prInfo = new PullRequestInfo { - _fixture.Reset(); - _fixture.Server.SetupCreatePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var prInfo = new PullRequestInfo - { - Title = "New PR", - Description = "Description", - FromRef = new FromToRef { Id = "refs/heads/feature" }, - ToRef = new FromToRef { Id = "refs/heads/main" } - }; - - var result = await client.CreatePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - prInfo); - - Assert.NotNull(result); - Assert.Equal(TestConstants.TestPullRequestId, result.Id); - } - - [Fact] - public async Task UpdatePullRequestAsync_ReturnsPullRequest() + Title = "New PR", + Description = "Description", + FromRef = new FromToRef { Id = "refs/heads/feature" }, + ToRef = new FromToRef { Id = "refs/heads/main" } + }; + + var result = await client.CreatePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + prInfo); + + Assert.NotNull(result); + Assert.Equal(TestConstants.TestPullRequestId, result.Id); + } + + [Fact] + public async Task UpdatePullRequestAsync_ReturnsPullRequest() + { + _fixture.Reset(); + _fixture.Server.SetupUpdatePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var update = new PullRequestUpdate { - _fixture.Reset(); - _fixture.Server.SetupUpdatePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var update = new PullRequestUpdate - { - Id = (int)TestConstants.TestPullRequestId, - Version = 0, - Title = "Updated Title" - }; - - var result = await client.UpdatePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - update); - - Assert.NotNull(result); - } + Id = (int)TestConstants.TestPullRequestId, + Version = 0, + Title = "Updated Title" + }; + + var result = await client.UpdatePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + update); + + Assert.NotNull(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestMockTests.cs index 475d79e..39b8621 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestMockTests.cs @@ -1,95 +1,89 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - /// - /// Unit tests for pull request-related operations using WireMock. - /// - public class PullRequestMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public PullRequestMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +/// +/// Unit tests for pull request-related operations using WireMock. +/// +public class PullRequestMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetPullRequestsAsync_ReturnsPullRequests() - { - // Arrange - _fixture.Reset(); - _fixture.Server.SetupGetPullRequests(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetPullRequestsAsync_ReturnsPullRequests() + { + // Arrange + _fixture.Reset(); + _fixture.Server.SetupGetPullRequests(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - // Act - var pullRequests = await client.GetPullRequestsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); + // Act + var pullRequests = await client.GetPullRequestsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); - // Assert - Assert.NotNull(pullRequests); - var prList = pullRequests.ToList(); - Assert.Single(prList); - var pr = prList[0]; - Assert.Equal(TestConstants.TestPullRequestId, pr.Id); - Assert.Equal(TestConstants.TestPullRequestTitle, pr.Title); - Assert.Equal(PullRequestStates.Open, pr.State); - } + // Assert + Assert.NotNull(pullRequests); + var prList = pullRequests.ToList(); + Assert.Single(prList); + var pr = prList[0]; + Assert.Equal(TestConstants.TestPullRequestId, pr.Id); + Assert.Equal(TestConstants.TestPullRequestTitle, pr.Title); + Assert.Equal(PullRequestStates.Open, pr.State); + } - [Fact] - public async Task GetPullRequestAsync_WithValidId_ReturnsPullRequest() - { - // Arrange - _fixture.Reset(); - _fixture.Server.SetupGetPullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetPullRequestAsync_WithValidId_ReturnsPullRequest() + { + // Arrange + _fixture.Reset(); + _fixture.Server.SetupGetPullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - // Act - var pullRequest = await client.GetPullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); + // Act + var pullRequest = await client.GetPullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); - // Assert - Assert.NotNull(pullRequest); - Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); - Assert.Equal(TestConstants.TestPullRequestTitle, pullRequest.Title); - Assert.NotNull(pullRequest.FromRef); - Assert.NotNull(pullRequest.ToRef); - } + // Assert + Assert.NotNull(pullRequest); + Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); + Assert.Equal(TestConstants.TestPullRequestTitle, pullRequest.Title); + Assert.NotNull(pullRequest.FromRef); + Assert.NotNull(pullRequest.ToRef); + } - [Fact] - public async Task GetPullRequestCommentsAsync_ReturnsComments() - { - // Arrange - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestComments( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetPullRequestCommentsAsync_ReturnsComments() + { + // Arrange + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestComments( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - // Act - var comments = await client.GetPullRequestCommentsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - "/"); + // Act + var comments = await client.GetPullRequestCommentsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + "/"); - // Assert - Assert.NotNull(comments); - var commentList = comments.ToList(); - Assert.Single(commentList); - var comment = commentList[0]; - Assert.Equal(TestConstants.TestCommentId, comment.Id); - } + // Assert + Assert.NotNull(comments); + var commentList = comments.ToList(); + Assert.Single(commentList); + var comment = commentList[0]; + Assert.Equal(TestConstants.TestCommentId, comment.Id); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestParticipantsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestParticipantsMockTests.cs index 4855ace..343cb56 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestParticipantsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestParticipantsMockTests.cs @@ -1,105 +1,99 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Core.Users; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class PullRequestParticipantsMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public PullRequestParticipantsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class PullRequestParticipantsMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetPullRequestParticipantsAsync_ReturnsParticipants() - { - _fixture.Reset(); - _fixture.Server.SetupGetPullRequestParticipants( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetPullRequestParticipantsAsync_ReturnsParticipants() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestParticipants( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var participants = await client.GetPullRequestParticipantsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); + var participants = await client.GetPullRequestParticipantsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); - Assert.NotNull(participants); - var participantList = participants.ToList(); - Assert.Single(participantList); - Assert.NotNull(participantList[0].User); - Assert.Equal("testuser", participantList[0].User!.Name); - Assert.Equal(Roles.Author, participantList[0].Role); - } + Assert.NotNull(participants); + var participantList = participants.ToList(); + Assert.Single(participantList); + Assert.NotNull(participantList[0].User); + Assert.Equal("testuser", participantList[0].User!.Name); + Assert.Equal(Roles.Author, participantList[0].Role); + } - [Fact] - public async Task AssignUserRoleToPullRequestAsync_ReturnsParticipant() - { - _fixture.Reset(); - _fixture.Server.SetupAssignUserRoleToPullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task AssignUserRoleToPullRequestAsync_ReturnsParticipant() + { + _fixture.Reset(); + _fixture.Server.SetupAssignUserRoleToPullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var named = new Named { Name = "reviewer" }; + var named = new Named { Name = "reviewer" }; - var participant = await client.AssignUserRoleToPullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - named, - Roles.Reviewer); + var participant = await client.AssignUserRoleToPullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + named, + Roles.Reviewer); - Assert.NotNull(participant); - Assert.NotNull(participant.User); - Assert.Equal(Roles.Reviewer, participant.Role); - } + Assert.NotNull(participant); + Assert.NotNull(participant.User); + Assert.Equal(Roles.Reviewer, participant.Role); + } - [Fact] - public async Task DeletePullRequestParticipantAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeletePullRequestParticipant( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeletePullRequestParticipantAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeletePullRequestParticipant( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); - var result = await client.DeletePullRequestParticipantAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - "testuser"); + var result = await client.DeletePullRequestParticipantAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + "testuser"); - Assert.True(result); - } + Assert.True(result); + } - [Fact] - public async Task UnassignUserFromPullRequestAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUnassignUserFromPullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - "testuser"); - var client = _fixture.CreateClient(); + [Fact] + public async Task UnassignUserFromPullRequestAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUnassignUserFromPullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + "testuser"); + var client = _fixture.CreateClient(); - var result = await client.UnassignUserFromPullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - "testuser"); + var result = await client.UnassignUserFromPullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + "testuser"); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestWatchMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestWatchMockTests.cs index dca5999..d753909 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestWatchMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestWatchMockTests.cs @@ -1,114 +1,108 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class PullRequestWatchMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class PullRequestWatchMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task WatchPullRequestAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupWatchPullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var result = await client.WatchPullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.True(result); + } + + [Fact] + public async Task UnwatchPullRequestAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUnwatchPullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var result = await client.UnwatchPullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.True(result); + } + + [Fact] + public async Task DeletePullRequestAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeletePullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var versionInfo = new VersionInfo { Version = 0 }; + + var result = await client.DeletePullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId, + versionInfo); + + Assert.True(result); + } + + [Fact] + public async Task ReopenPullRequestAsync_ReturnsPullRequest() + { + _fixture.Reset(); + _fixture.Server.SetupReopenPullRequest( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var pullRequest = await client.ReopenPullRequestAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(pullRequest); + Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); + } + + [Fact] + public async Task DeletePullRequestApprovalAsync_ReturnsReviewer() { - private readonly BitbucketMockFixture _fixture; - - public PullRequestWatchMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task WatchPullRequestAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupWatchPullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var result = await client.WatchPullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.True(result); - } - - [Fact] - public async Task UnwatchPullRequestAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUnwatchPullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var result = await client.UnwatchPullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.True(result); - } - - [Fact] - public async Task DeletePullRequestAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeletePullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var versionInfo = new VersionInfo { Version = 0 }; - - var result = await client.DeletePullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId, - versionInfo); - - Assert.True(result); - } - - [Fact] - public async Task ReopenPullRequestAsync_ReturnsPullRequest() - { - _fixture.Reset(); - _fixture.Server.SetupReopenPullRequest( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var pullRequest = await client.ReopenPullRequestAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(pullRequest); - Assert.Equal(TestConstants.TestPullRequestId, pullRequest.Id); - } - - [Fact] - public async Task DeletePullRequestApprovalAsync_ReturnsReviewer() - { - _fixture.Reset(); - _fixture.Server.SetupDeletePullRequestApproval( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - var client = _fixture.CreateClient(); - - var reviewer = await client.DeletePullRequestApprovalAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - TestConstants.TestPullRequestId); - - Assert.NotNull(reviewer); - Assert.False(reviewer.Approved); - Assert.Equal(ParticipantStatus.Unapproved, reviewer.Status); - } + _fixture.Reset(); + _fixture.Server.SetupDeletePullRequestApproval( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var reviewer = await client.DeletePullRequestApprovalAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + TestConstants.TestPullRequestId); + + Assert.NotNull(reviewer); + Assert.False(reviewer.Approved); + Assert.Equal(ParticipantStatus.Unapproved, reviewer.Status); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/RefRestrictionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RefRestrictionsMockTests.cs index 1566901..4cc7f1a 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RefRestrictionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RefRestrictionsMockTests.cs @@ -1,204 +1,198 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.DefaultReviewers; using Bitbucket.Net.Models.RefRestrictions; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class RefRestrictionsMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class RefRestrictionsMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + private const string ProjectKey = "PROJ"; + private const string RepoSlug = "repo"; + + [Fact] + public async Task GetProjectRefRestrictionsAsync_ReturnsRestrictions() { - private readonly BitbucketMockFixture _fixture; - private const string ProjectKey = "PROJ"; - private const string RepoSlug = "repo"; + _fixture.Reset(); + _fixture.Server.SetupGetProjectRefRestrictions(ProjectKey); + var client = _fixture.CreateClient(); - public RefRestrictionsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + var result = await client.GetProjectRefRestrictionsAsync(ProjectKey); - [Fact] - public async Task GetProjectRefRestrictionsAsync_ReturnsRestrictions() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectRefRestrictions(ProjectKey); - var client = _fixture.CreateClient(); + Assert.NotNull(result); + var restrictions = result.ToList(); + Assert.Equal(2, restrictions.Count); + Assert.Equal(1, restrictions[0].Id); + } - var result = await client.GetProjectRefRestrictionsAsync(ProjectKey); + [Fact] + public async Task GetProjectRefRestrictionAsync_ReturnsRestriction() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectRefRestriction(ProjectKey, 1); + var client = _fixture.CreateClient(); + + var result = await client.GetProjectRefRestrictionAsync(ProjectKey, 1); - Assert.NotNull(result); - var restrictions = result.ToList(); - Assert.Equal(2, restrictions.Count); - Assert.Equal(1, restrictions[0].Id); - } + Assert.NotNull(result); + Assert.Equal(1, result.Id); + Assert.NotNull(result.Matcher); + } - [Fact] - public async Task GetProjectRefRestrictionAsync_ReturnsRestriction() + [Fact] + public async Task CreateProjectRefRestrictionAsync_ReturnsCreatedRestriction() + { + _fixture.Reset(); + _fixture.Server.SetupCreateProjectRefRestriction(ProjectKey); + var client = _fixture.CreateClient(); + + var restriction = new RefRestrictionCreate { - _fixture.Reset(); - _fixture.Server.SetupGetProjectRefRestriction(ProjectKey, 1); - var client = _fixture.CreateClient(); + Type = RefRestrictionTypes.AllChanges, + Matcher = new RefMatcher + { + Id = "refs/heads/main", + DisplayId = "main", + Active = true, + Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } + } + }; - var result = await client.GetProjectRefRestrictionAsync(ProjectKey, 1); + var result = await client.CreateProjectRefRestrictionAsync(ProjectKey, restriction); - Assert.NotNull(result); - Assert.Equal(1, result.Id); - Assert.NotNull(result.Matcher); - } + Assert.NotNull(result); + Assert.Equal(1, result.Id); + } - [Fact] - public async Task CreateProjectRefRestrictionAsync_ReturnsCreatedRestriction() - { - _fixture.Reset(); - _fixture.Server.SetupCreateProjectRefRestriction(ProjectKey); - var client = _fixture.CreateClient(); + [Fact] + public async Task CreateProjectRefRestrictionsAsync_ReturnsCreatedRestrictions() + { + _fixture.Reset(); + _fixture.Server.SetupCreateProjectRefRestrictions(ProjectKey); + var client = _fixture.CreateClient(); - var restriction = new RefRestrictionCreate - { - Type = RefRestrictionTypes.AllChanges, - Matcher = new RefMatcher - { - Id = "refs/heads/main", - DisplayId = "main", - Active = true, - Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } - } - }; - - var result = await client.CreateProjectRefRestrictionAsync(ProjectKey, restriction); - - Assert.NotNull(result); - Assert.Equal(1, result.Id); - } - - [Fact] - public async Task CreateProjectRefRestrictionsAsync_ReturnsCreatedRestrictions() + var restriction = new RefRestrictionCreate { - _fixture.Reset(); - _fixture.Server.SetupCreateProjectRefRestrictions(ProjectKey); - var client = _fixture.CreateClient(); - - var restriction = new RefRestrictionCreate + Type = RefRestrictionTypes.Deletion, + Matcher = new RefMatcher { - Type = RefRestrictionTypes.Deletion, - Matcher = new RefMatcher - { - Id = "refs/heads/main", - DisplayId = "main", - Active = true, - Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } - } - }; - - var result = await client.CreateProjectRefRestrictionsAsync(ProjectKey, restriction); - - Assert.NotNull(result); - var restrictions = result.ToList(); - Assert.Single(restrictions); - Assert.Equal(3, restrictions[0].Id); - } - - [Fact] - public async Task DeleteProjectRefRestrictionAsync_ReturnsSuccess() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteProjectRefRestriction(ProjectKey, 1); - var client = _fixture.CreateClient(); + Id = "refs/heads/main", + DisplayId = "main", + Active = true, + Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } + } + }; + + var result = await client.CreateProjectRefRestrictionsAsync(ProjectKey, restriction); + + Assert.NotNull(result); + var restrictions = result.ToList(); + Assert.Single(restrictions); + Assert.Equal(3, restrictions[0].Id); + } - var result = await client.DeleteProjectRefRestrictionAsync(ProjectKey, 1); + [Fact] + public async Task DeleteProjectRefRestrictionAsync_ReturnsSuccess() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteProjectRefRestriction(ProjectKey, 1); + var client = _fixture.CreateClient(); - Assert.True(result); - } + var result = await client.DeleteProjectRefRestrictionAsync(ProjectKey, 1); - [Fact] - public async Task GetRepositoryRefRestrictionsAsync_ReturnsRestrictions() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositoryRefRestrictions(ProjectKey, RepoSlug); - var client = _fixture.CreateClient(); + Assert.True(result); + } - var result = await client.GetRepositoryRefRestrictionsAsync(ProjectKey, RepoSlug); + [Fact] + public async Task GetRepositoryRefRestrictionsAsync_ReturnsRestrictions() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryRefRestrictions(ProjectKey, RepoSlug); + var client = _fixture.CreateClient(); - Assert.NotNull(result); - var restrictions = result.ToList(); - Assert.Equal(2, restrictions.Count); - } + var result = await client.GetRepositoryRefRestrictionsAsync(ProjectKey, RepoSlug); - [Fact] - public async Task GetRepositoryRefRestrictionAsync_ReturnsRestriction() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositoryRefRestriction(ProjectKey, RepoSlug, 1); - var client = _fixture.CreateClient(); + Assert.NotNull(result); + var restrictions = result.ToList(); + Assert.Equal(2, restrictions.Count); + } - var result = await client.GetRepositoryRefRestrictionAsync(ProjectKey, RepoSlug, 1); + [Fact] + public async Task GetRepositoryRefRestrictionAsync_ReturnsRestriction() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryRefRestriction(ProjectKey, RepoSlug, 1); + var client = _fixture.CreateClient(); - Assert.NotNull(result); - Assert.Equal(1, result.Id); - } + var result = await client.GetRepositoryRefRestrictionAsync(ProjectKey, RepoSlug, 1); - [Fact] - public async Task CreateRepositoryRefRestrictionAsync_ReturnsCreatedRestriction() - { - _fixture.Reset(); - _fixture.Server.SetupCreateRepositoryRefRestriction(ProjectKey, RepoSlug); - var client = _fixture.CreateClient(); + Assert.NotNull(result); + Assert.Equal(1, result.Id); + } - var restriction = new RefRestrictionCreate - { - Type = RefRestrictionTypes.RewritingHistory, - Matcher = new RefMatcher - { - Id = "refs/heads/main", - DisplayId = "main", - Active = true, - Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } - } - }; - - var result = await client.CreateRepositoryRefRestrictionAsync(ProjectKey, RepoSlug, restriction); - - Assert.NotNull(result); - Assert.Equal(1, result.Id); - } - - [Fact] - public async Task CreateRepositoryRefRestrictionsAsync_ReturnsCreatedRestrictions() - { - _fixture.Reset(); - _fixture.Server.SetupCreateRepositoryRefRestrictions(ProjectKey, RepoSlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task CreateRepositoryRefRestrictionAsync_ReturnsCreatedRestriction() + { + _fixture.Reset(); + _fixture.Server.SetupCreateRepositoryRefRestriction(ProjectKey, RepoSlug); + var client = _fixture.CreateClient(); - var restriction = new RefRestrictionCreate + var restriction = new RefRestrictionCreate + { + Type = RefRestrictionTypes.RewritingHistory, + Matcher = new RefMatcher { - Type = RefRestrictionTypes.ChangesWithoutPullRequest, - Matcher = new RefMatcher - { - Id = "refs/heads/main", - DisplayId = "main", - Active = true, - Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } - } - }; - - var result = await client.CreateRepositoryRefRestrictionsAsync(ProjectKey, RepoSlug, restriction); - - Assert.NotNull(result); - var restrictions = result.ToList(); - Assert.Single(restrictions); - } - - [Fact] - public async Task DeleteRepositoryRefRestrictionAsync_ReturnsSuccess() + Id = "refs/heads/main", + DisplayId = "main", + Active = true, + Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } + } + }; + + var result = await client.CreateRepositoryRefRestrictionAsync(ProjectKey, RepoSlug, restriction); + + Assert.NotNull(result); + Assert.Equal(1, result.Id); + } + + [Fact] + public async Task CreateRepositoryRefRestrictionsAsync_ReturnsCreatedRestrictions() + { + _fixture.Reset(); + _fixture.Server.SetupCreateRepositoryRefRestrictions(ProjectKey, RepoSlug); + var client = _fixture.CreateClient(); + + var restriction = new RefRestrictionCreate { - _fixture.Reset(); - _fixture.Server.SetupDeleteRepositoryRefRestriction(ProjectKey, RepoSlug, 1); - var client = _fixture.CreateClient(); + Type = RefRestrictionTypes.ChangesWithoutPullRequest, + Matcher = new RefMatcher + { + Id = "refs/heads/main", + DisplayId = "main", + Active = true, + Type = new DefaultReviewerPullRequestConditionType { Id = "BRANCH", Name = "Branch" } + } + }; + + var result = await client.CreateRepositoryRefRestrictionsAsync(ProjectKey, RepoSlug, restriction); + + Assert.NotNull(result); + var restrictions = result.ToList(); + Assert.Single(restrictions); + } + + [Fact] + public async Task DeleteRepositoryRefRestrictionAsync_ReturnsSuccess() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteRepositoryRefRestriction(ProjectKey, RepoSlug, 1); + var client = _fixture.CreateClient(); - var result = await client.DeleteRepositoryRefRestrictionAsync(ProjectKey, RepoSlug, 1); + var result = await client.DeleteRepositoryRefRestrictionAsync(ProjectKey, RepoSlug, 1); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/RefSyncMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RefSyncMockTests.cs index 93edef8..9e76b18 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RefSyncMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RefSyncMockTests.cs @@ -1,73 +1,67 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.RefSync; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class RefSyncMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class RefSyncMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + private const string ProjectKey = "PROJ"; + private const string RepoSlug = "repo"; + + [Fact] + public async Task GetRepositorySynchronizationStatusAsync_ReturnsStatus() { - private readonly BitbucketMockFixture _fixture; - private const string ProjectKey = "PROJ"; - private const string RepoSlug = "repo"; + _fixture.Reset(); + _fixture.Server.SetupGetRepositorySynchronizationStatus(ProjectKey, RepoSlug); + var client = _fixture.CreateClient(); - public RefSyncMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + var result = await client.GetRepositorySynchronizationStatusAsync(ProjectKey, RepoSlug); - [Fact] - public async Task GetRepositorySynchronizationStatusAsync_ReturnsStatus() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositorySynchronizationStatus(ProjectKey, RepoSlug); - var client = _fixture.CreateClient(); - - var result = await client.GetRepositorySynchronizationStatusAsync(ProjectKey, RepoSlug); + Assert.NotNull(result); + Assert.True(result.Available); + Assert.True(result.Enabled); + Assert.NotNull(result.AheadRefs); + Assert.Single(result.AheadRefs); + Assert.NotNull(result.DivergedRefs); + Assert.Single(result.DivergedRefs); + Assert.NotNull(result.OrphanedRefs); + Assert.Single(result.OrphanedRefs); + } - Assert.NotNull(result); - Assert.True(result.Available); - Assert.True(result.Enabled); - Assert.NotNull(result.AheadRefs); - Assert.Single(result.AheadRefs); - Assert.NotNull(result.DivergedRefs); - Assert.Single(result.DivergedRefs); - Assert.NotNull(result.OrphanedRefs); - Assert.Single(result.OrphanedRefs); - } + [Fact] + public async Task EnableRepositorySynchronizationAsync_ReturnsStatus() + { + _fixture.Reset(); + _fixture.Server.SetupEnableRepositorySynchronization(ProjectKey, RepoSlug); + var client = _fixture.CreateClient(); - [Fact] - public async Task EnableRepositorySynchronizationAsync_ReturnsStatus() - { - _fixture.Reset(); - _fixture.Server.SetupEnableRepositorySynchronization(ProjectKey, RepoSlug); - var client = _fixture.CreateClient(); + var result = await client.EnableRepositorySynchronizationAsync(ProjectKey, RepoSlug, true); - var result = await client.EnableRepositorySynchronizationAsync(ProjectKey, RepoSlug, true); + Assert.NotNull(result); + Assert.True(result.Enabled); + } - Assert.NotNull(result); - Assert.True(result.Enabled); - } + [Fact] + public async Task SynchronizeRepositoryAsync_ReturnsFullRef() + { + _fixture.Reset(); + _fixture.Server.SetupSynchronizeRepository(ProjectKey, RepoSlug); + var client = _fixture.CreateClient(); - [Fact] - public async Task SynchronizeRepositoryAsync_ReturnsFullRef() + var synchronize = new Synchronize { - _fixture.Reset(); - _fixture.Server.SetupSynchronizeRepository(ProjectKey, RepoSlug); - var client = _fixture.CreateClient(); - - var synchronize = new Synchronize - { - RefId = "refs/heads/feature/synced", - Action = SynchronizeActions.Merge - }; + RefId = "refs/heads/feature/synced", + Action = SynchronizeActions.Merge + }; - var result = await client.SynchronizeRepositoryAsync(ProjectKey, RepoSlug, synchronize); + var result = await client.SynchronizeRepositoryAsync(ProjectKey, RepoSlug, synchronize); - Assert.NotNull(result); - Assert.Equal("refs/heads/feature/synced", result.Id); - Assert.Equal("SYNCED", result.State); - } + Assert.NotNull(result); + Assert.Equal("refs/heads/feature/synced", result.Id); + Assert.Equal("SYNCED", result.State); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/RepositoryCrudMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RepositoryCrudMockTests.cs index 55d49b7..3ed692b 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RepositoryCrudMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RepositoryCrudMockTests.cs @@ -1,108 +1,102 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class RepositoryCrudMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class RepositoryCrudMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task CreateProjectRepositoryAsync_CreatesAndReturnsRepository() + { + _fixture.Reset(); + _fixture.Server.SetupCreateRepository(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var result = await client.CreateProjectRepositoryAsync( + TestConstants.TestProjectKey, + "new-repo", + "git"); + + Assert.NotNull(result); + Assert.Equal("test-repo", result.Slug); + } + + [Fact] + public async Task UpdateProjectRepositoryAsync_UpdatesAndReturnsRepository() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.UpdateProjectRepositoryAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + targetName: "updated-repo", + isForkable: true); + + Assert.NotNull(result); + } + + [Fact] + public async Task ScheduleProjectRepositoryForDeletionAsync_DeletesRepository() { - private readonly BitbucketMockFixture _fixture; - - public RepositoryCrudMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task CreateProjectRepositoryAsync_CreatesAndReturnsRepository() - { - _fixture.Reset(); - _fixture.Server.SetupCreateRepository(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var result = await client.CreateProjectRepositoryAsync( - TestConstants.TestProjectKey, - "new-repo", - "git"); - - Assert.NotNull(result); - Assert.Equal("test-repo", result.Slug); - } - - [Fact] - public async Task UpdateProjectRepositoryAsync_UpdatesAndReturnsRepository() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.UpdateProjectRepositoryAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - targetName: "updated-repo", - isForkable: true); - - Assert.NotNull(result); - } - - [Fact] - public async Task ScheduleProjectRepositoryForDeletionAsync_DeletesRepository() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.ScheduleProjectRepositoryForDeletionAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.True(result); - } - - [Fact] - public async Task CreateProjectRepositoryForkAsync_CreatesAndReturnsFork() - { - _fixture.Reset(); - _fixture.Server.SetupForkRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.CreateProjectRepositoryForkAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - targetProjectKey: "FORK", - targetName: "forked-repo"); - - Assert.NotNull(result); - } - - [Fact] - public async Task GetProjectRepositoryForksAsync_ReturnsForks() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositoryForks(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.GetProjectRepositoryForksAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(result); - } - - [Fact] - public async Task GetProjectRepositoryAsync_ReturnsRepository() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.GetProjectRepositoryAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(result); - Assert.Equal("test-repo", result.Slug); - } + _fixture.Reset(); + _fixture.Server.SetupDeleteRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.ScheduleProjectRepositoryForDeletionAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.True(result); + } + + [Fact] + public async Task CreateProjectRepositoryForkAsync_CreatesAndReturnsFork() + { + _fixture.Reset(); + _fixture.Server.SetupForkRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.CreateProjectRepositoryForkAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + targetProjectKey: "FORK", + targetName: "forked-repo"); + + Assert.NotNull(result); + } + + [Fact] + public async Task GetProjectRepositoryForksAsync_ReturnsForks() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryForks(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.GetProjectRepositoryForksAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(result); + } + + [Fact] + public async Task GetProjectRepositoryAsync_ReturnsRepository() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.GetProjectRepositoryAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(result); + Assert.Equal("test-repo", result.Slug); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/RepositoryMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RepositoryMockTests.cs index 2e9bccc..02707f3 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RepositoryMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RepositoryMockTests.cs @@ -1,76 +1,70 @@ +using Bitbucket.Net.Tests.Infrastructure; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - /// - /// Unit tests for repository-related operations using WireMock. - /// - public class RepositoryMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public RepositoryMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +/// +/// Unit tests for repository-related operations using WireMock. +/// +public class RepositoryMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetProjectRepositoryAsync_WithValidSlug_ReturnsRepository() - { - // Arrange - _fixture.Reset(); - _fixture.Server.SetupGetRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetProjectRepositoryAsync_WithValidSlug_ReturnsRepository() + { + // Arrange + _fixture.Reset(); + _fixture.Server.SetupGetRepository(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - // Act - var repository = await client.GetProjectRepositoryAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); + // Act + var repository = await client.GetProjectRepositoryAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); - // Assert - Assert.NotNull(repository); - Assert.Equal(TestConstants.TestRepositorySlug, repository.Slug); - Assert.Equal(TestConstants.TestRepositoryName, repository.Name); - Assert.NotNull(repository.Project); - Assert.Equal(TestConstants.TestProjectKey, repository.Project.Key); - } + // Assert + Assert.NotNull(repository); + Assert.Equal(TestConstants.TestRepositorySlug, repository.Slug); + Assert.Equal(TestConstants.TestRepositoryName, repository.Name); + Assert.NotNull(repository.Project); + Assert.Equal(TestConstants.TestProjectKey, repository.Project.Key); + } - [Fact] - public async Task GetProjectRepositoriesAsync_ReturnsRepositories() - { - // Arrange - _fixture.Reset(); - _fixture.Server.SetupGetRepositories(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetProjectRepositoriesAsync_ReturnsRepositories() + { + // Arrange + _fixture.Reset(); + _fixture.Server.SetupGetRepositories(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); - // Act - var repositories = await client.GetProjectRepositoriesAsync(TestConstants.TestProjectKey); + // Act + var repositories = await client.GetProjectRepositoriesAsync(TestConstants.TestProjectKey); - // Assert - Assert.NotNull(repositories); - var repoList = repositories.ToList(); - Assert.Single(repoList); - var repo = repoList[0]; - Assert.Equal(TestConstants.TestRepositorySlug, repo.Slug); - Assert.Equal(TestConstants.TestRepositoryName, repo.Name); - } + // Assert + Assert.NotNull(repositories); + var repoList = repositories.ToList(); + Assert.Single(repoList); + var repo = repoList[0]; + Assert.Equal(TestConstants.TestRepositorySlug, repo.Slug); + Assert.Equal(TestConstants.TestRepositoryName, repo.Name); + } - [Fact] - public async Task GetRepositoryParticipantsAsync_ReturnsParticipants() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositoryParticipants(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetRepositoryParticipantsAsync_ReturnsParticipants() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryParticipants(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - var participants = await client.GetRepositoryParticipantsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); + var participants = await client.GetRepositoryParticipantsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); - Assert.NotNull(participants); - Assert.NotEmpty(participants); - } + Assert.NotNull(participants); + Assert.NotEmpty(participants); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/RepositoryOperationsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RepositoryOperationsMockTests.cs index bc244ce..2925edf 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RepositoryOperationsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RepositoryOperationsMockTests.cs @@ -1,170 +1,164 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class RepositoryOperationsMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class RepositoryOperationsMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task RecreateProjectRepositoryAsync_ReturnsRepository() { - private readonly BitbucketMockFixture _fixture; + _fixture.Reset(); + _fixture.Server.SetupRecreateProjectRepository( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.RecreateProjectRepositoryAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(result); + Assert.Equal(TestConstants.TestRepositorySlug, result.Slug); + } - public RepositoryOperationsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task GetRelatedProjectRepositoriesAsync_ReturnsRelatedRepos() + { + _fixture.Reset(); + _fixture.Server.SetupGetRelatedProjectRepositories( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.GetRelatedProjectRepositoriesAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(result); + var repos = result.ToList(); + Assert.NotEmpty(repos); + } - [Fact] - public async Task RecreateProjectRepositoryAsync_ReturnsRepository() - { - _fixture.Reset(); - _fixture.Server.SetupRecreateProjectRepository( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.RecreateProjectRepositoryAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(result); - Assert.Equal(TestConstants.TestRepositorySlug, result.Slug); - } - - [Fact] - public async Task GetRelatedProjectRepositoriesAsync_ReturnsRelatedRepos() - { - _fixture.Reset(); - _fixture.Server.SetupGetRelatedProjectRepositories( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.GetRelatedProjectRepositoriesAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(result); - var repos = result.ToList(); - Assert.NotEmpty(repos); - } - - [Fact] - public async Task GetProjectRepositoryArchiveAsync_ReturnsBytes() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectRepositoryArchive( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.GetProjectRepositoryArchiveAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - at: "refs/heads/main", - fileName: "archive", - archiveFormat: ArchiveFormats.Zip, - path: "/", - prefix: "repo/"); - - Assert.NotNull(result); - Assert.True(result.Length > 0); - } - - [Fact] - public async Task GetProjectRepositoryPullRequestSettingsAsync_ReturnsSettings() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectRepositoryPullRequestSettings( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetProjectRepositoryArchiveAsync_ReturnsBytes() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectRepositoryArchive( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.GetProjectRepositoryArchiveAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + at: "refs/heads/main", + fileName: "archive", + archiveFormat: ArchiveFormats.Zip, + path: "/", + prefix: "repo/"); + + Assert.NotNull(result); + Assert.True(result.Length > 0); + } - var result = await client.GetProjectRepositoryPullRequestSettingsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); + [Fact] + public async Task GetProjectRepositoryPullRequestSettingsAsync_ReturnsSettings() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectRepositoryPullRequestSettings( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - Assert.NotNull(result); - } + var result = await client.GetProjectRepositoryPullRequestSettingsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); - [Fact] - public async Task UpdateProjectRepositoryPullRequestSettingsAsync_ReturnsUpdatedSettings() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateProjectRepositoryPullRequestSettings( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var settings = new PullRequestSettings - { - RequiredApprovers = 2, - RequiredSuccessfulBuilds = 1, - RequiredAllApprovers = false, - RequiredAllTasksComplete = true - }; - - var result = await client.UpdateProjectRepositoryPullRequestSettingsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - settings); - - Assert.NotNull(result); - } - - [Fact] - public async Task GetProjectRepositoryHooksSettingsAsync_ReturnsHooksSettings() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectRepositoryHooksSettings( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.GetProjectRepositoryHooksSettingsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(result); - var hooks = result.ToList(); - Assert.NotEmpty(hooks); - } - - [Fact] - public async Task EnableProjectRepositoryHookAsync_ReturnsHook() - { - _fixture.Reset(); - _fixture.Server.SetupEnableProjectRepositoryHook( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "com.example.hook"); - var client = _fixture.CreateClient(); - - var result = await client.EnableProjectRepositoryHookAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "com.example.hook"); - - Assert.NotNull(result); - } - - [Fact] - public async Task DisableProjectRepositoryHookAsync_ReturnsHook() + Assert.NotNull(result); + } + + [Fact] + public async Task UpdateProjectRepositoryPullRequestSettingsAsync_ReturnsUpdatedSettings() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateProjectRepositoryPullRequestSettings( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var settings = new PullRequestSettings { - _fixture.Reset(); - _fixture.Server.SetupDisableProjectRepositoryHook( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "com.example.hook"); - var client = _fixture.CreateClient(); - - var result = await client.DisableProjectRepositoryHookAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "com.example.hook"); - - Assert.NotNull(result); - } + RequiredApprovers = 2, + RequiredSuccessfulBuilds = 1, + RequiredAllApprovers = false, + RequiredAllTasksComplete = true + }; + + var result = await client.UpdateProjectRepositoryPullRequestSettingsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + settings); + + Assert.NotNull(result); + } + + [Fact] + public async Task GetProjectRepositoryHooksSettingsAsync_ReturnsHooksSettings() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectRepositoryHooksSettings( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.GetProjectRepositoryHooksSettingsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(result); + var hooks = result.ToList(); + Assert.NotEmpty(hooks); + } + + [Fact] + public async Task EnableProjectRepositoryHookAsync_ReturnsHook() + { + _fixture.Reset(); + _fixture.Server.SetupEnableProjectRepositoryHook( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "com.example.hook"); + var client = _fixture.CreateClient(); + + var result = await client.EnableProjectRepositoryHookAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "com.example.hook"); + + Assert.NotNull(result); + } + + [Fact] + public async Task DisableProjectRepositoryHookAsync_ReturnsHook() + { + _fixture.Reset(); + _fixture.Server.SetupDisableProjectRepositoryHook( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "com.example.hook"); + var client = _fixture.CreateClient(); + + var result = await client.DisableProjectRepositoryHookAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "com.example.hook"); + + Assert.NotNull(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/RepositoryPermissionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RepositoryPermissionsMockTests.cs index 4f95cf3..bd5a099 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RepositoryPermissionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RepositoryPermissionsMockTests.cs @@ -1,151 +1,145 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class RepositoryPermissionsMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class RepositoryPermissionsMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetProjectRepositoryUserPermissionsAsync_ReturnsUserPermissions() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryUserPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var permissions = await client.GetProjectRepositoryUserPermissionsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(permissions); + var permissionList = permissions.ToList(); + Assert.Single(permissionList); + Assert.NotNull(permissionList[0].User); + Assert.Equal("testuser", permissionList[0].User!.Name); + Assert.Equal(Permissions.RepoAdmin, permissionList[0].Permission); + } + + [Fact] + public async Task UpdateProjectRepositoryUserPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateRepositoryUserPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.UpdateProjectRepositoryUserPermissionsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + Permissions.RepoAdmin, + "testuser"); + + Assert.True(result); + } + + [Fact] + public async Task DeleteProjectRepositoryUserPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteRepositoryUserPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.DeleteProjectRepositoryUserPermissionsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "testuser"); + + Assert.True(result); + } + + [Fact] + public async Task GetProjectRepositoryUserPermissionsNoneAsync_ReturnsUsers() { - private readonly BitbucketMockFixture _fixture; - - public RepositoryPermissionsMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetProjectRepositoryUserPermissionsAsync_ReturnsUserPermissions() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositoryUserPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var permissions = await client.GetProjectRepositoryUserPermissionsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(permissions); - var permissionList = permissions.ToList(); - Assert.Single(permissionList); - Assert.NotNull(permissionList[0].User); - Assert.Equal("testuser", permissionList[0].User!.Name); - Assert.Equal(Permissions.RepoAdmin, permissionList[0].Permission); - } - - [Fact] - public async Task UpdateProjectRepositoryUserPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateRepositoryUserPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.UpdateProjectRepositoryUserPermissionsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - Permissions.RepoAdmin, - "testuser"); - - Assert.True(result); - } - - [Fact] - public async Task DeleteProjectRepositoryUserPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteRepositoryUserPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.DeleteProjectRepositoryUserPermissionsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "testuser"); - - Assert.True(result); - } - - [Fact] - public async Task GetProjectRepositoryUserPermissionsNoneAsync_ReturnsUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositoryUserPermissionsNone(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var users = await client.GetProjectRepositoryUserPermissionsNoneAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(users); - var userList = users.ToList(); - Assert.NotEmpty(userList); - } - - [Fact] - public async Task GetProjectRepositoryGroupPermissionsAsync_ReturnsGroupPermissions() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositoryGroupPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var permissions = await client.GetProjectRepositoryGroupPermissionsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(permissions); - var permissionList = permissions.ToList(); - Assert.Single(permissionList); - Assert.NotNull(permissionList[0].Group); - Assert.Equal("developers", permissionList[0].Group!.Name); - Assert.Equal(Permissions.RepoWrite, permissionList[0].Permission); - } - - [Fact] - public async Task UpdateProjectRepositoryGroupPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateRepositoryGroupPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.UpdateProjectRepositoryGroupPermissionsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - Permissions.RepoWrite, - "developers"); - - Assert.True(result); - } - - [Fact] - public async Task DeleteProjectRepositoryGroupPermissionsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteRepositoryGroupPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var result = await client.DeleteProjectRepositoryGroupPermissionsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "developers"); - - Assert.True(result); - } - - [Fact] - public async Task GetProjectRepositoryGroupPermissionsNoneAsync_ReturnsDeletableGroupsOrUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepositoryGroupPermissionsNone(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var entities = await client.GetProjectRepositoryGroupPermissionsNoneAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(entities); - var entityList = entities.ToList(); - Assert.NotEmpty(entityList); - } + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryUserPermissionsNone(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var users = await client.GetProjectRepositoryUserPermissionsNoneAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(users); + var userList = users.ToList(); + Assert.NotEmpty(userList); + } + + [Fact] + public async Task GetProjectRepositoryGroupPermissionsAsync_ReturnsGroupPermissions() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryGroupPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var permissions = await client.GetProjectRepositoryGroupPermissionsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(permissions); + var permissionList = permissions.ToList(); + Assert.Single(permissionList); + Assert.NotNull(permissionList[0].Group); + Assert.Equal("developers", permissionList[0].Group!.Name); + Assert.Equal(Permissions.RepoWrite, permissionList[0].Permission); + } + + [Fact] + public async Task UpdateProjectRepositoryGroupPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateRepositoryGroupPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.UpdateProjectRepositoryGroupPermissionsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + Permissions.RepoWrite, + "developers"); + + Assert.True(result); + } + + [Fact] + public async Task DeleteProjectRepositoryGroupPermissionsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteRepositoryGroupPermissions(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var result = await client.DeleteProjectRepositoryGroupPermissionsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "developers"); + + Assert.True(result); + } + + [Fact] + public async Task GetProjectRepositoryGroupPermissionsNoneAsync_ReturnsDeletableGroupsOrUsers() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryGroupPermissionsNone(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var entities = await client.GetProjectRepositoryGroupPermissionsNoneAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(entities); + var entityList = entities.ToList(); + Assert.NotEmpty(entityList); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/SshKeyMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/SshKeyMockTests.cs index 9171e72..15c6cb3 100644 --- a/test/Bitbucket.Net.Tests/MockTests/SshKeyMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/SshKeyMockTests.cs @@ -1,271 +1,265 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class SshKeyMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class SshKeyMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetProjectKeysAsync_ByKeyId_ReturnsKeys() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectKeysByKeyId(1); + var client = _fixture.CreateClient(); + + var keys = await client.GetProjectKeysAsync(keyId: 1); + + Assert.NotNull(keys); + var keyList = keys.ToList(); + Assert.Equal(2, keyList.Count); + } + + [Fact] + public async Task GetProjectKeysAsync_ByProjectKey_ReturnsKeys() { - private readonly BitbucketMockFixture _fixture; - - public SshKeyMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } - - [Fact] - public async Task GetProjectKeysAsync_ByKeyId_ReturnsKeys() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectKeysByKeyId(1); - var client = _fixture.CreateClient(); - - var keys = await client.GetProjectKeysAsync(keyId: 1); - - Assert.NotNull(keys); - var keyList = keys.ToList(); - Assert.Equal(2, keyList.Count); - } - - [Fact] - public async Task GetProjectKeysAsync_ByProjectKey_ReturnsKeys() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectKeysByProject(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var keys = await client.GetProjectKeysAsync(projectKey: TestConstants.TestProjectKey); - - Assert.NotNull(keys); - } - - [Fact] - public async Task CreateProjectKeyAsync_CreatesKey() - { - _fixture.Reset(); - _fixture.Server.SetupCreateProjectKey(TestConstants.TestProjectKey); - var client = _fixture.CreateClient(); - - var key = await client.CreateProjectKeyAsync( - TestConstants.TestProjectKey, - "ssh-rsa AAAAB3...", - Permissions.RepoRead); - - Assert.NotNull(key); - } - - [Fact] - public async Task GetProjectKeyAsync_ReturnsKey() - { - _fixture.Reset(); - _fixture.Server.SetupGetProjectKey(TestConstants.TestProjectKey, 1); - var client = _fixture.CreateClient(); - - var key = await client.GetProjectKeyAsync(TestConstants.TestProjectKey, 1); - - Assert.NotNull(key); - } - - [Fact] - public async Task DeleteProjectKeyAsync_DeletesKey() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteProjectKey(TestConstants.TestProjectKey, 1); - var client = _fixture.CreateClient(); - - var result = await client.DeleteProjectKeyAsync(TestConstants.TestProjectKey, 1); - - Assert.True(result); - } - - [Fact] - public async Task UpdateProjectKeyPermissionAsync_UpdatesPermission() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateProjectKeyPermission(TestConstants.TestProjectKey, 1); - var client = _fixture.CreateClient(); - - var key = await client.UpdateProjectKeyPermissionAsync( - TestConstants.TestProjectKey, - 1, - Permissions.RepoWrite); - - Assert.NotNull(key); - } - - [Fact] - public async Task GetRepoKeysAsync_ByKeyId_ReturnsKeys() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepoKeysByKeyId(1); - var client = _fixture.CreateClient(); - - var keys = await client.GetRepoKeysAsync(keyId: 1); - - Assert.NotNull(keys); - } - - [Fact] - public async Task GetRepoKeysAsync_ByProjectAndRepo_ReturnsKeys() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepoKeysByProjectAndRepo( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var keys = await client.GetRepoKeysAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - - Assert.NotNull(keys); - } - - [Fact] - public async Task CreateRepoKeyAsync_CreatesKey() - { - _fixture.Reset(); - _fixture.Server.SetupCreateRepoKey( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var key = await client.CreateRepoKeyAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "ssh-rsa AAAAB3...", - Permissions.RepoRead); - - Assert.NotNull(key); - } - - [Fact] - public async Task GetRepoKeyAsync_ReturnsKey() - { - _fixture.Reset(); - _fixture.Server.SetupGetRepoKey( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - 1); - var client = _fixture.CreateClient(); - - var key = await client.GetRepoKeyAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - 1); - - Assert.NotNull(key); - } - - [Fact] - public async Task DeleteRepoKeyAsync_DeletesKey() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteRepoKey( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - 1); - var client = _fixture.CreateClient(); - - var result = await client.DeleteRepoKeyAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - 1); - - Assert.True(result); - } - - [Fact] - public async Task UpdateRepoKeyPermissionAsync_UpdatesPermission() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateRepoKeyPermission( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - 1); - var client = _fixture.CreateClient(); - - var key = await client.UpdateRepoKeyPermissionAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - 1, - Permissions.RepoWrite); - - Assert.NotNull(key); - } - - [Fact] - public async Task GetUserKeysAsync_ReturnsKeys() - { - _fixture.Reset(); - _fixture.Server.SetupGetUserKeys(); - var client = _fixture.CreateClient(); - - var keys = await client.GetUserKeysAsync(); - - Assert.NotNull(keys); - } - - [Fact] - public async Task CreateUserKeyAsync_CreatesKey() - { - _fixture.Reset(); - _fixture.Server.SetupCreateUserKey(); - var client = _fixture.CreateClient(); - - var key = await client.CreateUserKeyAsync("ssh-rsa AAAAB3..."); - - Assert.NotNull(key); - } - - [Fact] - public async Task DeleteUserKeysAsync_DeletesKeys() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteUserKeys(); - var client = _fixture.CreateClient(); - - var result = await client.DeleteUserKeysAsync(); - - Assert.True(result); - } - - [Fact] - public async Task DeleteUserKeyAsync_DeletesKey() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteUserKey(1); - var client = _fixture.CreateClient(); - - var result = await client.DeleteUserKeyAsync(1); - - Assert.True(result); - } - - [Fact] - public async Task DeleteProjectsReposKeysAsync_DeletesKeys() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteProjectsReposKeys(1); - var client = _fixture.CreateClient(); - - var result = await client.DeleteProjectsReposKeysAsync(1, "PROJECT:TEST", "REPO:test-repo"); - - Assert.True(result); - } - - [Fact] - public async Task GetSshSettingsAsync_ReturnsSettings() - { - _fixture.Reset(); - _fixture.Server.SetupGetSshSettings(); - var client = _fixture.CreateClient(); - - var settings = await client.GetSshSettingsAsync(); - - Assert.NotNull(settings); - } + _fixture.Reset(); + _fixture.Server.SetupGetProjectKeysByProject(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var keys = await client.GetProjectKeysAsync(projectKey: TestConstants.TestProjectKey); + + Assert.NotNull(keys); + } + + [Fact] + public async Task CreateProjectKeyAsync_CreatesKey() + { + _fixture.Reset(); + _fixture.Server.SetupCreateProjectKey(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var key = await client.CreateProjectKeyAsync( + TestConstants.TestProjectKey, + "ssh-rsa AAAAB3...", + Permissions.RepoRead); + + Assert.NotNull(key); + } + + [Fact] + public async Task GetProjectKeyAsync_ReturnsKey() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectKey(TestConstants.TestProjectKey, 1); + var client = _fixture.CreateClient(); + + var key = await client.GetProjectKeyAsync(TestConstants.TestProjectKey, 1); + + Assert.NotNull(key); + } + + [Fact] + public async Task DeleteProjectKeyAsync_DeletesKey() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteProjectKey(TestConstants.TestProjectKey, 1); + var client = _fixture.CreateClient(); + + var result = await client.DeleteProjectKeyAsync(TestConstants.TestProjectKey, 1); + + Assert.True(result); + } + + [Fact] + public async Task UpdateProjectKeyPermissionAsync_UpdatesPermission() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateProjectKeyPermission(TestConstants.TestProjectKey, 1); + var client = _fixture.CreateClient(); + + var key = await client.UpdateProjectKeyPermissionAsync( + TestConstants.TestProjectKey, + 1, + Permissions.RepoWrite); + + Assert.NotNull(key); + } + + [Fact] + public async Task GetRepoKeysAsync_ByKeyId_ReturnsKeys() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepoKeysByKeyId(1); + var client = _fixture.CreateClient(); + + var keys = await client.GetRepoKeysAsync(keyId: 1); + + Assert.NotNull(keys); + } + + [Fact] + public async Task GetRepoKeysAsync_ByProjectAndRepo_ReturnsKeys() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepoKeysByProjectAndRepo( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var keys = await client.GetRepoKeysAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + + Assert.NotNull(keys); + } + + [Fact] + public async Task CreateRepoKeyAsync_CreatesKey() + { + _fixture.Reset(); + _fixture.Server.SetupCreateRepoKey( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var key = await client.CreateRepoKeyAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "ssh-rsa AAAAB3...", + Permissions.RepoRead); + + Assert.NotNull(key); + } + + [Fact] + public async Task GetRepoKeyAsync_ReturnsKey() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepoKey( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + 1); + var client = _fixture.CreateClient(); + + var key = await client.GetRepoKeyAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + 1); + + Assert.NotNull(key); + } + + [Fact] + public async Task DeleteRepoKeyAsync_DeletesKey() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteRepoKey( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + 1); + var client = _fixture.CreateClient(); + + var result = await client.DeleteRepoKeyAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + 1); + + Assert.True(result); + } + + [Fact] + public async Task UpdateRepoKeyPermissionAsync_UpdatesPermission() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateRepoKeyPermission( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + 1); + var client = _fixture.CreateClient(); + + var key = await client.UpdateRepoKeyPermissionAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + 1, + Permissions.RepoWrite); + + Assert.NotNull(key); + } + + [Fact] + public async Task GetUserKeysAsync_ReturnsKeys() + { + _fixture.Reset(); + _fixture.Server.SetupGetUserKeys(); + var client = _fixture.CreateClient(); + + var keys = await client.GetUserKeysAsync(); + + Assert.NotNull(keys); + } + + [Fact] + public async Task CreateUserKeyAsync_CreatesKey() + { + _fixture.Reset(); + _fixture.Server.SetupCreateUserKey(); + var client = _fixture.CreateClient(); + + var key = await client.CreateUserKeyAsync("ssh-rsa AAAAB3..."); + + Assert.NotNull(key); + } + + [Fact] + public async Task DeleteUserKeysAsync_DeletesKeys() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteUserKeys(); + var client = _fixture.CreateClient(); + + var result = await client.DeleteUserKeysAsync(); + + Assert.True(result); + } + + [Fact] + public async Task DeleteUserKeyAsync_DeletesKey() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteUserKey(1); + var client = _fixture.CreateClient(); + + var result = await client.DeleteUserKeyAsync(1); + + Assert.True(result); + } + + [Fact] + public async Task DeleteProjectsReposKeysAsync_DeletesKeys() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteProjectsReposKeys(1); + var client = _fixture.CreateClient(); + + var result = await client.DeleteProjectsReposKeysAsync(1, "PROJECT:TEST", "REPO:test-repo"); + + Assert.True(result); + } + + [Fact] + public async Task GetSshSettingsAsync_ReturnsSettings() + { + _fixture.Reset(); + _fixture.Server.SetupGetSshSettings(); + var client = _fixture.CreateClient(); + + var settings = await client.GetSshSettingsAsync(); + + Assert.NotNull(settings); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/TasksMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/TasksMockTests.cs index 31ddc4f..24ae724 100644 --- a/test/Bitbucket.Net.Tests/MockTests/TasksMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/TasksMockTests.cs @@ -1,82 +1,76 @@ -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Tasks; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class TasksMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class TasksMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; + private readonly BitbucketMockFixture _fixture = fixture; - public TasksMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task CreateTaskAsync_ReturnsCreatedTask() + { + _fixture.Reset(); + _fixture.Server.SetupCreateTask(); + var client = _fixture.CreateClient(); - [Fact] - public async Task CreateTaskAsync_ReturnsCreatedTask() + var taskInfo = new TaskInfo { - _fixture.Reset(); - _fixture.Server.SetupCreateTask(); - var client = _fixture.CreateClient(); + Anchor = new TaskBasicAnchor { Id = 101, Type = "COMMENT" }, + Text = "Fix the null pointer exception" + }; - var taskInfo = new TaskInfo - { - Anchor = new TaskBasicAnchor { Id = 101, Type = "COMMENT" }, - Text = "Fix the null pointer exception" - }; + var task = await client.CreateTaskAsync(taskInfo); - var task = await client.CreateTaskAsync(taskInfo); - - Assert.NotNull(task); - Assert.Equal(1, task.Id); - Assert.Equal("Fix the null pointer exception", task.Text); - Assert.Equal("OPEN", task.State); - Assert.NotNull(task.Author); - Assert.Equal("jsmith", task.Author.Name); - } + Assert.NotNull(task); + Assert.Equal(1, task.Id); + Assert.Equal("Fix the null pointer exception", task.Text); + Assert.Equal("OPEN", task.State); + Assert.NotNull(task.Author); + Assert.Equal("jsmith", task.Author.Name); + } - [Fact] - public async Task GetTaskAsync_ReturnsTask() - { - _fixture.Reset(); - _fixture.Server.SetupGetTask(1); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetTaskAsync_ReturnsTask() + { + _fixture.Reset(); + _fixture.Server.SetupGetTask(1); + var client = _fixture.CreateClient(); - var task = await client.GetTaskAsync(1); + var task = await client.GetTaskAsync(1); - Assert.NotNull(task); - Assert.Equal(1, task.Id); - Assert.Equal("Fix the null pointer exception", task.Text); - Assert.Equal("OPEN", task.State); - Assert.NotNull(task.Anchor); - } + Assert.NotNull(task); + Assert.Equal(1, task.Id); + Assert.Equal("Fix the null pointer exception", task.Text); + Assert.Equal("OPEN", task.State); + Assert.NotNull(task.Anchor); + } - [Fact] - public async Task UpdateTaskAsync_ReturnsUpdatedTask() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateTask(1); - var client = _fixture.CreateClient(); + [Fact] + public async Task UpdateTaskAsync_ReturnsUpdatedTask() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateTask(1); + var client = _fixture.CreateClient(); - var task = await client.UpdateTaskAsync(1, "Updated task text"); + var task = await client.UpdateTaskAsync(1, "Updated task text"); - Assert.NotNull(task); - Assert.Equal(1, task.Id); - Assert.NotNull(task.Author); - } + Assert.NotNull(task); + Assert.Equal(1, task.Id); + Assert.NotNull(task.Author); + } - [Fact] - public async Task DeleteTaskAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteTask(1); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeleteTaskAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteTask(1); + var client = _fixture.CreateClient(); - var result = await client.DeleteTaskAsync(1); + var result = await client.DeleteTaskAsync(1); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/UserMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/UserMockTests.cs index 6c12192..903220e 100644 --- a/test/Bitbucket.Net.Tests/MockTests/UserMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/UserMockTests.cs @@ -1,109 +1,103 @@ #nullable enable +using Bitbucket.Net.Tests.Infrastructure; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class UserMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class UserMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetUsersAsync_ReturnsUsers() { - private readonly BitbucketMockFixture _fixture; + _fixture.Reset(); + _fixture.Server.SetupGetUsers(); + var client = _fixture.CreateClient(); - public UserMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + var users = await client.GetUsersAsync(); - [Fact] - public async Task GetUsersAsync_ReturnsUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetUsers(); - var client = _fixture.CreateClient(); + Assert.NotNull(users); + var userList = users.ToList(); + Assert.Equal(2, userList.Count); + Assert.Equal("admin", userList[0].Name); + Assert.Equal("admin@example.com", userList[0].EmailAddress); + } - var users = await client.GetUsersAsync(); + [Fact] + public async Task GetUserAsync_ReturnsUser() + { + _fixture.Reset(); + _fixture.Server.SetupGetUser("admin"); + var client = _fixture.CreateClient(); - Assert.NotNull(users); - var userList = users.ToList(); - Assert.Equal(2, userList.Count); - Assert.Equal("admin", userList[0].Name); - Assert.Equal("admin@example.com", userList[0].EmailAddress); - } + var user = await client.GetUserAsync("admin"); - [Fact] - public async Task GetUserAsync_ReturnsUser() - { - _fixture.Reset(); - _fixture.Server.SetupGetUser("admin"); - var client = _fixture.CreateClient(); - - var user = await client.GetUserAsync("admin"); + Assert.NotNull(user); + Assert.Equal("admin", user.Name); + Assert.Equal("Administrator", user.DisplayName); + Assert.True(user.Active); + } - Assert.NotNull(user); - Assert.Equal("admin", user.Name); - Assert.Equal("Administrator", user.DisplayName); - Assert.True(user.Active); - } + [Fact] + public async Task UpdateUserAsync_ReturnsUpdatedUser() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateUser(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateUserAsync_ReturnsUpdatedUser() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateUser(); - var client = _fixture.CreateClient(); + var user = await client.UpdateUserAsync( + email: "newemail@example.com", + displayName: "New Display Name"); - var user = await client.UpdateUserAsync( - email: "newemail@example.com", - displayName: "New Display Name"); + Assert.NotNull(user); + Assert.Equal("admin", user.Name); + } - Assert.NotNull(user); - Assert.Equal("admin", user.Name); - } + [Fact] + public async Task DeleteUserAvatarAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteUserAvatar("admin"); + var client = _fixture.CreateClient(); - [Fact] - public async Task DeleteUserAvatarAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteUserAvatar("admin"); - var client = _fixture.CreateClient(); + var result = await client.DeleteUserAvatarAsync("admin"); - var result = await client.DeleteUserAvatarAsync("admin"); + Assert.True(result); + } - Assert.True(result); - } + [Fact] + public async Task GetUserSettingsAsync_ReturnsSettings() + { + _fixture.Reset(); + _fixture.Server.SetupGetUserSettings("admin"); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetUserSettingsAsync_ReturnsSettings() - { - _fixture.Reset(); - _fixture.Server.SetupGetUserSettings("admin"); - var client = _fixture.CreateClient(); + var settings = await client.GetUserSettingsAsync("admin"); - var settings = await client.GetUserSettingsAsync("admin"); + Assert.NotNull(settings); + Assert.Equal("dark", settings["theme"]?.ToString()); + } - Assert.NotNull(settings); - Assert.Equal("dark", settings["theme"]?.ToString()); - } + [Fact] + public async Task UpdateUserSettingsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateUserSettings("admin"); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateUserSettingsAsync_ReturnsTrue() + var newSettings = new Dictionary { - _fixture.Reset(); - _fixture.Server.SetupUpdateUserSettings("admin"); - var client = _fixture.CreateClient(); - - var newSettings = new Dictionary - { - ["theme"] = "light", - ["notifications"] = false - }; + ["theme"] = "light", + ["notifications"] = false + }; - var result = await client.UpdateUserSettingsAsync("admin", newSettings); + var result = await client.UpdateUserSettingsAsync("admin", newSettings); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/UsersMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/UsersMockTests.cs index c8f2d78..9886ad0 100644 --- a/test/Bitbucket.Net.Tests/MockTests/UsersMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/UsersMockTests.cs @@ -1,124 +1,118 @@ #nullable enable +using Bitbucket.Net.Models.Core.Users; +using Bitbucket.Net.Tests.Infrastructure; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Models.Core.Users; -using Bitbucket.Net.Tests.Infrastructure; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class UsersMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class UsersMockTests : IClassFixture + private readonly BitbucketMockFixture _fixture = fixture; + + [Fact] + public async Task GetUsersAsync_ReturnsUsers() { - private readonly BitbucketMockFixture _fixture; + _fixture.Reset(); + _fixture.Server.SetupGetUsers(); + var client = _fixture.CreateClient(); + + var users = await client.GetUsersAsync(); + + var userList = users.ToList(); + Assert.NotEmpty(userList); + Assert.Equal(2, userList.Count); + Assert.Equal("admin", userList[0].Name); + Assert.Equal("admin@example.com", userList[0].EmailAddress); + Assert.Equal("developer", userList[1].Name); + } - public UsersMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task GetUserAsync_ReturnsUser() + { + _fixture.Reset(); + _fixture.Server.SetupGetUser("admin"); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetUsersAsync_ReturnsUsers() - { - _fixture.Reset(); - _fixture.Server.SetupGetUsers(); - var client = _fixture.CreateClient(); - - var users = await client.GetUsersAsync(); - - var userList = users.ToList(); - Assert.NotEmpty(userList); - Assert.Equal(2, userList.Count); - Assert.Equal("admin", userList[0].Name); - Assert.Equal("admin@example.com", userList[0].EmailAddress); - Assert.Equal("developer", userList[1].Name); - } - - [Fact] - public async Task GetUserAsync_ReturnsUser() - { - _fixture.Reset(); - _fixture.Server.SetupGetUser("admin"); - var client = _fixture.CreateClient(); + var user = await client.GetUserAsync("admin"); - var user = await client.GetUserAsync("admin"); + Assert.NotNull(user); + Assert.Equal("admin", user.Name); + Assert.Equal("admin@example.com", user.EmailAddress); + Assert.Equal("Administrator", user.DisplayName); + Assert.True(user.Active); + } - Assert.NotNull(user); - Assert.Equal("admin", user.Name); - Assert.Equal("admin@example.com", user.EmailAddress); - Assert.Equal("Administrator", user.DisplayName); - Assert.True(user.Active); - } + [Fact] + public async Task UpdateUserAsync_ReturnsUpdatedUser() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateUser(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateUserAsync_ReturnsUpdatedUser() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateUser(); - var client = _fixture.CreateClient(); + var user = await client.UpdateUserAsync(email: "newemail@example.com", displayName: "New Name"); - var user = await client.UpdateUserAsync(email: "newemail@example.com", displayName: "New Name"); + Assert.NotNull(user); + Assert.Equal("admin", user.Name); + } - Assert.NotNull(user); - Assert.Equal("admin", user.Name); - } + [Fact] + public async Task UpdateUserCredentialsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateUserCredentials(); + var client = _fixture.CreateClient(); - [Fact] - public async Task UpdateUserCredentialsAsync_ReturnsTrue() + var passwordChange = new PasswordChange { - _fixture.Reset(); - _fixture.Server.SetupUpdateUserCredentials(); - var client = _fixture.CreateClient(); - - var passwordChange = new PasswordChange - { - OldPassword = "oldPassword", - Password = "newPassword", - PasswordConfirm = "newPassword" - }; + OldPassword = "oldPassword", + Password = "newPassword", + PasswordConfirm = "newPassword" + }; - var result = await client.UpdateUserCredentialsAsync(passwordChange); + var result = await client.UpdateUserCredentialsAsync(passwordChange); - Assert.True(result); - } + Assert.True(result); + } - [Fact] - public async Task DeleteUserAvatarAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteUserAvatar("admin"); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeleteUserAvatarAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteUserAvatar("admin"); + var client = _fixture.CreateClient(); - var result = await client.DeleteUserAvatarAsync("admin"); + var result = await client.DeleteUserAvatarAsync("admin"); - Assert.True(result); - } + Assert.True(result); + } - [Fact] - public async Task GetUserSettingsAsync_ReturnsSettings() - { - _fixture.Reset(); - _fixture.Server.SetupGetUserSettings("admin"); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetUserSettingsAsync_ReturnsSettings() + { + _fixture.Reset(); + _fixture.Server.SetupGetUserSettings("admin"); + var client = _fixture.CreateClient(); - var settings = await client.GetUserSettingsAsync("admin"); + var settings = await client.GetUserSettingsAsync("admin"); - Assert.NotNull(settings); - Assert.Equal("dark", settings["theme"]?.ToString()); - } + Assert.NotNull(settings); + Assert.Equal("dark", settings["theme"]?.ToString()); + } - [Fact] - public async Task UpdateUserSettingsAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupUpdateUserSettings("admin"); - var client = _fixture.CreateClient(); + [Fact] + public async Task UpdateUserSettingsAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupUpdateUserSettings("admin"); + var client = _fixture.CreateClient(); - var settings = new Dictionary { ["theme"] = "dark" }; - var result = await client.UpdateUserSettingsAsync("admin", settings); + var settings = new Dictionary { ["theme"] = "dark" }; + var result = await client.UpdateUserSettingsAsync("admin", settings); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/WebhookAndCompareMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/WebhookAndCompareMockTests.cs index ec41908..a623280 100644 --- a/test/Bitbucket.Net.Tests/MockTests/WebhookAndCompareMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/WebhookAndCompareMockTests.cs @@ -1,77 +1,71 @@ -using System.Linq; -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Linq; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class WebhookAndCompareMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public WebhookAndCompareMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class WebhookAndCompareMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetProjectRepositoryWebHooksAsync_ReturnsWebhooks() - { - _fixture.Reset(); - _fixture.Server.SetupGetWebhooks( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetProjectRepositoryWebHooksAsync_ReturnsWebhooks() + { + _fixture.Reset(); + _fixture.Server.SetupGetWebhooks( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - var webhooks = await client.GetProjectRepositoryWebHooksAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); + var webhooks = await client.GetProjectRepositoryWebHooksAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); - Assert.NotNull(webhooks); - var webhookList = webhooks.ToList(); - Assert.Equal(2, webhookList.Count); - Assert.Equal("CI/CD Webhook", webhookList[0].Name); - Assert.True(webhookList[0].Active); - } + Assert.NotNull(webhooks); + var webhookList = webhooks.ToList(); + Assert.Equal(2, webhookList.Count); + Assert.Equal("CI/CD Webhook", webhookList[0].Name); + Assert.True(webhookList[0].Active); + } - [Fact] - public async Task GetRepositoryCompareCommitsAsync_ReturnsCommits() - { - _fixture.Reset(); - _fixture.Server.SetupGetCompareCommits( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetRepositoryCompareCommitsAsync_ReturnsCommits() + { + _fixture.Reset(); + _fixture.Server.SetupGetCompareCommits( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - var commits = await client.GetRepositoryCompareCommitsAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - from: "HEAD~5", - to: "HEAD"); + var commits = await client.GetRepositoryCompareCommitsAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + from: "HEAD~5", + to: "HEAD"); - Assert.NotNull(commits); - var commitList = commits.ToList(); - Assert.Equal(2, commitList.Count); - } + Assert.NotNull(commits); + var commitList = commits.ToList(); + Assert.Equal(2, commitList.Count); + } - [Fact] - public async Task GetRepositoryCompareDiffAsync_ReturnsDiff() - { - _fixture.Reset(); - _fixture.Server.SetupGetCompareDiff( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetRepositoryCompareDiffAsync_ReturnsDiff() + { + _fixture.Reset(); + _fixture.Server.SetupGetCompareDiff( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - var diff = await client.GetRepositoryCompareDiffAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - from: "HEAD~5", - to: "HEAD"); + var diff = await client.GetRepositoryCompareDiffAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + from: "HEAD~5", + to: "HEAD"); - Assert.NotNull(diff); - Assert.NotNull(diff.Diffs); - } + Assert.NotNull(diff); + Assert.NotNull(diff.Diffs); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/WebhookMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/WebhookMockTests.cs index 754f9ed..6aa0882 100644 --- a/test/Bitbucket.Net.Tests/MockTests/WebhookMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/WebhookMockTests.cs @@ -1,73 +1,67 @@ -using System.Threading.Tasks; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests +namespace Bitbucket.Net.Tests.MockTests; + +public class WebhookMockTests(BitbucketMockFixture fixture) : IClassFixture { - public class WebhookMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; + private readonly BitbucketMockFixture _fixture = fixture; - public WebhookMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } + [Fact] + public async Task GetProjectRepositoryWebHookAsync_ReturnsWebhook() + { + _fixture.Reset(); + _fixture.Server.SetupGetWebhook(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, "1"); + var client = _fixture.CreateClient(); - [Fact] - public async Task GetProjectRepositoryWebHookAsync_ReturnsWebhook() - { - _fixture.Reset(); - _fixture.Server.SetupGetWebhook(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, "1"); - var client = _fixture.CreateClient(); + var webhook = await client.GetProjectRepositoryWebHookAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "1"); - var webhook = await client.GetProjectRepositoryWebHookAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "1"); + Assert.NotNull(webhook); + Assert.Equal(1, webhook.Id); + Assert.Equal("CI/CD Webhook", webhook.Name); + } - Assert.NotNull(webhook); - Assert.Equal(1, webhook.Id); - Assert.Equal("CI/CD Webhook", webhook.Name); - } + [Fact] + public async Task CreateProjectRepositoryWebHookAsync_ReturnsWebhook() + { + _fixture.Reset(); + _fixture.Server.SetupCreateWebhook(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); - [Fact] - public async Task CreateProjectRepositoryWebHookAsync_ReturnsWebhook() + var newWebhook = new WebHook { - _fixture.Reset(); - _fixture.Server.SetupCreateWebhook(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); - var client = _fixture.CreateClient(); - - var newWebhook = new WebHook - { - Name = "Test Webhook", - Url = "https://example.com/webhook", - Active = true, - Events = ["repo:refs_changed"] - }; + Name = "Test Webhook", + Url = "https://example.com/webhook", + Active = true, + Events = ["repo:refs_changed"] + }; - var webhook = await client.CreateProjectRepositoryWebHookAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - newWebhook); + var webhook = await client.CreateProjectRepositoryWebHookAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + newWebhook); - Assert.NotNull(webhook); - Assert.Equal(1, webhook.Id); - } + Assert.NotNull(webhook); + Assert.Equal(1, webhook.Id); + } - [Fact] - public async Task DeleteProjectRepositoryWebHookAsync_ReturnsTrue() - { - _fixture.Reset(); - _fixture.Server.SetupDeleteWebhook(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, "1"); - var client = _fixture.CreateClient(); + [Fact] + public async Task DeleteProjectRepositoryWebHookAsync_ReturnsTrue() + { + _fixture.Reset(); + _fixture.Server.SetupDeleteWebhook(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, "1"); + var client = _fixture.CreateClient(); - var result = await client.DeleteProjectRepositoryWebHookAsync( - TestConstants.TestProjectKey, - TestConstants.TestRepositorySlug, - "1"); + var result = await client.DeleteProjectRepositoryWebHookAsync( + TestConstants.TestProjectKey, + TestConstants.TestRepositorySlug, + "1"); - Assert.True(result); - } + Assert.True(result); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/WhoAmIMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/WhoAmIMockTests.cs index b6770be..9623636 100644 --- a/test/Bitbucket.Net.Tests/MockTests/WhoAmIMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/WhoAmIMockTests.cs @@ -1,40 +1,34 @@ -using System.Threading.Tasks; using Bitbucket.Net.Tests.Infrastructure; +using System.Threading.Tasks; using Xunit; -namespace Bitbucket.Net.Tests.MockTests -{ - public class WhoAmIMockTests : IClassFixture - { - private readonly BitbucketMockFixture _fixture; +namespace Bitbucket.Net.Tests.MockTests; - public WhoAmIMockTests(BitbucketMockFixture fixture) - { - _fixture = fixture; - } +public class WhoAmIMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private readonly BitbucketMockFixture _fixture = fixture; - [Fact] - public async Task GetWhoAmIAsync_ReturnsUsername() - { - _fixture.Reset(); - _fixture.Server.SetupGetWhoAmI("jsmith"); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetWhoAmIAsync_ReturnsUsername() + { + _fixture.Reset(); + _fixture.Server.SetupGetWhoAmI("jsmith"); + var client = _fixture.CreateClient(); - var username = await client.GetWhoAmIAsync(); + var username = await client.GetWhoAmIAsync(); - Assert.Equal("jsmith", username); - } + Assert.Equal("jsmith", username); + } - [Fact] - public async Task GetWhoAmIAsync_WithWhitespace_ReturnsTrimmedUsername() - { - _fixture.Reset(); - _fixture.Server.SetupGetWhoAmI(" jsmith "); - var client = _fixture.CreateClient(); + [Fact] + public async Task GetWhoAmIAsync_WithWhitespace_ReturnsTrimmedUsername() + { + _fixture.Reset(); + _fixture.Server.SetupGetWhoAmI(" jsmith "); + var client = _fixture.CreateClient(); - var username = await client.GetWhoAmIAsync(); + var username = await client.GetWhoAmIAsync(); - Assert.Equal("jsmith", username); - } + Assert.Equal("jsmith", username); } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/UnitTests/BitbucketClientConstructorTests.cs b/test/Bitbucket.Net.Tests/UnitTests/BitbucketClientConstructorTests.cs index 1868d5e..90ea6a9 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/BitbucketClientConstructorTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/BitbucketClientConstructorTests.cs @@ -1,8 +1,8 @@ #nullable enable +using Flurl.Http; using System; using System.Net.Http; -using Flurl.Http; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; @@ -133,4 +133,4 @@ public void Constructor_FlurlClient_NoBaseUrl_ThrowsArgumentException() } #endregion -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/UnitTests/BitbucketHelpersTests.cs b/test/Bitbucket.Net.Tests/UnitTests/BitbucketHelpersTests.cs index b079508..ddc8702 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/BitbucketHelpersTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/BitbucketHelpersTests.cs @@ -1,12 +1,12 @@ #nullable enable -using System; using Bitbucket.Net.Common; using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Git; using Bitbucket.Net.Models.RefRestrictions; using Bitbucket.Net.Models.RefSync; +using System; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; @@ -676,4 +676,4 @@ public void CommentSeverityToString_Nullable_ReturnsCorrectValue(CommentSeverity } #endregion -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs b/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs index 27d782e..ab8c9d9 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs @@ -1,11 +1,11 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Text.Json; using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Serialization; +using System; +using System.Collections.Generic; +using System.Text.Json; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; @@ -195,7 +195,7 @@ public void ErrorResponse_Serialization_RoundTrips() Assert.NotNull(deserialized); Assert.NotNull(deserialized.Errors); - + var errorList = new List(deserialized.Errors); Assert.Equal(2, errorList.Count); Assert.Equal("First error", errorList[0].Message); @@ -297,4 +297,4 @@ public void ToUnixTimeSeconds_KnownDate_ReturnsCorrectValue() } #endregion -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/UnitTests/DiffStreamingExtensionsTests.cs b/test/Bitbucket.Net.Tests/UnitTests/DiffStreamingExtensionsTests.cs index 52c0b82..e3ef054 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/DiffStreamingExtensionsTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/DiffStreamingExtensionsTests.cs @@ -1,10 +1,10 @@ #nullable enable +using Bitbucket.Net.Common.Mcp; +using Bitbucket.Net.Models.Core.Projects; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using Bitbucket.Net.Common.Mcp; -using Bitbucket.Net.Models.Core.Projects; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; @@ -288,4 +288,4 @@ private static async Task> ToListAsync(IAsyncEnumerable source) } #endregion -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/UnitTests/ExceptionTests.cs b/test/Bitbucket.Net.Tests/UnitTests/ExceptionTests.cs index fa3836e..2644d17 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/ExceptionTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/ExceptionTests.cs @@ -1,17 +1,17 @@ -using System.Collections.Generic; -using System.Net; using Bitbucket.Net.Common.Exceptions; using Bitbucket.Net.Common.Models; +using System.Collections.Generic; +using System.Net; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; public class ExceptionTests { - private static readonly List SampleErrors = new() - { + private static readonly List SampleErrors = + [ new Error { Message = "Test error", Context = "test-context" } - }; + ]; #region BitbucketApiException Factory Tests @@ -140,7 +140,7 @@ public void Create_WithContextInError_IncludesContextInMessage() { var errors = new List { - new Error { Message = "Field is invalid", Context = "username" } + new() { Message = "Field is invalid", Context = "username" } }; var exception = BitbucketApiException.Create(400, errors); @@ -153,7 +153,7 @@ public void Create_WithoutContextInError_OmitsContextFromMessage() { var errors = new List { - new Error { Message = "Something went wrong", Context = null } + new() { Message = "Something went wrong", Context = null } }; var exception = BitbucketApiException.Create(400, errors); @@ -168,8 +168,8 @@ public void Create_WithMultipleErrors_IncludesAllMessages() { var errors = new List { - new Error { Message = "Error 1", Context = "field1" }, - new Error { Message = "Error 2", Context = "field2" } + new() { Message = "Error 1", Context = "field1" }, + new() { Message = "Error 2", Context = "field2" } }; var exception = BitbucketApiException.Create(400, errors); @@ -188,7 +188,7 @@ public void BitbucketApiException_Properties_AreSetCorrectly() { var errors = new List { - new Error { Message = "Test", Context = "ctx" } + new() { Message = "Test", Context = "ctx" } }; var exception = new BitbucketApiException("Test message", HttpStatusCode.NotFound, errors, "https://api.test"); @@ -237,4 +237,4 @@ public void BitbucketApiException_WithInnerException_PreservesInnerException() } #endregion -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs b/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs index dbdd7df..f48e8d9 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs @@ -1,7 +1,7 @@ -using System; -using System.Text.Json; using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Projects; +using System; +using System.Text.Json; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; @@ -381,4 +381,4 @@ public void RolesConverter_Read_NumberToken_ThrowsJsonException() } #endregion -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/UnitTests/McpExtensionsTests.cs b/test/Bitbucket.Net.Tests/UnitTests/McpExtensionsTests.cs index 87a1520..4117484 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/McpExtensionsTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/McpExtensionsTests.cs @@ -1,11 +1,11 @@ #nullable enable +using Bitbucket.Net.Common.Mcp; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using Bitbucket.Net.Common.Mcp; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; @@ -27,10 +27,12 @@ public async Task TakeWithPaginationAsync_EmptySource_ReturnsEmptyResult() Assert.Equal(0, result.Count); } + private static readonly int[] int32Array = new[] { 1, 2, 3 }; + [Fact] public async Task TakeWithPaginationAsync_LessThanLimit_ReturnsAllItems() { - var source = CreateAsyncEnumerable(new[] { 1, 2, 3 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.TakeWithPaginationAsync(10); @@ -40,10 +42,12 @@ public async Task TakeWithPaginationAsync_LessThanLimit_ReturnsAllItems() Assert.Null(result.NextOffset); } + private static readonly int[] int32Array = new[] { 1, 2, 3, 4, 5 }; + [Fact] public async Task TakeWithPaginationAsync_ExactlyLimit_ReturnsAllItems() { - var source = CreateAsyncEnumerable(new[] { 1, 2, 3, 4, 5 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.TakeWithPaginationAsync(5); @@ -52,10 +56,12 @@ public async Task TakeWithPaginationAsync_ExactlyLimit_ReturnsAllItems() Assert.Null(result.NextOffset); } + private static readonly int[] int32Array = new[] { 1, 2, 3, 4, 5, 6, 7 }; + [Fact] public async Task TakeWithPaginationAsync_MoreThanLimit_ReturnsLimitedItems() { - var source = CreateAsyncEnumerable(new[] { 1, 2, 3, 4, 5, 6, 7 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.TakeWithPaginationAsync(5); @@ -65,12 +71,14 @@ public async Task TakeWithPaginationAsync_MoreThanLimit_ReturnsLimitedItems() Assert.Equal(5, result.NextOffset); } + private static readonly int[] int32Array = new[] { 1, 2, 3 }; + [Fact] public async Task TakeWithPaginationAsync_AcceptsCancellationToken() { // Just verify the method accepts and passes through the cancellation token var cts = new CancellationTokenSource(); - var source = CreateAsyncEnumerable(new[] { 1, 2, 3 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.TakeWithPaginationAsync(10, cts.Token); @@ -91,30 +99,36 @@ public async Task TakeAsync_EmptySource_YieldsNothing() Assert.Empty(result); } + private static readonly int[] int32Array = new[] { 1, 2, 3 }; + [Fact] public async Task TakeAsync_LessThanLimit_YieldsAllItems() { - var source = CreateAsyncEnumerable(new[] { 1, 2, 3 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.TakeAsync(10).ToListAsync(); Assert.Equal(new[] { 1, 2, 3 }, result); } + private static readonly int[] int32Array = new[] { 1, 2, 3, 4, 5, 6, 7 }; + [Fact] public async Task TakeAsync_MoreThanLimit_YieldsLimitedItems() { - var source = CreateAsyncEnumerable(new[] { 1, 2, 3, 4, 5, 6, 7 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.TakeAsync(3).ToListAsync(); Assert.Equal(new[] { 1, 2, 3 }, result); } + private static readonly int[] int32Array = new[] { 1, 2, 3 }; + [Fact] public async Task TakeAsync_ZeroLimit_YieldsNothing() { - var source = CreateAsyncEnumerable(new[] { 1, 2, 3 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.TakeAsync(0).ToListAsync(); @@ -135,30 +149,36 @@ public async Task SkipAsync_EmptySource_YieldsNothing() Assert.Empty(result); } + private static readonly int[] int32Array = new[] { 1, 2, 3, 4, 5 }; + [Fact] public async Task SkipAsync_SkipLessThanCount_YieldsRemaining() { - var source = CreateAsyncEnumerable(new[] { 1, 2, 3, 4, 5 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.SkipAsync(2).ToListAsync(); Assert.Equal(new[] { 3, 4, 5 }, result); } + private static readonly int[] int32Array = new[] { 1, 2, 3 }; + [Fact] public async Task SkipAsync_SkipMoreThanCount_YieldsNothing() { - var source = CreateAsyncEnumerable(new[] { 1, 2, 3 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.SkipAsync(10).ToListAsync(); Assert.Empty(result); } + private static readonly int[] int32Array = new[] { 1, 2, 3 }; + [Fact] public async Task SkipAsync_SkipZero_YieldsAllItems() { - var source = CreateAsyncEnumerable(new[] { 1, 2, 3 }); + var source = CreateAsyncEnumerable(int32Array); var result = await source.SkipAsync(0).ToListAsync(); @@ -246,7 +266,7 @@ public void PaginatedResult_Items_IsReadOnly() { var result = new PaginatedResult([1, 2, 3], hasMore: false, nextOffset: null); - Assert.IsAssignableFrom>(result.Items); + Assert.IsType>(result.Items, exactMatch: false); } #endregion @@ -276,4 +296,4 @@ public static async Task> ToListAsync(this IAsyncEnumerable source } return list; } -} +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/UnitTests/ModelSerializationTests.cs b/test/Bitbucket.Net.Tests/UnitTests/ModelSerializationTests.cs index eb37ae1..287a057 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/ModelSerializationTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/ModelSerializationTests.cs @@ -1,13 +1,13 @@ #nullable enable -using System; -using System.Collections.Generic; -using System.Text.Json; -using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Models.Builds; using Bitbucket.Net.Models.Core.Admin; +using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Core.Users; -using Bitbucket.Net.Models.Builds; using Bitbucket.Net.Serialization; +using System; +using System.Collections.Generic; +using System.Text.Json; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; @@ -552,4 +552,4 @@ public void PullRequest_WithFromRef_Serialization_RoundTrips() } #endregion -} +} \ No newline at end of file From 615c0b800e5f6f2e7fc2e6b143118a9784a93f2c Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:09:44 +0000 Subject: [PATCH 07/50] feat: add formatting verification scripts and pre-commit hook --- .editorconfig | 8 ++++ .githooks/pre-commit | 28 ++++++++++++ .github/workflows/ci.yml | 3 ++ .github/workflows/publish.yml | 3 ++ CONTRIBUTING.md | 80 +++++++++++++++++++++++++++++++++++ README.md | 5 +++ scripts/setup-githooks.ps1 | 23 ++++++++++ scripts/setup-githooks.sh | 20 +++++++++ scripts/verify-format.ps1 | 35 +++++++++++++++ scripts/verify-format.sh | 66 +++++++++++++++++++++++++++++ 10 files changed, 271 insertions(+) create mode 100644 .githooks/pre-commit create mode 100644 CONTRIBUTING.md create mode 100644 scripts/setup-githooks.ps1 create mode 100644 scripts/setup-githooks.sh create mode 100644 scripts/verify-format.ps1 create mode 100644 scripts/verify-format.sh diff --git a/.editorconfig b/.editorconfig index 7c5f337..565f5f1 100644 --- a/.editorconfig +++ b/.editorconfig @@ -240,6 +240,14 @@ dotnet_diagnostic.IDE0055.severity = none # partial classes across folders, so namespace != folder is expected dotnet_diagnostic.IDE0130.severity = none +# IDE* suggestions that can cause noisy or potentially breaking refactors when +# running `dotnet format style --severity info` (e.g., renames). Keep formatting +# verification focused on whitespace + explicit style preferences. +dotnet_diagnostic.IDE1006.severity = none +dotnet_diagnostic.IDE0028.severity = none +dotnet_diagnostic.IDE0300.severity = none +dotnet_diagnostic.IDE0305.severity = none + # Meziantou/CA rules that have no auto code-fix — suppress so dotnet format # does not emit "Unable to fix" warnings dotnet_diagnostic.MA0011.severity = none diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100644 index 0000000..6f7daf3 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,28 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Git pre-commit hook. +# +# To enable for this repo: +# bash scripts/setup-githooks.sh +# (or) ./scripts/setup-githooks.ps1 +# +# To bypass once: +# git commit --no-verify + +if [[ "${SKIP_DOTNET_FORMAT:-}" == "1" ]]; then + exit 0 +fi + +ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || true)" +if [[ -z "$ROOT_DIR" ]]; then + echo "pre-commit: not inside a git repository" >&2 + exit 1 +fi + +bash "$ROOT_DIR/scripts/verify-format.sh" --verify || { + echo "" >&2 + echo "Commit aborted: formatting verification failed." >&2 + echo "Fix by running: bash scripts/verify-format.sh --fix" >&2 + exit 1 +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1d9dc4c..531d6a5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,6 +31,9 @@ jobs: - name: Restore run: dotnet restore + - name: Verify formatting + run: bash scripts/verify-format.sh --verify + - name: Build run: dotnet build --no-restore -c Release diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index f898bca..891aeef 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -33,6 +33,9 @@ jobs: - name: Restore run: dotnet restore + - name: Verify formatting + run: bash scripts/verify-format.sh --verify + - name: Build run: dotnet build --no-restore -c Release diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..ee9797b --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,80 @@ +## Contributing + +Thanks for your interest in contributing! This repository enforces a consistent +C# code style via `.editorconfig`, a pre-commit hook (opt-in), and CI checks. + +## Prerequisites + +- Git +- .NET SDK 10.x +- Bash (macOS/Linux: built-in; Windows: Git for Windows ships Git Bash) + +## Enable the pre-commit hook (recommended) + +Git hooks are not transferred by default when you clone a repository. This repo +keeps hooks in `.githooks/` and uses `core.hooksPath` so they can be versioned. + +Run one of the setup scripts from the repo root: + +```powershell +./scripts/setup-githooks.ps1 +``` + +```bash +bash scripts/setup-githooks.sh +``` + +This sets: + +- `git config core.hooksPath .githooks` + +After that, every `git commit` will run the formatting verification. + +### Bypass (when you really need to) + +- One-off bypass: + +```bash +git commit --no-verify +``` + +- Disable the formatting hook for a command/session: + +```bash +SKIP_DOTNET_FORMAT=1 git commit +``` + +## Formatting: verify vs fix + +The hook (and CI) run formatting in **verify** mode: + +- `dotnet format whitespace ... --verify-no-changes` +- `dotnet format style ... --severity warn --verify-no-changes` + +To run the same checks manually: + +```powershell +./scripts/verify-format.ps1 +``` + +```bash +bash scripts/verify-format.sh --verify +``` + +To apply fixes locally: + +```powershell +./scripts/verify-format.ps1 -Fix +``` + +```bash +bash scripts/verify-format.sh --fix +``` + +## Tests + +Please ensure tests pass before submitting a PR. + +```bash +dotnet test ./test/Bitbucket.Net.Tests/Bitbucket.Net.Tests.csproj +``` diff --git a/README.md b/README.md index 3c5ebc2..22f4aca 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,11 @@ Modernized C# client for **Bitbucket Server** (Stash) REST API. +## Contributing + +Development setup (including the pre-commit formatting hook) is documented in +[CONTRIBUTING.md](CONTRIBUTING.md). + > **Fork notice** — This is an actively maintained fork of > [lvermeulen/Bitbucket.Net](https://github.com/lvermeulen/Bitbucket.Net), > which appears to be abandoned (last release 2020). diff --git a/scripts/setup-githooks.ps1 b/scripts/setup-githooks.ps1 new file mode 100644 index 0000000..34bb30c --- /dev/null +++ b/scripts/setup-githooks.ps1 @@ -0,0 +1,23 @@ +[CmdletBinding()] +param() + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +if (-not (Get-Command git -ErrorAction SilentlyContinue)) { + throw 'git is required but was not found in PATH.' +} + +$rootDir = (git rev-parse --show-toplevel 2>$null) +if (-not $rootDir) { + throw 'Not inside a git repository.' +} + +Push-Location -LiteralPath $rootDir +try { + git config core.hooksPath .githooks + Write-Output 'Git hooks enabled (core.hooksPath=.githooks).' +} +finally { + Pop-Location +} diff --git a/scripts/setup-githooks.sh b/scripts/setup-githooks.sh new file mode 100644 index 0000000..5a5f520 --- /dev/null +++ b/scripts/setup-githooks.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(git rev-parse --show-toplevel 2>/dev/null || true)" + +if [[ -z "$ROOT_DIR" ]]; then + echo "Not inside a git repository." >&2 + exit 1 +fi + +cd "$ROOT_DIR" + +git config core.hooksPath .githooks + +# Make sure hook script is executable on macOS/Linux. +if command -v chmod >/dev/null 2>&1; then + chmod +x .githooks/pre-commit 2>/dev/null || true +fi + +echo "Git hooks enabled (core.hooksPath=.githooks)." >&2 diff --git a/scripts/verify-format.ps1 b/scripts/verify-format.ps1 new file mode 100644 index 0000000..5a919dd --- /dev/null +++ b/scripts/verify-format.ps1 @@ -0,0 +1,35 @@ +[CmdletBinding()] +param( + [Parameter()] + [switch]$Fix +) + +Set-StrictMode -Version Latest +$ErrorActionPreference = 'Stop' + +$rootDir = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path +$solutionPath = Join-Path $rootDir 'Bitbucket.Net.slnx' + +if (-not (Test-Path -LiteralPath $solutionPath)) { + throw "Solution not found at: $solutionPath" +} + +$verifyArgs = @() +if (-not $Fix) { + $verifyArgs += '--verify-no-changes' +} + +if (-not (Get-Command dotnet -ErrorAction SilentlyContinue)) { + throw 'dotnet is required but was not found in PATH.' +} + +if ($env:SKIP_DOTNET_FORMAT_WHITESPACE -ne '1') { + Write-Verbose "Running: dotnet format whitespace $solutionPath" + dotnet format whitespace $solutionPath @verifyArgs +} +else { + Write-Verbose 'Skipping whitespace formatting (SKIP_DOTNET_FORMAT_WHITESPACE=1)' +} + +Write-Verbose "Running: dotnet format style $solutionPath" +dotnet format style $solutionPath --severity warn @verifyArgs diff --git a/scripts/verify-format.sh b/scripts/verify-format.sh new file mode 100644 index 0000000..c8424d2 --- /dev/null +++ b/scripts/verify-format.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Verifies (default) or fixes formatting for the repository using dotnet format. +# +# Usage: +# bash scripts/verify-format.sh # verify +# bash scripts/verify-format.sh --verify # verify +# bash scripts/verify-format.sh --fix # apply fixes + +case "${1:-}" in + --fix) + MODE="fix" + ;; + --verify|"") + MODE="verify" + ;; + -h|--help) + cat <<'EOF' +verify-format.sh + +Verifies (default) or fixes formatting using dotnet format. + +Usage: + bash scripts/verify-format.sh # verify + bash scripts/verify-format.sh --verify # verify + bash scripts/verify-format.sh --fix # apply fixes + +Environment variables: + SKIP_DOTNET_FORMAT_WHITESPACE=1 Skip whitespace formatting checks. +EOF + exit 0 + ;; + *) + echo "Unknown argument: $1" >&2 + exit 2 + ;; +esac + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SOLUTION_PATH="$ROOT_DIR/Bitbucket.Net.slnx" + +if [[ ! -f "$SOLUTION_PATH" ]]; then + echo "Solution not found at: $SOLUTION_PATH" >&2 + exit 1 +fi + +if ! command -v dotnet >/dev/null 2>&1; then + echo "dotnet is required but was not found in PATH." >&2 + exit 1 +fi + +VERIFY_ARGS=() +if [[ "$MODE" == "verify" ]]; then + VERIFY_ARGS+=(--verify-no-changes) +fi + +if [[ "${SKIP_DOTNET_FORMAT_WHITESPACE:-}" != "1" ]]; then + echo "Running: dotnet format whitespace ($MODE)" >&2 + dotnet format whitespace "$SOLUTION_PATH" "${VERIFY_ARGS[@]}" +else + echo "Skipping: dotnet format whitespace (SKIP_DOTNET_FORMAT_WHITESPACE=1)" >&2 +fi + +echo "Running: dotnet format style ($MODE)" >&2 +dotnet format style "$SOLUTION_PATH" --severity warn "${VERIFY_ARGS[@]}" From a08cd2d6db53ee09bec4d136c5e7a80f11ff8144 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:20:45 +0000 Subject: [PATCH 08/50] refactor: remove redundant expected arrays and simplify assertions in McpExtensionsTests --- .../Common/Mcp/McpExtensionsTests.cs | 17 +- .../UnitTests/McpExtensionsTests.cs | 299 ------------------ 2 files changed, 6 insertions(+), 310 deletions(-) delete mode 100644 test/Bitbucket.Net.Tests/UnitTests/McpExtensionsTests.cs diff --git a/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs b/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs index db28e61..1d3f00c 100644 --- a/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs +++ b/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs @@ -88,8 +88,6 @@ public async Task TakeWithPaginationAsync_SupportsDeconstruction() Assert.Equal(10, nextOffset); } - private static readonly int[] expected = new[] { 0, 1, 2, 3, 4 }; - [Fact] public async Task TakeWithPaginationAsync_RespectsItemOrder() { @@ -100,7 +98,7 @@ public async Task TakeWithPaginationAsync_RespectsItemOrder() var result = await source.TakeWithPaginationAsync(5); // Assert - Assert.Equal(expected, result.Items); + Assert.Equal(new[] { 0, 1, 2, 3, 4 }, result.Items); } #endregion @@ -151,8 +149,6 @@ public async Task TakeAsync_StopsEnumerationAfterLimit() Assert.True(itemsGenerated <= 11, $"Expected at most 11 items generated, but got {itemsGenerated}"); } - private static readonly int[] expected = new[] { 5, 6, 7, 8, 9 }; - #endregion #region SkipAsync Tests @@ -168,7 +164,7 @@ public async Task SkipAsync_WithValidOffset_SkipsCorrectItems() // Assert Assert.Equal(5, result.Count); - Assert.Equal(expected, result); + Assert.Equal(new[] { 5, 6, 7, 8, 9 }, result); } [Fact] @@ -255,13 +251,13 @@ public async Task TakeWithPaginationAsync_RespecsCancellation() { // Arrange - create source that checks cancellation using var cts = new CancellationTokenSource(); - var source = CreateCancellableAsyncEnumerable(100, cts); + var source = CreateCancellableAsyncEnumerable(100, cts.Token); // Cancel after a short delay _ = Task.Run(async () => { await Task.Delay(10); - cts.Cancel(); + await cts.CancelAsync(); }); // Act & Assert @@ -274,13 +270,13 @@ public async Task TakeAsync_RespectsCancellation() { // Arrange - create source that checks cancellation using var cts = new CancellationTokenSource(); - var source = CreateCancellableAsyncEnumerable(100, cts); + var source = CreateCancellableAsyncEnumerable(100, cts.Token); // Cancel after a short delay _ = Task.Run(async () => { await Task.Delay(10); - cts.Cancel(); + await cts.CancelAsync(); }); // Act & Assert @@ -313,7 +309,6 @@ private static async IAsyncEnumerable CreateCountingAsyncEnumerable(int cou private static async IAsyncEnumerable CreateCancellableAsyncEnumerable( int count, - CancellationTokenSource cts, [EnumeratorCancellation] CancellationToken cancellationToken = default) { for (int i = 0; i < count; i++) diff --git a/test/Bitbucket.Net.Tests/UnitTests/McpExtensionsTests.cs b/test/Bitbucket.Net.Tests/UnitTests/McpExtensionsTests.cs deleted file mode 100644 index 4117484..0000000 --- a/test/Bitbucket.Net.Tests/UnitTests/McpExtensionsTests.cs +++ /dev/null @@ -1,299 +0,0 @@ -#nullable enable - -using Bitbucket.Net.Common.Mcp; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Xunit; - -namespace Bitbucket.Net.Tests.UnitTests; - -public class McpExtensionsTests -{ - #region TakeWithPaginationAsync Tests - - [Fact] - public async Task TakeWithPaginationAsync_EmptySource_ReturnsEmptyResult() - { - var source = AsyncEnumerable.Empty(); - - var result = await source.TakeWithPaginationAsync(10); - - Assert.Empty(result.Items); - Assert.False(result.HasMore); - Assert.Null(result.NextOffset); - Assert.Equal(0, result.Count); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3 }; - - [Fact] - public async Task TakeWithPaginationAsync_LessThanLimit_ReturnsAllItems() - { - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.TakeWithPaginationAsync(10); - - Assert.Equal(3, result.Items.Count); - Assert.Equal(new[] { 1, 2, 3 }, result.Items); - Assert.False(result.HasMore); - Assert.Null(result.NextOffset); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3, 4, 5 }; - - [Fact] - public async Task TakeWithPaginationAsync_ExactlyLimit_ReturnsAllItems() - { - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.TakeWithPaginationAsync(5); - - Assert.Equal(5, result.Items.Count); - Assert.False(result.HasMore); - Assert.Null(result.NextOffset); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3, 4, 5, 6, 7 }; - - [Fact] - public async Task TakeWithPaginationAsync_MoreThanLimit_ReturnsLimitedItems() - { - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.TakeWithPaginationAsync(5); - - Assert.Equal(5, result.Items.Count); - Assert.Equal(new[] { 1, 2, 3, 4, 5 }, result.Items); - Assert.True(result.HasMore); - Assert.Equal(5, result.NextOffset); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3 }; - - [Fact] - public async Task TakeWithPaginationAsync_AcceptsCancellationToken() - { - // Just verify the method accepts and passes through the cancellation token - var cts = new CancellationTokenSource(); - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.TakeWithPaginationAsync(10, cts.Token); - - Assert.Equal(3, result.Items.Count); - } - - #endregion - - #region TakeAsync Tests - - [Fact] - public async Task TakeAsync_EmptySource_YieldsNothing() - { - var source = AsyncEnumerable.Empty(); - - var result = await source.TakeAsync(10).ToListAsync(); - - Assert.Empty(result); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3 }; - - [Fact] - public async Task TakeAsync_LessThanLimit_YieldsAllItems() - { - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.TakeAsync(10).ToListAsync(); - - Assert.Equal(new[] { 1, 2, 3 }, result); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3, 4, 5, 6, 7 }; - - [Fact] - public async Task TakeAsync_MoreThanLimit_YieldsLimitedItems() - { - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.TakeAsync(3).ToListAsync(); - - Assert.Equal(new[] { 1, 2, 3 }, result); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3 }; - - [Fact] - public async Task TakeAsync_ZeroLimit_YieldsNothing() - { - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.TakeAsync(0).ToListAsync(); - - Assert.Empty(result); - } - - #endregion - - #region SkipAsync Tests - - [Fact] - public async Task SkipAsync_EmptySource_YieldsNothing() - { - var source = AsyncEnumerable.Empty(); - - var result = await source.SkipAsync(5).ToListAsync(); - - Assert.Empty(result); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3, 4, 5 }; - - [Fact] - public async Task SkipAsync_SkipLessThanCount_YieldsRemaining() - { - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.SkipAsync(2).ToListAsync(); - - Assert.Equal(new[] { 3, 4, 5 }, result); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3 }; - - [Fact] - public async Task SkipAsync_SkipMoreThanCount_YieldsNothing() - { - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.SkipAsync(10).ToListAsync(); - - Assert.Empty(result); - } - - private static readonly int[] int32Array = new[] { 1, 2, 3 }; - - [Fact] - public async Task SkipAsync_SkipZero_YieldsAllItems() - { - var source = CreateAsyncEnumerable(int32Array); - - var result = await source.SkipAsync(0).ToListAsync(); - - Assert.Equal(new[] { 1, 2, 3 }, result); - } - - #endregion - - #region PageAsync Tests - - [Fact] - public async Task PageAsync_FirstPage_ReturnsCorrectItems() - { - var source = CreateAsyncEnumerable(Enumerable.Range(1, 100)); - - var result = await source.PageAsync(offset: 0, limit: 10); - - Assert.Equal(Enumerable.Range(1, 10), result.Items); - Assert.True(result.HasMore); - Assert.Equal(10, result.NextOffset); - } - - [Fact] - public async Task PageAsync_MiddlePage_ReturnsCorrectItems() - { - var source = CreateAsyncEnumerable(Enumerable.Range(1, 100)); - - var result = await source.PageAsync(offset: 20, limit: 10); - - Assert.Equal(Enumerable.Range(21, 10), result.Items); - Assert.True(result.HasMore); - Assert.Equal(10, result.NextOffset); - } - - [Fact] - public async Task PageAsync_LastPage_ReturnsRemainingItems() - { - var source = CreateAsyncEnumerable(Enumerable.Range(1, 25)); - - var result = await source.PageAsync(offset: 20, limit: 10); - - Assert.Equal(Enumerable.Range(21, 5), result.Items); - Assert.False(result.HasMore); - Assert.Null(result.NextOffset); - } - - [Fact] - public async Task PageAsync_BeyondEnd_ReturnsEmpty() - { - var source = CreateAsyncEnumerable(Enumerable.Range(1, 10)); - - var result = await source.PageAsync(offset: 100, limit: 10); - - Assert.Empty(result.Items); - Assert.False(result.HasMore); - Assert.Null(result.NextOffset); - } - - #endregion - - #region PaginatedResult Tests - - [Fact] - public void PaginatedResult_Count_ReturnsItemCount() - { - var result = new PaginatedResult([1, 2, 3], hasMore: true, nextOffset: 3); - - Assert.Equal(3, result.Count); - } - - [Fact] - public void PaginatedResult_Deconstruct_Works() - { - var result = new PaginatedResult([1, 2], hasMore: true, nextOffset: 2); - - var (items, hasMore, nextOffset) = result; - - Assert.Equal(2, items.Count); - Assert.True(hasMore); - Assert.Equal(2, nextOffset); - } - - [Fact] - public void PaginatedResult_Items_IsReadOnly() - { - var result = new PaginatedResult([1, 2, 3], hasMore: false, nextOffset: null); - - Assert.IsType>(result.Items, exactMatch: false); - } - - #endregion - - #region Helper Methods - - private static async IAsyncEnumerable CreateAsyncEnumerable(IEnumerable source) - { - foreach (var item in source) - { - yield return item; - await Task.Yield(); // Simulate async behavior - } - } - - #endregion -} - -internal static class AsyncEnumerableTestExtensions -{ - public static async Task> ToListAsync(this IAsyncEnumerable source) - { - var list = new List(); - await foreach (var item in source) - { - list.Add(item); - } - return list; - } -} \ No newline at end of file From cdac727164e5f6012f0e1ada884ce0719fbd6cf8 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:27:38 +0000 Subject: [PATCH 09/50] refactor: update JsonConverter for Permissions property to use PermissionsListConverter --- .../Common/Converters/PermissionsConverter.cs | 13 +++++++++++++ .../PersonalAccessTokens/AccessTokenCreate.cs | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs b/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs index c2c33f5..28e1149 100644 --- a/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs @@ -9,6 +9,19 @@ protected override string ConvertToString(Permissions value) return BitbucketHelpers.PermissionToString(value); } + protected override Permissions ConvertFromString(string s) + { + return BitbucketHelpers.StringToPermission(s); + } +} + +public class PermissionsListConverter : JsonEnumListConverter +{ + protected override string ConvertToString(Permissions value) + { + return BitbucketHelpers.PermissionToString(value); + } + protected override Permissions ConvertFromString(string s) { return BitbucketHelpers.StringToPermission(s); diff --git a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs index e81df37..e0ae487 100644 --- a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs +++ b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs @@ -8,6 +8,6 @@ namespace Bitbucket.Net.Models.PersonalAccessTokens; public class AccessTokenCreate { public string? Name { get; set; } - [JsonConverter(typeof(PermissionsConverter))] + [JsonConverter(typeof(PermissionsListConverter))] public List? Permissions { get; set; } } \ No newline at end of file From 250d57f6ba1d28e330bc27af2058307c63ec2bda Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 16:48:03 +0000 Subject: [PATCH 10/50] refactor: enhance documentation for audit URL methods and improve dictionary initialization --- src/Bitbucket.Net/Audit/BitbucketClient.cs | 39 ++++++++++++++++++---- 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/src/Bitbucket.Net/Audit/BitbucketClient.cs b/src/Bitbucket.Net/Audit/BitbucketClient.cs index 4fa1be0..524b3c8 100644 --- a/src/Bitbucket.Net/Audit/BitbucketClient.cs +++ b/src/Bitbucket.Net/Audit/BitbucketClient.cs @@ -1,6 +1,7 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Audit; using Flurl.Http; +using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; @@ -9,12 +10,30 @@ namespace Bitbucket.Net; public partial class BitbucketClient { + /// + /// Gets the base audit URL. + /// + /// An representing the audit endpoint. private IFlurlRequest GetAuditUrl() => GetBaseUrl("/audit"); + /// + /// Gets the audit URL with the specified path appended. + /// + /// The path to append to the audit URL. + /// An representing the audit endpoint with the specified path. private IFlurlRequest GetAuditUrl(string path) => GetAuditUrl() .AppendPathSegment(path); - [System.Diagnostics.CodeAnalysis.SuppressMessage("AsyncUsage", "AsyncFixer01:Unnecessary async/await usage", Justification = "")] + /// + /// Retrieves audit events for a specific project. + /// + /// The key of the project. + /// The maximum number of pages to retrieve. If null, all pages are retrieved. + /// The maximum number of results per page. + /// The starting index for pagination. + /// The size of user avatars to include in the response. + /// A cancellation token that can be used to cancel the operation. + /// A task that represents the asynchronous operation. The task result contains a collection of objects. public async Task> GetProjectAuditEventsAsync(string projectKey, int? maxPages = null, int? limit = null, @@ -22,8 +41,7 @@ public async Task> GetProjectAuditEventsAsync(string pro int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -38,7 +56,17 @@ await GetAuditUrl($"/projects/{projectKey}/events") .ConfigureAwait(false); } - [System.Diagnostics.CodeAnalysis.SuppressMessage("AsyncUsage", "AsyncFixer01:Unnecessary async/await usage", Justification = "")] + /// + /// Retrieves audit events for a specific repository within a project. + /// + /// The key of the project. + /// The slug (identifier) of the repository. + /// The maximum number of pages to retrieve. If null, all pages are retrieved. + /// The maximum number of results per page. + /// The starting index for pagination. + /// The size of user avatars to include in the response. + /// A cancellation token that can be used to cancel the operation. + /// A task that represents the asynchronous operation. The task result contains a collection of objects. public async Task> GetProjectRepoAuditEventsAsync(string projectKey, string repositorySlug, int? maxPages = null, int? limit = null, @@ -46,8 +74,7 @@ public async Task> GetProjectRepoAuditEventsAsync(string int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(System.StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, From c2c8db671a0fa04f8185f4ab170023732d30d6c2 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:17:44 +0000 Subject: [PATCH 11/50] refactor: add xml docs to files --- src/Bitbucket.Net/Audit/BitbucketClient.cs | 2 +- src/Bitbucket.Net/BitbucketClient.cs | 56 ++++++ src/Bitbucket.Net/Branches/BitbucketClient.cs | 49 +++++ src/Bitbucket.Net/Builds/BitbucketClient.cs | 46 +++++ .../CommentLikes/BitbucketClient.cs | 73 ++++++++ .../ApplicationProperties/BitbucketClient.cs | 12 ++ .../Core/Dashboard/BitbucketClient.cs | 34 ++++ .../Core/Groups/BitbucketClient.cs | 16 ++ .../Core/Hooks/BitbucketClient.cs | 14 ++ .../Core/Inbox/BitbucketClient.cs | 26 +++ .../Core/Logs/BitbucketClient.cs | 36 ++++ .../Core/Markup/BitbucketClient.cs | 21 +++ .../Core/Profile/BitbucketClient.cs | 21 +++ .../Core/Projects/BitbucketClient.cs | 101 ++++++++++ .../Core/Repos/BitbucketClient.cs | 31 ++++ .../Core/Tasks/BitbucketClient.cs | 38 ++++ .../Core/Users/BitbucketClient.cs | 64 +++++++ .../Core/WhoAmI/BitbucketClient.cs | 3 + .../DefaultReviewers/BitbucketClient.cs | 86 +++++++++ src/Bitbucket.Net/Git/BitbucketClient.cs | 47 +++++ src/Bitbucket.Net/Jira/BitbucketClient.cs | 39 ++++ .../PersonalAccessTokens/BitbucketClient.cs | 52 ++++++ .../RefRestrictions/BitbucketClient.cs | 114 ++++++++++++ src/Bitbucket.Net/RefSync/BitbucketClient.cs | 36 ++++ src/Bitbucket.Net/Ssh/BitbucketClient.cs | 173 ++++++++++++++++++ 25 files changed, 1189 insertions(+), 1 deletion(-) diff --git a/src/Bitbucket.Net/Audit/BitbucketClient.cs b/src/Bitbucket.Net/Audit/BitbucketClient.cs index 524b3c8..dd214c8 100644 --- a/src/Bitbucket.Net/Audit/BitbucketClient.cs +++ b/src/Bitbucket.Net/Audit/BitbucketClient.cs @@ -61,7 +61,7 @@ await GetAuditUrl($"/projects/{projectKey}/events") /// /// The key of the project. /// The slug (identifier) of the repository. - /// The maximum number of pages to retrieve. If null, all pages are retrieved. + /// The maximum number of pages to retrieve. If , all pages are retrieved. /// The maximum number of results per page. /// The starting index for pagination. /// The size of user avatars to include in the response. diff --git a/src/Bitbucket.Net/BitbucketClient.cs b/src/Bitbucket.Net/BitbucketClient.cs index 6237952..e687081 100644 --- a/src/Bitbucket.Net/BitbucketClient.cs +++ b/src/Bitbucket.Net/BitbucketClient.cs @@ -19,6 +19,9 @@ namespace Bitbucket.Net; +/// +/// Client for interacting with Bitbucket Server REST APIs. +/// public partial class BitbucketClient { private static readonly JsonSerializerOptions s_jsonOptions = new() @@ -66,6 +69,10 @@ static BitbucketClient() private readonly string? _password; private readonly IFlurlClient? _injectedClient; + /// + /// Initializes a new instance of the class with the specified base URL. + /// + /// The base Bitbucket Server URL. private BitbucketClient(string url) { _url = url; @@ -152,6 +159,12 @@ public BitbucketClient(IFlurlClient flurlClient, Func? getToken = null) _getToken = getToken; } + /// + /// Builds a Flurl request rooted at the Bitbucket REST API. + /// + /// The API root segment (default is /api). + /// The API version segment (default is 1.0). + /// An configured with authentication and serialization. private IFlurlRequest GetBaseUrl(string root = "/api", string version = "1.0") { // If using injected client, use it directly @@ -178,6 +191,14 @@ private IFlurlRequest GetBaseUrl(string root = "/api", string version = "1.0") .WithAuthentication(_getToken, _userName, _password); } + /// + /// Reads the response content and deserializes it. + /// + /// The type of the result. + /// The HTTP response. + /// Optional custom handler to parse the response content. + /// Token to cancel the operation. + /// The deserialized response content. private static async Task ReadResponseContentAsync(IFlurlResponse response, Func? contentHandler = null, CancellationToken cancellationToken = default) { string content = await response.GetStringAsync().ConfigureAwait(false); @@ -186,12 +207,24 @@ private static async Task ReadResponseContentAsync(IFlurlRespo : JsonSerializer.Deserialize(content, s_jsonOptions)!; } + /// + /// Reads the response content and returns success based on empty body. + /// + /// The HTTP response. + /// Token to cancel the operation. + /// true if the response body is empty; otherwise, false. private static async Task ReadResponseContentAsync(IFlurlResponse response, CancellationToken cancellationToken = default) { string content = await response.GetStringAsync().ConfigureAwait(false); return content == ""; } + /// + /// Throws an exception if the response indicates an error. + /// + /// The HTTP response. + /// Token to cancel the operation. + /// A task that completes when error handling is finished. private static async Task HandleErrorsAsync(IFlurlResponse response, CancellationToken cancellationToken = default) { if (response.StatusCode >= 400) @@ -231,18 +264,41 @@ private static async Task HandleErrorsAsync(IFlurlResponse response, Cancellatio } } + /// + /// Handles an HTTP response, throwing on errors and deserializing the content. + /// + /// The result type. + /// The HTTP response. + /// Optional custom content handler. + /// Token to cancel the operation. + /// The deserialized response content. private static async Task HandleResponseAsync(IFlurlResponse response, Func? contentHandler = null, CancellationToken cancellationToken = default) { await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); return await ReadResponseContentAsync(response, contentHandler, cancellationToken).ConfigureAwait(false); } + /// + /// Handles an HTTP response, throwing on errors and returning a boolean success indicator. + /// + /// The HTTP response. + /// Token to cancel the operation. + /// true if the response body is empty; otherwise, false. private static async Task HandleResponseAsync(IFlurlResponse response, CancellationToken cancellationToken = default) { await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); return await ReadResponseContentAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves paged results from a paginated endpoint. + /// + /// The item type in the paged results. + /// Optional maximum number of pages to retrieve. + /// The query parameter values for requests. + /// A delegate that retrieves a page of results. + /// Token to cancel the operation. + /// All retrieved items. private static async Task> GetPagedResultsAsync(int? maxPages, IDictionary queryParamValues, Func, CancellationToken, Task>> selector, CancellationToken cancellationToken = default) { var results = new List(); diff --git a/src/Bitbucket.Net/Branches/BitbucketClient.cs b/src/Bitbucket.Net/Branches/BitbucketClient.cs index aae10e6..c42a4b4 100644 --- a/src/Bitbucket.Net/Branches/BitbucketClient.cs +++ b/src/Bitbucket.Net/Branches/BitbucketClient.cs @@ -12,13 +12,36 @@ namespace Bitbucket.Net; +/// +/// Provides branch-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base URL for branch utilities. + /// + /// An configured for branch utilities. private IFlurlRequest GetBranchUrl() => GetBaseUrl("/branch-utils"); + /// + /// Gets the branch utilities URL for the specified path. + /// + /// The path to append to the branch utilities root. + /// An pointing to the requested branch utilities path. private IFlurlRequest GetBranchUrl(string path) => GetBranchUrl() .AppendPathSegment(path); + /// + /// Retrieves branch information for a specific commit. + /// + /// The project key. + /// The repository slug. + /// The full commit SHA to query. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of branch information entries for the commit. public async Task> GetCommitBranchInfoAsync(string projectKey, string repositorySlug, string fullSha, int? maxPages = null, int? limit = null, @@ -40,6 +63,13 @@ await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches/info .ConfigureAwait(false); } + /// + /// Retrieves the branch model configuration for a repository. + /// + /// The project key. + /// The repository slug. + /// Token to cancel the operation. + /// The branch model configuration. public async Task GetRepoBranchModelAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { return await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branchmodel") @@ -47,6 +77,15 @@ public async Task GetRepoBranchModelAsync(string projectKey, string .ConfigureAwait(false); } + /// + /// Creates a new branch in a repository. + /// + /// The project key. + /// The repository slug. + /// The name of the new branch. + /// The commit or ref from which to branch. + /// Token to cancel the operation. + /// The created branch. public async Task CreateRepoBranchAsync(string projectKey, string repositorySlug, string branchName, string startPoint, CancellationToken cancellationToken = default) { var data = new @@ -62,6 +101,16 @@ public async Task CreateRepoBranchAsync(string projectKey, string reposi return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a branch from a repository. + /// + /// The project key. + /// The repository slug. + /// The name of the branch to delete. + /// If true, performs validation without deleting. + /// Optional endpoint ref to compare for merge checks. + /// Token to cancel the operation. + /// true if the branch was deleted; otherwise, false. public async Task DeleteRepoBranchAsync(string projectKey, string repositorySlug, string branchName, bool dryRun, string? endPoint = null, CancellationToken cancellationToken = default) { var data = new diff --git a/src/Bitbucket.Net/Builds/BitbucketClient.cs b/src/Bitbucket.Net/Builds/BitbucketClient.cs index 719f62c..06a2f5f 100644 --- a/src/Bitbucket.Net/Builds/BitbucketClient.cs +++ b/src/Bitbucket.Net/Builds/BitbucketClient.cs @@ -8,13 +8,32 @@ namespace Bitbucket.Net; +/// +/// Provides build status related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base build status URL. + /// + /// An targeting the build-status root. private IFlurlRequest GetBuildsUrl() => GetBaseUrl("/build-status"); + /// + /// Gets the build status URL for the specified path. + /// + /// The path to append to the build-status root. + /// An pointing to the build-status path. private IFlurlRequest GetBuildsUrl(string path) => GetBuildsUrl() .AppendPathSegment(path); + /// + /// Retrieves build statistics for a specific commit. + /// + /// The commit identifier. + /// Whether to include unique build statistics. + /// Token to cancel the operation. + /// Build statistics for the commit. public async Task GetBuildStatsForCommitAsync(string commitId, bool includeUnique = false, CancellationToken cancellationToken = default) { return await GetBuildsUrl($"/commits/stats/{commitId}") @@ -23,6 +42,12 @@ public async Task GetBuildStatsForCommitAsync(string commitId, bool .ConfigureAwait(false); } + /// + /// Retrieves build statistics for multiple commits. + /// + /// Token to cancel the operation. + /// The commit identifiers. + /// A dictionary mapping commit IDs to build statistics. public async Task> GetBuildStatsForCommitsAsync(CancellationToken cancellationToken, params string[] commitIds) { var response = await GetBuildsUrl("/commits/stats") @@ -32,11 +57,25 @@ public async Task> GetBuildStatsForCommitsAsync(C return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves build statistics for multiple commits using the default cancellation token. + /// + /// The commit identifiers. + /// A dictionary mapping commit IDs to build statistics. public async Task> GetBuildStatsForCommitsAsync(params string[] commitIds) { return await GetBuildStatsForCommitsAsync(default, commitIds).ConfigureAwait(false); } + /// + /// Retrieves build status entries for a specific commit. + /// + /// The commit identifier. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of build status entries. public async Task> GetBuildStatusForCommitAsync(string commitId, int? maxPages = null, int? limit = null, @@ -57,6 +96,13 @@ await GetBuildsUrl($"/commits/{commitId}") .ConfigureAwait(false); } + /// + /// Associates a build status with a commit. + /// + /// The commit identifier. + /// The build status to associate. + /// Token to cancel the operation. + /// true if the association was successful; otherwise, false. public async Task AssociateBuildStatusWithCommitAsync(string commitId, BuildStatus buildStatus, CancellationToken cancellationToken = default) { var response = await GetBuildsUrl($"/commits/{commitId}") diff --git a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs index 346122a..74a6fc6 100644 --- a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs +++ b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs @@ -8,13 +8,38 @@ namespace Bitbucket.Net; +/// +/// Provides comment-like related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base URL for comment likes. + /// + /// An targeting the comment likes root. private IFlurlRequest GetCommentLikesUrl() => GetBaseUrl("/comment-likes"); + /// + /// Gets the comment likes URL for the specified path. + /// + /// The path to append to the comment likes root. + /// An pointing to the comment likes path. private IFlurlRequest GetCommentLikesUrl(string path) => GetCommentLikesUrl() .AppendPathSegment(path); + /// + /// Retrieves users who liked a commit comment. + /// + /// The project key. + /// The repository slug. + /// The commit identifier. + /// The comment identifier. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of users who liked the comment. public async Task> GetCommitCommentLikesAsync(string projectKey, string repositorySlug, string commitId, string commentId, int? maxPages = null, int? limit = null, @@ -38,6 +63,15 @@ await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits .ConfigureAwait(false); } + /// + /// Adds a like to a commit comment. + /// + /// The project key. + /// The repository slug. + /// The commit identifier. + /// The comment identifier. + /// Token to cancel the operation. + /// true if the like was added; otherwise, false. public async Task LikeCommitCommentAsync(string projectKey, string repositorySlug, string commitId, string commentId, CancellationToken cancellationToken = default) { var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") @@ -47,6 +81,15 @@ public async Task LikeCommitCommentAsync(string projectKey, string reposit return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Removes a like from a commit comment. + /// + /// The project key. + /// The repository slug. + /// The commit identifier. + /// The comment identifier. + /// Token to cancel the operation. + /// true if the like was removed; otherwise, false. public async Task UnlikeCommitCommentAsync(string projectKey, string repositorySlug, string commitId, string commentId, CancellationToken cancellationToken = default) { var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") @@ -56,6 +99,18 @@ public async Task UnlikeCommitCommentAsync(string projectKey, string repos return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves users who liked a pull request comment. + /// + /// The project key. + /// The repository slug. + /// The pull request identifier. + /// The comment identifier. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of users who liked the pull request comment. public async Task> GetPullRequestCommentLikesAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, int? maxPages = null, int? limit = null, @@ -77,6 +132,15 @@ await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-re .ConfigureAwait(false); } + /// + /// Adds a like to a pull request comment. + /// + /// The project key. + /// The repository slug. + /// The pull request identifier. + /// The comment identifier. + /// Token to cancel the operation. + /// true if the like was added; otherwise, false. public async Task LikePullRequestCommentAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, CancellationToken cancellationToken = default) { var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") @@ -86,6 +150,15 @@ public async Task LikePullRequestCommentAsync(string projectKey, string re return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Removes a like from a pull request comment. + /// + /// The project key. + /// The repository slug. + /// The pull request identifier. + /// The comment identifier. + /// Token to cancel the operation. + /// true if the like was removed; otherwise, false. public async Task UnlikePullRequestCommentAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, CancellationToken cancellationToken = default) { var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") diff --git a/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs b/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs index 56b396d..b797c09 100644 --- a/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs @@ -6,11 +6,23 @@ namespace Bitbucket.Net; +/// +/// Provides application properties Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base application properties URL. + /// + /// An targeting the application-properties endpoint. private IFlurlRequest GetApplicationPropertiesUrl() => GetBaseUrl() .AppendPathSegment("/application-properties"); + /// + /// Retrieves application properties. + /// + /// Token to cancel the operation. + /// A dictionary of application property values. public async Task> GetApplicationPropertiesAsync(CancellationToken cancellationToken = default) { var response = await GetApplicationPropertiesUrl() diff --git a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs index 7f4a426..dda680d 100644 --- a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs @@ -9,14 +9,39 @@ namespace Bitbucket.Net; +/// +/// Provides dashboard-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base dashboard URL. + /// + /// An targeting the dashboard root. private IFlurlRequest GetDashboardUrl() => GetBaseUrl() .AppendPathSegment("/dashboard"); + /// + /// Gets the dashboard URL for the specified path. + /// + /// The path to append to the dashboard root. + /// An pointing to the dashboard path. private IFlurlRequest GetDashboardUrl(string path) => GetDashboardUrl() .AppendPathSegment(path); + /// + /// Retrieves pull requests for the current user's dashboard. + /// + /// Optional pull request state filter. + /// Optional participant role filter. + /// Optional participant status filters. + /// Optional sort order. + /// Optional filter for recently closed PRs (seconds). + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of pull requests. public async Task> GetDashboardPullRequestsAsync(PullRequestStates? state = null, Roles? role = null, List? status = null, @@ -47,6 +72,15 @@ await GetDashboardUrl("/pull-requests") .ConfigureAwait(false); } + /// + /// Retrieves pull request suggestions for the current user. + /// + /// Time window in seconds to consider changes. + /// Optional maximum number of pages to retrieve. + /// Optional page size (default 3). + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of pull request suggestions. public async Task> GetDashboardPullRequestSuggestionsAsync(int changesSinceSeconds = 172800, int? maxPages = null, int? limit = 3, diff --git a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs index 7042081..fe36850 100644 --- a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs @@ -6,11 +6,27 @@ namespace Bitbucket.Net; +/// +/// Provides group-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base groups URL. + /// + /// An targeting the groups endpoint. private IFlurlRequest GetGroupsUrl() => GetBaseUrl() .AppendPathSegment("/groups"); + /// + /// Retrieves group names with optional filtering. + /// + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of group names. public async Task> GetGroupNamesAsync(string? filter = null, int? maxPages = null, int? limit = null, diff --git a/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs b/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs index 1e01ad1..9a3d744 100644 --- a/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs @@ -4,11 +4,25 @@ namespace Bitbucket.Net; +/// +/// Provides hook-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base hooks URL. + /// + /// An targeting the hooks endpoint. private IFlurlRequest GetHooksUrl() => GetBaseUrl() .AppendPathSegment("/hooks"); + /// + /// Retrieves the avatar for a project hook. + /// + /// The hook key. + /// Optional avatar version. + /// Token to cancel the operation. + /// The avatar image bytes. public async Task GetProjectHooksAvatarAsync(string hookKey, string? version = null, CancellationToken cancellationToken = default) { return await GetHooksUrl() diff --git a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs index c8abf84..f6d7e21 100644 --- a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs @@ -9,14 +9,35 @@ namespace Bitbucket.Net; +/// +/// Provides inbox-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base inbox URL. + /// + /// An targeting the inbox endpoint. private IFlurlRequest GetInboxUrl() => GetBaseUrl() .AppendPathSegment("/inbox"); + /// + /// Gets the inbox URL for the specified path. + /// + /// The path to append to the inbox endpoint. + /// An pointing to the inbox path. private IFlurlRequest GetInboxUrl(string path) => GetInboxUrl() .AppendPathSegment(path); + /// + /// Retrieves pull requests in the user's inbox. + /// + /// Optional maximum number of pages to retrieve. + /// Optional page size (default 25). + /// Optional starting index for pagination. + /// The participant role filter (default reviewer). + /// Token to cancel the operation. + /// A collection of pull requests. public async Task> GetInboxPullRequestsAsync( int? maxPages = null, int? limit = 25, @@ -40,6 +61,11 @@ await GetInboxUrl("/pull-requests") .ConfigureAwait(false); } + /// + /// Retrieves the count of pull requests in the user's inbox. + /// + /// Token to cancel the operation. + /// The number of inbox pull requests. public async Task GetInboxPullRequestsCountAsync(CancellationToken cancellationToken = default) { var response = await GetInboxUrl("/pull-requests/count") diff --git a/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs b/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs index c9bbec7..95dc4ba 100644 --- a/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs @@ -8,14 +8,32 @@ namespace Bitbucket.Net; +/// +/// Provides log-level management Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base logs URL. + /// + /// An targeting the logs endpoint. private IFlurlRequest GetLogsUrl() => GetBaseUrl() .AppendPathSegment("/logs"); + /// + /// Gets the logs URL for the specified path. + /// + /// The path to append to the logs endpoint. + /// An pointing to the logs path. private IFlurlRequest GetLogsUrl(string path) => GetLogsUrl() .AppendPathSegment(path); + /// + /// Retrieves the log level for a specific logger. + /// + /// The logger name. + /// Token to cancel the operation. + /// The configured log level. public async Task GetLogLevelAsync(string loggerName, CancellationToken cancellationToken = default) { var response = await GetLogsUrl($"/logger/{loggerName}") @@ -30,6 +48,13 @@ public async Task GetLogLevelAsync(string loggerName, CancellationTok .ConfigureAwait(false); } + /// + /// Sets the log level for a specific logger. + /// + /// The logger name. + /// The log level to set. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. public async Task SetLogLevelAsync(string loggerName, LogLevels logLevel, CancellationToken cancellationToken = default) { var response = await GetLogsUrl($"/logger/{loggerName}/{BitbucketHelpers.LogLevelToString(logLevel)}") @@ -39,6 +64,11 @@ public async Task SetLogLevelAsync(string loggerName, LogLevels logLevel, return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves the root logger's log level. + /// + /// Token to cancel the operation. + /// The root log level. public async Task GetRootLogLevelAsync(CancellationToken cancellationToken = default) { var response = await GetLogsUrl("/logger/rootLogger") @@ -53,6 +83,12 @@ public async Task GetRootLogLevelAsync(CancellationToken cancellation .ConfigureAwait(false); } + /// + /// Sets the root logger's log level. + /// + /// The log level to set. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. public async Task SetRootLogLevelAsync(LogLevels logLevel, CancellationToken cancellationToken = default) { var response = await GetLogsUrl($"/logger/rootLogger/{BitbucketHelpers.LogLevelToString(logLevel)}") diff --git a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs index 2922fd2..6ccd570 100644 --- a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs @@ -8,14 +8,35 @@ namespace Bitbucket.Net; +/// +/// Provides markup preview Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base markup URL. + /// + /// An targeting the markup endpoint. private IFlurlRequest GetMarkupUrl() => GetBaseUrl() .AppendPathSegment("/markup"); + /// + /// Gets the markup URL for the specified path. + /// + /// The path to append to the markup endpoint. + /// An pointing to the markup path. private IFlurlRequest GetMarkupUrl(string path) => GetMarkupUrl() .AppendPathSegment(path); + /// + /// Previews markup text as HTML. + /// + /// The markup text to preview. + /// Optional URL rendering mode. + /// Whether to hard wrap lines. + /// Whether to HTML-escape content. + /// Token to cancel the operation. + /// The rendered HTML. public async Task PreviewMarkupAsync(string text, string? urlMode = null, bool? hardWrap = null, diff --git a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs index 13a089f..be1dc1e 100644 --- a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs @@ -9,14 +9,35 @@ namespace Bitbucket.Net; +/// +/// Provides profile-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base profile URL. + /// + /// An targeting the profile endpoint. private IFlurlRequest GetProfileUrl() => GetBaseUrl() .AppendPathSegment("/profile"); + /// + /// Gets the profile URL for the specified path. + /// + /// The path to append to the profile endpoint. + /// An pointing to the profile path. private IFlurlRequest GetProfileUrl(string path) => GetProfileUrl() .AppendPathSegment(path); + /// + /// Retrieves recent repositories for the current user. + /// + /// Optional permission filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of recent repositories. public async Task> GetRecentReposAsync(Permissions? permission = null, int? maxPages = null, int? limit = null, diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs index 2360a67..85216a8 100644 --- a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs @@ -20,22 +20,62 @@ namespace Bitbucket.Net; +/// +/// Provides project and repository management Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base projects URL. + /// + /// An targeting the projects endpoint. private IFlurlRequest GetProjectsUrl() => GetBaseUrl() .AppendPathSegment("/projects"); + /// + /// Gets the projects URL for the specified path. + /// + /// The path to append. + /// An pointing to the projects path. private IFlurlRequest GetProjectsUrl(string path) => GetProjectsUrl() .AppendPathSegment(path); + /// + /// Gets the URL for a specific project. + /// + /// The project key. + /// An pointing to the project. private IFlurlRequest GetProjectUrl(string projectKey) => GetProjectsUrl() .AppendPathSegment($"/{projectKey}"); + /// + /// Gets the URL for a repository within a project. + /// + /// The project key. + /// The repository slug. + /// An pointing to the repository. private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug) => GetProjectsUrl($"/{projectKey}/repos/{repositorySlug}"); + /// + /// Gets the URL for a specific path within a project repository. + /// + /// The project key. + /// The repository slug. + /// The additional path to append. + /// An pointing to the repository path. private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug, string path) => GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment(path); + /// + /// Retrieves projects accessible to the current user. + /// + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional project name filter. + /// Optional permission filter. + /// Token to cancel the operation. + /// A collection of projects. public async Task> GetProjectsAsync( int? maxPages = null, int? limit = null, @@ -65,6 +105,16 @@ await GetProjectsUrl() /// Streams all projects as an IAsyncEnumerable, yielding items as they are retrieved. /// This is more memory-efficient for large result sets. /// + /// + /// Streams projects accessible to the current user. + /// + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional project name filter. + /// Optional permission filter. + /// Token to cancel the operation. + /// An async enumerable of projects. public IAsyncEnumerable GetProjectsStreamAsync( int? maxPages = null, int? limit = null, @@ -89,6 +139,12 @@ await GetProjectsUrl() .ConfigureAwait(false), cancellationToken); } + /// + /// Creates a project. + /// + /// The project definition. + /// Token to cancel the operation. + /// The created project. public async Task CreateProjectAsync(ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) { var response = await GetProjectsUrl() @@ -98,6 +154,12 @@ public async Task CreateProjectAsync(ProjectDefinition projectDefinitio return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a project. + /// + /// The project key. + /// Token to cancel the operation. + /// true if deletion succeeded; otherwise, false. public async Task DeleteProjectAsync(string projectKey, CancellationToken cancellationToken = default) { var response = await GetProjectsUrl($"/{projectKey}") @@ -107,6 +169,13 @@ public async Task DeleteProjectAsync(string projectKey, CancellationToken return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Updates a project. + /// + /// The project key. + /// The updated project definition. + /// Token to cancel the operation. + /// The updated project. public async Task UpdateProjectAsync(string projectKey, ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) { var response = await GetProjectsUrl($"/{projectKey}") @@ -116,6 +185,12 @@ public async Task UpdateProjectAsync(string projectKey, ProjectDefiniti return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a project by key. + /// + /// The project key. + /// Token to cancel the operation. + /// The requested project. public async Task GetProjectAsync(string projectKey, CancellationToken cancellationToken = default) { var response = await GetProjectsUrl($"/{projectKey}") @@ -125,6 +200,17 @@ public async Task GetProjectAsync(string projectKey, CancellationToken return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves user permissions for a project. + /// + /// The project key. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// A collection of user permissions. public async Task> GetProjectUserPermissionsAsync(string projectKey, string? filter = null, int? maxPages = null, int? limit = null, @@ -149,6 +235,13 @@ await GetProjectsUrl($"/{projectKey}/permissions/users") .ConfigureAwait(false); } + /// + /// Removes a user's permissions from a project. + /// + /// The project key. + /// The user name. + /// Token to cancel the operation. + /// true if removal succeeded; otherwise, false. public async Task DeleteProjectUserPermissionsAsync(string projectKey, string userName, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -165,6 +258,14 @@ public async Task DeleteProjectUserPermissionsAsync(string projectKey, str return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Updates a user's permissions for a project. + /// + /// The project key. + /// The user name. + /// The permission to grant. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. public async Task UpdateProjectUserPermissionsAsync(string projectKey, string userName, Permissions permission, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary diff --git a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs index 95efdba..e4e52af 100644 --- a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs @@ -9,11 +9,30 @@ namespace Bitbucket.Net; +/// +/// Provides repository listing Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base repositories URL. + /// + /// An targeting the repos endpoint. private IFlurlRequest GetReposUrl() => GetBaseUrl() .AppendPathSegment("/repos"); + /// + /// Retrieves repositories accessible to the current user. + /// + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional repository name filter. + /// Optional project name filter. + /// Optional permission filter. + /// Whether to include only public repositories. + /// Token to cancel the operation. + /// A collection of repositories. public async Task> GetRepositoriesAsync( int? maxPages = null, int? limit = null, @@ -46,6 +65,18 @@ await GetReposUrl() /// /// Streams all repositories as an IAsyncEnumerable, yielding items as they are retrieved. /// + /// + /// Streams repositories accessible to the current user. + /// + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional repository name filter. + /// Optional project name filter. + /// Optional permission filter. + /// Whether to include only public repositories. + /// Token to cancel the operation. + /// An async enumerable of repositories. public IAsyncEnumerable GetRepositoriesStreamAsync( int? maxPages = null, int? limit = null, diff --git a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs index 42b6877..82bdd75 100644 --- a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs @@ -5,14 +5,32 @@ namespace Bitbucket.Net; +/// +/// Provides task-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base tasks URL. + /// + /// An targeting the tasks endpoint. private IFlurlRequest GetTasksUrl() => GetBaseUrl() .AppendPathSegment("/tasks"); + /// + /// Gets the tasks URL for the specified path. + /// + /// The path to append to the tasks endpoint. + /// An pointing to the tasks path. private IFlurlRequest GetTasksUrl(string path) => GetTasksUrl() .AppendPathSegment(path); + /// + /// Creates a task. + /// + /// The task information. + /// Token to cancel the operation. + /// The created task. public async Task CreateTaskAsync(TaskInfo taskInfo, CancellationToken cancellationToken = default) { var response = await GetTasksUrl() @@ -22,6 +40,13 @@ public async Task CreateTaskAsync(TaskInfo taskInfo, Cancellation return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a task by identifier. + /// + /// The task identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// The requested task. public async Task GetTaskAsync(long taskId, int? avatarSize = null, CancellationToken cancellationToken = default) { return await GetTasksUrl($"/{taskId}") @@ -30,6 +55,13 @@ public async Task GetTaskAsync(long taskId, int? avatarSize = nul .ConfigureAwait(false); } + /// + /// Updates a task's text. + /// + /// The task identifier. + /// The updated task text. + /// Token to cancel the operation. + /// The updated task. public async Task UpdateTaskAsync(long taskId, string text, CancellationToken cancellationToken = default) { var obj = new @@ -45,6 +77,12 @@ public async Task UpdateTaskAsync(long taskId, string text, Cance return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a task. + /// + /// The task identifier. + /// Token to cancel the operation. + /// true if the task was deleted; otherwise, false. public async Task DeleteTaskAsync(long taskId, CancellationToken cancellationToken = default) { var response = await GetTasksUrl($"/{taskId}") diff --git a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs index 9cdd694..2f0a2c9 100644 --- a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs @@ -8,14 +8,39 @@ namespace Bitbucket.Net; +/// +/// Provides user-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base users URL. + /// + /// An targeting the users endpoint. private IFlurlRequest GetUsersUrl() => GetBaseUrl() .AppendPathSegment("/users"); + /// + /// Gets the users URL for the specified path. + /// + /// The path to append to the users endpoint. + /// An pointing to the users path. private IFlurlRequest GetUsersUrl(string path) => GetUsersUrl() .AppendPathSegment(path); + /// + /// Retrieves users with optional filters. + /// + /// Optional search filter. + /// Optional group filter. + /// Optional permission filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// Additional permission filters. + /// A collection of users. public async Task> GetUsersAsync(string? filter = null, string? group = null, string? permission = null, int? maxPages = null, int? limit = null, @@ -50,6 +75,13 @@ await GetUsersUrl() .ConfigureAwait(false); } + /// + /// Updates the current user's profile fields. + /// + /// Optional email address. + /// Optional display name. + /// Token to cancel the operation. + /// The updated user. public async Task UpdateUserAsync(string? email = null, string? displayName = null, CancellationToken cancellationToken = default) { var obj = new @@ -65,6 +97,12 @@ public async Task UpdateUserAsync(string? email = null, string? displayNam return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Updates the current user's credentials. + /// + /// The password change payload. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. public async Task UpdateUserCredentialsAsync(PasswordChange passwordChange, CancellationToken cancellationToken = default) { var response = await GetUsersUrl("/credentials") @@ -74,6 +112,13 @@ public async Task UpdateUserCredentialsAsync(PasswordChange passwordChange return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a user by slug. + /// + /// The user slug. + /// Optional avatar size for the returned user. + /// Token to cancel the operation. + /// The requested user. public async Task GetUserAsync(string userSlug, int? avatarSize = null, CancellationToken cancellationToken = default) { return await GetUsersUrl($"/{userSlug}") @@ -82,6 +127,12 @@ public async Task GetUserAsync(string userSlug, int? avatarSize = null, Ca .ConfigureAwait(false); } + /// + /// Deletes a user's avatar. + /// + /// The user slug. + /// Token to cancel the operation. + /// true if the avatar was deleted; otherwise, false. public async Task DeleteUserAvatarAsync(string userSlug, CancellationToken cancellationToken = default) { var response = await GetUsersUrl($"/{userSlug}/avatar.png") @@ -91,6 +142,12 @@ public async Task DeleteUserAvatarAsync(string userSlug, CancellationToken return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves user settings. + /// + /// The user slug. + /// Token to cancel the operation. + /// A dictionary of user settings. public async Task> GetUserSettingsAsync(string userSlug, CancellationToken cancellationToken = default) { var response = await GetUsersUrl($"/{userSlug}/settings") @@ -100,6 +157,13 @@ public async Task DeleteUserAvatarAsync(string userSlug, CancellationToken return response; } + /// + /// Updates user settings. + /// + /// The user slug. + /// The settings to update. + /// Token to cancel the operation. + /// true if the settings were updated; otherwise, false. public async Task UpdateUserSettingsAsync(string userSlug, IDictionary userSettings, CancellationToken cancellationToken = default) { var response = await GetUsersUrl($"/{userSlug}/settings") diff --git a/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs b/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs index ee2a699..14c899a 100644 --- a/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs @@ -6,6 +6,9 @@ namespace Bitbucket.Net; +/// +/// Provides user identity helper operations. +/// public partial class BitbucketClient { /// diff --git a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs index d26fe11..bb27026 100644 --- a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs +++ b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs @@ -7,13 +7,32 @@ namespace Bitbucket.Net; +/// +/// Provides default reviewer related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base default reviewers URL. + /// + /// An targeting the default reviewers root. private IFlurlRequest GetDefaultReviewersUrl() => GetBaseUrl("/default-reviewers"); + /// + /// Gets the default reviewers URL for the specified path. + /// + /// The path to append to the default reviewers root. + /// An pointing to the requested default reviewers path. private IFlurlRequest GetDefaultReviewersUrl(string path) => GetDefaultReviewersUrl() .AppendPathSegment(path); + /// + /// Retrieves default reviewer conditions for a project. + /// + /// The project key. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of default reviewer conditions. public async Task> GetDefaultReviewerConditionsAsync(string projectKey, int? avatarSize = null, CancellationToken cancellationToken = default) { @@ -25,6 +44,13 @@ public async Task> GetDefaultRe return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Creates a default reviewer condition for a project. + /// + /// The project key. + /// The condition to create. + /// Token to cancel the operation. + /// The created default reviewer condition. public async Task CreateDefaultReviewerConditionAsync(string projectKey, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") @@ -34,6 +60,14 @@ public async Task CreateDefaultReviewerCond return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Updates a default reviewer condition for a project. + /// + /// The project key. + /// The condition identifier. + /// The updated condition. + /// Token to cancel the operation. + /// The updated default reviewer condition. public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") @@ -43,6 +77,13 @@ public async Task UpdateDefaultReviewerCond return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a default reviewer condition from a project. + /// + /// The project key. + /// The condition identifier. + /// Token to cancel the operation. + /// true if the condition was deleted; otherwise, false. public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") @@ -52,6 +93,14 @@ public async Task DeleteDefaultReviewerConditionAsync(string projectKey, s return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves default reviewer conditions for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of default reviewer conditions. public async Task> GetDefaultReviewerConditionsAsync(string projectKey, string repositorySlug, int? avatarSize = null, CancellationToken cancellationToken = default) { @@ -63,6 +112,14 @@ public async Task> GetDefaultRe return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Creates a default reviewer condition for a repository. + /// + /// The project key. + /// The repository slug. + /// The condition to create. + /// Token to cancel the operation. + /// The created default reviewer condition. public async Task CreateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") @@ -72,6 +129,15 @@ public async Task CreateDefaultReviewerCond return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Updates a default reviewer condition for a repository. + /// + /// The project key. + /// The repository slug. + /// The condition identifier. + /// The updated condition. + /// Token to cancel the operation. + /// The updated default reviewer condition. public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") @@ -81,6 +147,14 @@ public async Task UpdateDefaultReviewerCond return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a default reviewer condition from a repository. + /// + /// The project key. + /// The repository slug. + /// The condition identifier. + /// Token to cancel the operation. + /// true if the condition was deleted; otherwise, false. public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") @@ -90,6 +164,18 @@ public async Task DeleteDefaultReviewerConditionAsync(string projectKey, s return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves default reviewers for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional source repository identifier. + /// Optional target repository identifier. + /// Optional source reference identifier. + /// Optional target reference identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of default reviewers. public async Task> GetDefaultReviewersAsync(string projectKey, string repositorySlug, int? sourceRepoId = null, int? targetRepoId = null, diff --git a/src/Bitbucket.Net/Git/BitbucketClient.cs b/src/Bitbucket.Net/Git/BitbucketClient.cs index 8b8ca0f..0e27d9e 100644 --- a/src/Bitbucket.Net/Git/BitbucketClient.cs +++ b/src/Bitbucket.Net/Git/BitbucketClient.cs @@ -7,13 +7,33 @@ namespace Bitbucket.Net; +/// +/// Provides Git-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base Git URL. + /// + /// An targeting the Git root. private IFlurlRequest GetGitUrl() => GetBaseUrl("/git"); + /// + /// Gets the Git URL for the specified path. + /// + /// The path to append to the Git root. + /// An pointing to the Git path. private IFlurlRequest GetGitUrl(string path) => GetGitUrl() .AppendPathSegment(path); + /// + /// Determines whether a pull request can be rebased. + /// + /// The project key. + /// The repository slug. + /// The pull request identifier. + /// Token to cancel the operation. + /// The rebase eligibility details. public async Task GetCanRebasePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/rebase") @@ -23,6 +43,15 @@ public async Task GetCanRebasePullRequestAsync(strin return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Rebases a pull request to the latest target branch state. + /// + /// The project key. + /// The repository slug. + /// The pull request identifier. + /// The pull request version for concurrency control. + /// Token to cancel the operation. + /// The updated pull request. public async Task RebasePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version, CancellationToken cancellationToken = default) { var data = new { version }; @@ -33,6 +62,16 @@ public async Task RebasePullRequestAsync(string projectKey, string return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Creates a tag in a repository. + /// + /// The project key. + /// The repository slug. + /// The type of tag to create. + /// The name of the tag. + /// The commit or ref where the tag should point. + /// Token to cancel the operation. + /// The created tag. public async Task CreateTagAsync(string projectKey, string repositorySlug, TagTypes tagType, string tagName, string startPoint, CancellationToken cancellationToken = default) { var data = new @@ -49,6 +88,14 @@ public async Task CreateTagAsync(string projectKey, string repositorySlug, return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a tag from a repository. + /// + /// The project key. + /// The repository slug. + /// The name of the tag to delete. + /// Token to cancel the operation. + /// true if the tag was deleted; otherwise, false. public async Task DeleteTagAsync(string projectKey, string repositorySlug, string tagName, CancellationToken cancellationToken = default) { var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/tags/{tagName}") diff --git a/src/Bitbucket.Net/Jira/BitbucketClient.cs b/src/Bitbucket.Net/Jira/BitbucketClient.cs index 764431b..98797cd 100644 --- a/src/Bitbucket.Net/Jira/BitbucketClient.cs +++ b/src/Bitbucket.Net/Jira/BitbucketClient.cs @@ -8,13 +8,35 @@ namespace Bitbucket.Net; +/// +/// Provides Jira-related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base Jira URL. + /// + /// An targeting the Jira root. private IFlurlRequest GetJiraUrl() => GetBaseUrl("/jira"); + /// + /// Gets the Jira URL for the specified path. + /// + /// The path to append to the Jira root. + /// An pointing to the Jira path. private IFlurlRequest GetJiraUrl(string path) => GetJiraUrl() .AppendPathSegment(path); + /// + /// Retrieves changesets linked to a Jira issue. + /// + /// The Jira issue key. + /// Maximum number of changes per commit to include. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of changesets. public async Task> GetChangeSetsAsync(string issueKey, int maxChanges = 10, int? maxPages = null, int? limit = null, @@ -37,6 +59,15 @@ await GetJiraUrl($"/issues/{issueKey}/commits") .ConfigureAwait(false); } + /// + /// Creates a Jira issue linked to a pull request comment. + /// + /// The pull request comment identifier. + /// The application identifier. + /// The issue title. + /// The issue type. + /// Token to cancel the operation. + /// The created Jira issue. public async Task CreateJiraIssueAsync(string pullRequestCommentId, string applicationId, string title, string type, CancellationToken cancellationToken = default) { var data = new @@ -54,6 +85,14 @@ public async Task CreateJiraIssueAsync(string pullRequestCommentId, s return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves Jira issues linked to a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request identifier. + /// Token to cancel the operation. + /// A collection of Jira issue links. public async Task> GetJiraIssuesAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { var response = await GetJiraUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/issues") diff --git a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs index 89e18c8..0f4ee43 100644 --- a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs +++ b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs @@ -7,13 +7,35 @@ namespace Bitbucket.Net; +/// +/// Provides personal access token related Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base personal access token URL. + /// + /// An targeting the PAT root. private IFlurlRequest GetPatUrl() => GetBaseUrl("/access-tokens"); + /// + /// Gets the personal access token URL for the specified path. + /// + /// The path to append to the PAT root. + /// An pointing to the PAT path. private IFlurlRequest GetPatUrl(string path) => GetPatUrl() .AppendPathSegment(path); + /// + /// Retrieves access tokens for a user. + /// + /// The user slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of access tokens. public async Task> GetUserAccessTokensAsync(string userSlug, int? maxPages = null, int? limit = null, @@ -37,6 +59,13 @@ await GetPatUrl($"/users/{userSlug}") .ConfigureAwait(false); } + /// + /// Creates a personal access token for a user. + /// + /// The user slug. + /// The token creation payload. + /// Token to cancel the operation. + /// The created access token including secret. public async Task CreateAccessTokenAsync(string userSlug, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) { var response = await GetPatUrl($"/users/{userSlug}") @@ -46,6 +75,14 @@ public async Task CreateAccessTokenAsync(string userSlug, Acces return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a personal access token by identifier. + /// + /// The user slug. + /// The token identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// The access token details. public async Task GetUserAccessTokenAsync(string userSlug, string tokenId, int? avatarSize = null, CancellationToken cancellationToken = default) { return await GetPatUrl($"/users/{userSlug}/{tokenId}") @@ -54,6 +91,14 @@ public async Task GetUserAccessTokenAsync(string userSlug, string t .ConfigureAwait(false); } + /// + /// Updates a personal access token. + /// + /// The user slug. + /// The token identifier. + /// The updated token payload. + /// Token to cancel the operation. + /// The updated access token details. public async Task ChangeUserAccessTokenAsync(string userSlug, string tokenId, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) { var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") @@ -63,6 +108,13 @@ public async Task ChangeUserAccessTokenAsync(string userSlug, strin return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a personal access token. + /// + /// The user slug. + /// The token identifier. + /// Token to cancel the operation. + /// true if the token was deleted; otherwise, false. public async Task DeleteUserAccessTokenAsync(string userSlug, string tokenId, CancellationToken cancellationToken = default) { var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") diff --git a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs index 5647cf4..bf5c668 100644 --- a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs @@ -8,13 +8,38 @@ namespace Bitbucket.Net; +/// +/// Provides reference restriction (branch permissions) Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base ref restrictions URL. + /// + /// An targeting the branch permissions root. private IFlurlRequest GetRefRestrictionsUrl() => GetBaseUrl("/branch-permissions", "2.0"); + /// + /// Gets the ref restrictions URL for the specified path. + /// + /// The path to append to the branch permissions root. + /// An pointing to the requested branch permissions path. private IFlurlRequest GetRefRestrictionsUrl(string path) => GetRefRestrictionsUrl() .AppendPathSegment(path); + /// + /// Retrieves reference restrictions for a project. + /// + /// The project key. + /// Optional restriction type filter. + /// Optional matcher type filter. + /// Optional matcher identifier filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of reference restrictions. public async Task> GetProjectRefRestrictionsAsync(string projectKey, RefRestrictionTypes? type = null, RefMatcherTypes? matcherType = null, @@ -44,6 +69,13 @@ await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") .ConfigureAwait(false); } + /// + /// Creates multiple reference restrictions for a project. + /// + /// The project key. + /// Token to cancel the operation. + /// The reference restrictions to create. + /// The created reference restrictions. public async Task> CreateProjectRefRestrictionsAsync(string projectKey, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") @@ -54,11 +86,24 @@ public async Task> CreateProjectRefRestrictionsAsync return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Creates multiple reference restrictions for a project using the default cancellation token. + /// + /// The project key. + /// The reference restrictions to create. + /// The created reference restrictions. public async Task> CreateProjectRefRestrictionsAsync(string projectKey, params RefRestrictionCreate[] refRestrictions) { return await CreateProjectRefRestrictionsAsync(projectKey, default, refRestrictions).ConfigureAwait(false); } + /// + /// Creates a single reference restriction for a project. + /// + /// The project key. + /// The reference restriction to create. + /// Token to cancel the operation. + /// The created reference restriction. public async Task CreateProjectRefRestrictionAsync(string projectKey, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") @@ -68,6 +113,14 @@ public async Task CreateProjectRefRestrictionAsync(string projec return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a specific project reference restriction. + /// + /// The project key. + /// The restriction identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// The requested reference restriction. public async Task GetProjectRefRestrictionAsync(string projectKey, int refRestrictionId, int? avatarSize = null, CancellationToken cancellationToken = default) { return await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") @@ -76,6 +129,13 @@ public async Task GetProjectRefRestrictionAsync(string projectKe .ConfigureAwait(false); } + /// + /// Deletes a project reference restriction. + /// + /// The project key. + /// The restriction identifier. + /// Token to cancel the operation. + /// true if the restriction was deleted; otherwise, false. public async Task DeleteProjectRefRestrictionAsync(string projectKey, int refRestrictionId, CancellationToken cancellationToken = default) { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") @@ -85,6 +145,20 @@ public async Task DeleteProjectRefRestrictionAsync(string projectKey, int return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves reference restrictions for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional restriction type filter. + /// Optional matcher type filter. + /// Optional matcher identifier filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of reference restrictions. public async Task> GetRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, RefRestrictionTypes? type = null, RefMatcherTypes? matcherType = null, @@ -114,6 +188,14 @@ await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/rest .ConfigureAwait(false); } + /// + /// Creates multiple reference restrictions for a repository. + /// + /// The project key. + /// The repository slug. + /// Token to cancel the operation. + /// The reference restrictions to create. + /// The created reference restrictions. public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") @@ -124,11 +206,26 @@ public async Task> CreateRepositoryRefRestrictionsAs return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Creates multiple reference restrictions for a repository using the default cancellation token. + /// + /// The project key. + /// The repository slug. + /// The reference restrictions to create. + /// The created reference restrictions. public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, params RefRestrictionCreate[] refRestrictions) { return await CreateRepositoryRefRestrictionsAsync(projectKey, repositorySlug, default, refRestrictions).ConfigureAwait(false); } + /// + /// Creates a single reference restriction for a repository. + /// + /// The project key. + /// The repository slug. + /// The reference restriction to create. + /// Token to cancel the operation. + /// The created reference restriction. public async Task CreateRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") @@ -138,6 +235,15 @@ public async Task CreateRepositoryRefRestrictionAsync(string pro return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a specific repository reference restriction. + /// + /// The project key. + /// The repository slug. + /// The restriction identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// The requested reference restriction. public async Task GetRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, int? avatarSize = null, CancellationToken cancellationToken = default) { @@ -147,6 +253,14 @@ public async Task GetRepositoryRefRestrictionAsync(string projec .ConfigureAwait(false); } + /// + /// Deletes a repository reference restriction. + /// + /// The project key. + /// The repository slug. + /// The restriction identifier. + /// Token to cancel the operation. + /// true if the restriction was deleted; otherwise, false. public async Task DeleteRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, CancellationToken cancellationToken = default) { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") diff --git a/src/Bitbucket.Net/RefSync/BitbucketClient.cs b/src/Bitbucket.Net/RefSync/BitbucketClient.cs index 37bb192..3ad93fd 100644 --- a/src/Bitbucket.Net/RefSync/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefSync/BitbucketClient.cs @@ -6,13 +6,33 @@ namespace Bitbucket.Net; +/// +/// Provides repository synchronization Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base reference synchronization URL. + /// + /// An targeting the sync root. private IFlurlRequest GetRefSyncUrl() => GetBaseUrl("/sync"); + /// + /// Gets the reference synchronization URL for the specified path. + /// + /// The path to append to the sync root. + /// An pointing to the sync path. private IFlurlRequest GetRefSyncUrl(string path) => GetRefSyncUrl() .AppendPathSegment(path); + /// + /// Retrieves the synchronization status for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional reference to scope the status. + /// Token to cancel the operation. + /// The repository synchronization status. public async Task GetRepositorySynchronizationStatusAsync(string projectKey, string repositorySlug, string? at = null, CancellationToken cancellationToken = default) { @@ -24,6 +44,14 @@ public async Task GetRepositorySynchronizationS return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Enables or disables repository synchronization. + /// + /// The project key. + /// The repository slug. + /// Whether synchronization should be enabled. + /// Token to cancel the operation. + /// The updated repository synchronization status. public async Task EnableRepositorySynchronizationAsync(string projectKey, string repositorySlug, bool enabled, CancellationToken cancellationToken = default) { var data = new @@ -38,6 +66,14 @@ public async Task EnableRepositorySynchronizati return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Triggers synchronization for a repository. + /// + /// The project key. + /// The repository slug. + /// The synchronization payload. + /// Token to cancel the operation. + /// The result of the synchronization. public async Task SynchronizeRepositoryAsync(string projectKey, string repositorySlug, Synchronize synchronize, CancellationToken cancellationToken = default) { var response = await GetRefSyncUrl($"/projects/{projectKey}/repos/{repositorySlug}") diff --git a/src/Bitbucket.Net/Ssh/BitbucketClient.cs b/src/Bitbucket.Net/Ssh/BitbucketClient.cs index c12d25d..235527e 100644 --- a/src/Bitbucket.Net/Ssh/BitbucketClient.cs +++ b/src/Bitbucket.Net/Ssh/BitbucketClient.cs @@ -13,18 +13,46 @@ namespace Bitbucket.Net; +/// +/// Provides SSH key management Bitbucket API operations. +/// public partial class BitbucketClient { + /// + /// Gets the base SSH keys URL. + /// + /// An targeting the keys root. private IFlurlRequest GetKeysUrl() => GetBaseUrl("/keys"); + /// + /// Gets the SSH keys URL for the specified path. + /// + /// The path to append to the keys root. + /// An pointing to the keys path. private IFlurlRequest GetKeysUrl(string path) => GetKeysUrl() .AppendPathSegment(path); + /// + /// Gets the base SSH URL. + /// + /// An targeting the SSH root. private IFlurlRequest GetSshUrl() => GetBaseUrl("/ssh"); + /// + /// Gets the SSH URL for the specified path. + /// + /// The path to append to the SSH root. + /// An pointing to the SSH path. private IFlurlRequest GetSshUrl(string path) => GetSshUrl() .AppendPathSegment(path); + /// + /// Deletes an SSH key from multiple projects or repositories. + /// + /// The SSH key identifier. + /// Token to cancel the operation. + /// Project or repository identifiers. + /// true if deletion succeeded; otherwise, false. public async Task DeleteProjectsReposKeysAsync(int keyId, CancellationToken cancellationToken, params string[] projectsOrRepos) { var json = JsonSerializer.Serialize(projectsOrRepos); @@ -36,11 +64,26 @@ public async Task DeleteProjectsReposKeysAsync(int keyId, CancellationToke return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Deletes an SSH key from multiple projects or repositories using the default cancellation token. + /// + /// The SSH key identifier. + /// Project or repository identifiers. + /// true if deletion succeeded; otherwise, false. public async Task DeleteProjectsReposKeysAsync(int keyId, params string[] projectsOrRepos) { return await DeleteProjectsReposKeysAsync(keyId, default, projectsOrRepos).ConfigureAwait(false); } + /// + /// Retrieves project keys associated with an SSH key identifier. + /// + /// The SSH key identifier. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of project keys. public async Task> GetProjectKeysAsync(int keyId, int? maxPages = null, int? limit = null, @@ -62,6 +105,17 @@ await GetKeysUrl($"/ssh/{keyId}/projects") .ConfigureAwait(false); } + /// + /// Retrieves project SSH keys within a project. + /// + /// The project key. + /// Optional filter for search. + /// Optional permission filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of project keys. public async Task> GetProjectKeysAsync(string projectKey, string? filter = null, Permissions? permission = null, @@ -87,6 +141,14 @@ await GetKeysUrl($"/projects/{projectKey}/ssh") .ConfigureAwait(false); } + /// + /// Creates an SSH key for a project. + /// + /// The project key. + /// The public key text. + /// The permission to grant. + /// Token to cancel the operation. + /// The created project key. public async Task CreateProjectKeyAsync(string projectKey, string keyText, Permissions permission, CancellationToken cancellationToken = default) { var data = new @@ -102,6 +164,13 @@ public async Task CreateProjectKeyAsync(string projectKey, string ke return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a specific project SSH key. + /// + /// The project key. + /// The key identifier. + /// Token to cancel the operation. + /// The requested project key. public async Task GetProjectKeyAsync(string projectKey, int keyId, CancellationToken cancellationToken = default) { var response = await GetKeysUrl($"/projects/{projectKey}/ssh/{keyId}") @@ -111,6 +180,13 @@ public async Task GetProjectKeyAsync(string projectKey, int keyId, C return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a project SSH key. + /// + /// The project key. + /// The key identifier. + /// Token to cancel the operation. + /// true if the key was deleted; otherwise, false. public async Task DeleteProjectKeyAsync(string projectKey, int keyId, CancellationToken cancellationToken = default) { var response = await GetKeysUrl($"/projects/{projectKey}/ssh/{keyId}") @@ -120,6 +196,14 @@ public async Task DeleteProjectKeyAsync(string projectKey, int keyId, Canc return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Updates the permission of a project SSH key. + /// + /// The project key. + /// The key identifier. + /// The permission to apply. + /// Token to cancel the operation. + /// The updated project key. public async Task UpdateProjectKeyPermissionAsync(string projectKey, int keyId, Permissions permission, CancellationToken cancellationToken = default) { var response = await GetKeysUrl($"/projects/{projectKey}/ssh/{keyId}/permissions/{BitbucketHelpers.PermissionToString(permission)}") @@ -129,6 +213,15 @@ public async Task UpdateProjectKeyPermissionAsync(string projectKey, return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves repository keys associated with an SSH key identifier. + /// + /// The SSH key identifier. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of repository keys. public async Task> GetRepoKeysAsync(int keyId, int? maxPages = null, int? limit = null, @@ -150,6 +243,19 @@ await GetKeysUrl($"/ssh/{keyId}/repos") .ConfigureAwait(false); } + /// + /// Retrieves repository SSH keys within a repository. + /// + /// The project key. + /// The repository slug. + /// Optional filter for search. + /// Whether to include effective permissions. + /// Optional permission filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of repository keys. public async Task> GetRepoKeysAsync(string projectKey, string repositorySlug, string? filter = null, bool? effective = null, @@ -177,6 +283,15 @@ await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh") .ConfigureAwait(false); } + /// + /// Creates an SSH key for a repository. + /// + /// The project key. + /// The repository slug. + /// The public key text. + /// The permission to grant. + /// Token to cancel the operation. + /// The created repository key. public async Task CreateRepoKeyAsync(string projectKey, string repositorySlug, string keyText, Permissions permission, CancellationToken cancellationToken = default) { var data = new @@ -192,6 +307,14 @@ public async Task CreateRepoKeyAsync(string projectKey, string re return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a specific repository SSH key. + /// + /// The project key. + /// The repository slug. + /// The key identifier. + /// Token to cancel the operation. + /// The requested repository key. public async Task GetRepoKeyAsync(string projectKey, string repositorySlug, int keyId, CancellationToken cancellationToken = default) { var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh/{keyId}") @@ -201,6 +324,14 @@ public async Task GetRepoKeyAsync(string projectKey, string repos return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a repository SSH key. + /// + /// The project key. + /// The repository slug. + /// The key identifier. + /// Token to cancel the operation. + /// true if the key was deleted; otherwise, false. public async Task DeleteRepoKeyAsync(string projectKey, string repositorySlug, int keyId, CancellationToken cancellationToken = default) { var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh/{keyId}") @@ -210,6 +341,15 @@ public async Task DeleteRepoKeyAsync(string projectKey, string repositoryS return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Updates the permission of a repository SSH key. + /// + /// The project key. + /// The repository slug. + /// The key identifier. + /// The permission to apply. + /// Token to cancel the operation. + /// The updated repository key. public async Task UpdateRepoKeyPermissionAsync(string projectKey, string repositorySlug, int keyId, Permissions permission, CancellationToken cancellationToken = default) { var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh/{keyId}/permissions/{BitbucketHelpers.PermissionToString(permission)}") @@ -219,6 +359,15 @@ public async Task UpdateRepoKeyPermissionAsync(string projectKey, return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves SSH keys for a user. + /// + /// Optional user slug. If null, retrieves keys for the current user. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of SSH keys. public async Task> GetUserKeysAsync(string? userSlug = null, int? maxPages = null, int? limit = null, @@ -241,6 +390,13 @@ await GetSshUrl("/keys") .ConfigureAwait(false); } + /// + /// Creates an SSH key for a user. + /// + /// The public key text. + /// Optional user slug. If null, applies to the current user. + /// Token to cancel the operation. + /// The created SSH key. public async Task CreateUserKeyAsync(string keyText, string? userSlug = null, CancellationToken cancellationToken = default) { var response = await GetSshUrl("/keys") @@ -251,6 +407,12 @@ public async Task CreateUserKeyAsync(string keyText, string? userSlug = nul return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes all SSH keys for a user. + /// + /// Optional user slug. If null, deletes keys for the current user. + /// Token to cancel the operation. + /// true if keys were deleted; otherwise, false. public async Task DeleteUserKeysAsync(string? userSlug = null, CancellationToken cancellationToken = default) { var response = await GetSshUrl("/keys") @@ -261,6 +423,12 @@ public async Task DeleteUserKeysAsync(string? userSlug = null, Cancellatio return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a specific SSH key for the current user. + /// + /// The key identifier. + /// Token to cancel the operation. + /// true if the key was deleted; otherwise, false. public async Task DeleteUserKeyAsync(int keyId, CancellationToken cancellationToken = default) { var response = await GetSshUrl($"/keys/{keyId}") @@ -270,6 +438,11 @@ public async Task DeleteUserKeyAsync(int keyId, CancellationToken cancella return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves SSH settings. + /// + /// Token to cancel the operation. + /// The SSH settings. public async Task GetSshSettingsAsync(CancellationToken cancellationToken = default) { return await GetSshUrl("/settings") From 1fd12fbcb0cfc5b992920f490e2f9da0edf471cd Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 17:56:55 +0000 Subject: [PATCH 12/50] refactor: add xml docs to projects client --- .../Core/Projects/BitbucketClient.cs | 1055 ++++++++++++++++- 1 file changed, 1053 insertions(+), 2 deletions(-) diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs index 85216a8..204a237 100644 --- a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs @@ -283,6 +283,16 @@ public async Task UpdateProjectUserPermissionsAsync(string projectKey, str return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves users with no permissions on a project. + /// + /// The project key. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of licensed users without project permissions. public async Task> GetProjectUserPermissionsNoneAsync(string projectKey, string? filter = null, int? maxPages = null, int? limit = null, @@ -305,6 +315,16 @@ await GetProjectsUrl($"/{projectKey}/permissions/users/none") .ConfigureAwait(false); } + /// + /// Retrieves group permissions for a project. + /// + /// The project key. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of group permissions. public async Task> GetProjectGroupPermissionsAsync(string projectKey, string? filter = null, int? maxPages = null, int? limit = null, @@ -327,6 +347,13 @@ await GetProjectsUrl($"/{projectKey}/permissions/groups") .ConfigureAwait(false); } + /// + /// Removes a group's permissions from a project. + /// + /// The project key. + /// The group name. + /// Token to cancel the operation. + /// true if the group permissions were removed; otherwise, false. public async Task DeleteProjectGroupPermissionsAsync(string projectKey, string groupName, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -343,6 +370,14 @@ public async Task DeleteProjectGroupPermissionsAsync(string projectKey, st return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Updates a group's permissions for a project. + /// + /// The project key. + /// The group name. + /// The permission to grant. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. public async Task UpdateProjectGroupPermissionsAsync(string projectKey, string groupName, Permissions permission, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -360,6 +395,16 @@ public async Task UpdateProjectGroupPermissionsAsync(string projectKey, st return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves groups that currently have no permissions on a project. + /// + /// The project key. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of licensed users representing groups without permissions. public async Task> GetProjectGroupPermissionsNoneAsync(string projectKey, string? filter = null, int? maxPages = null, int? limit = null, @@ -382,6 +427,13 @@ await GetProjectsUrl($"/{projectKey}/permissions/groups/none") .ConfigureAwait(false); } + /// + /// Checks whether a permission is granted to all users for a project. + /// + /// The project key. + /// The permission to check. + /// Token to cancel the operation. + /// true if the permission is granted to all; otherwise, false. public async Task IsProjectDefaultPermissionAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) { var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") @@ -412,16 +464,39 @@ private async Task SetProjectDefaultPermissionAsync(string projectKey, Per return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Grants a permission to all users for a project. + /// + /// The project key. + /// The permission to grant. + /// Token to cancel the operation. + /// true if the permission was granted; otherwise, false. public async Task GrantProjectPermissionToAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) { return await SetProjectDefaultPermissionAsync(projectKey, permission, allow: true, cancellationToken).ConfigureAwait(false); } + /// + /// Revokes a permission from all users for a project. + /// + /// The project key. + /// The permission to revoke. + /// Token to cancel the operation. + /// true if the permission was revoked; otherwise, false. public async Task RevokeProjectPermissionFromAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) { return await SetProjectDefaultPermissionAsync(projectKey, permission, allow: false, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves repositories for a project. + /// + /// The project key. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of repositories. public async Task> GetProjectRepositoriesAsync(string projectKey, int? maxPages = null, int? limit = null, @@ -444,7 +519,7 @@ await GetProjectsUrl($"/{projectKey}/repos") } /// - /// Streams all repositories for a project as an IAsyncEnumerable. + /// Streams all repositories for a project as an . /// public IAsyncEnumerable GetProjectRepositoriesStreamAsync(string projectKey, int? maxPages = null, @@ -466,6 +541,14 @@ await GetProjectsUrl($"/{projectKey}/repos") .ConfigureAwait(false), cancellationToken); } + /// + /// Creates a repository within a project. + /// + /// The project key. + /// The repository name. + /// Optional SCM identifier (default is git). + /// Token to cancel the operation. + /// The created repository. public async Task CreateProjectRepositoryAsync(string projectKey, string repositoryName, string scmId = "git", CancellationToken cancellationToken = default) { var data = new @@ -481,6 +564,13 @@ public async Task CreateProjectRepositoryAsync(string projectKey, st return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a repository within a project. + /// + /// The project key. + /// The repository slug. + /// Token to cancel the operation. + /// The requested repository. public async Task GetProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { return await GetProjectsReposUrl(projectKey, repositorySlug) @@ -488,6 +578,16 @@ public async Task GetProjectRepositoryAsync(string projectKey, strin .ConfigureAwait(false); } + /// + /// Creates a fork of a repository, optionally targeting another project or slug. + /// + /// The source project key. + /// The source repository slug. + /// Optional target project key for the fork. + /// Optional target repository slug. + /// Optional display name for the fork. + /// Token to cancel the operation. + /// The created repository fork. public async Task CreateProjectRepositoryForkAsync(string projectKey, string repositorySlug, string? targetProjectKey = null, string? targetSlug = null, string? targetName = null, CancellationToken cancellationToken = default) { var data = new @@ -504,6 +604,13 @@ public async Task CreateProjectRepositoryForkAsync(string projec return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Schedules a repository for deletion. + /// + /// The project key. + /// The repository slug. + /// Token to cancel the operation. + /// true if the repository was scheduled for deletion; otherwise, false. public async Task ScheduleProjectRepositoryForDeletionAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug) @@ -513,6 +620,17 @@ public async Task ScheduleProjectRepositoryForDeletionAsync(string project return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Updates repository metadata such as name, forkability, project, or visibility. + /// + /// The project key. + /// The repository slug. + /// Optional new repository name. + /// Optional forkable flag. + /// Optional target project key. + /// Optional public visibility flag. + /// Token to cancel the operation. + /// The updated repository. public async Task UpdateProjectRepositoryAsync(string projectKey, string repositorySlug, string? targetName = null, bool? isForkable = null, @@ -535,6 +653,16 @@ public async Task UpdateProjectRepositoryAsync(string projectKey, st return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves forks of a repository. + /// + /// The project key. + /// The repository slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of repository forks. public async Task> GetProjectRepositoryForksAsync(string projectKey, string repositorySlug, int? maxPages = null, int? limit = null, @@ -556,6 +684,13 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/forks") .ConfigureAwait(false); } + /// + /// Recreates a repository in place. + /// + /// The project key. + /// The repository slug. + /// Token to cancel the operation. + /// The recreated repository. public async Task RecreateProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/recreate") @@ -565,6 +700,16 @@ public async Task RecreateProjectRepositoryAsync(string projectKey, return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves repositories related to the specified repository (e.g., forks). + /// + /// The project key. + /// The repository slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of related repository forks. public async Task> GetRelatedProjectRepositoriesAsync(string projectKey, string repositorySlug, int? maxPages = null, int? limit = null, @@ -586,6 +731,18 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/related") .ConfigureAwait(false); } + /// + /// Retrieves an archive (zip/tar) of a repository at a specific ref. + /// + /// The project key. + /// The repository slug. + /// The ref (commit/branch/tag) to archive. + /// The archive file name. + /// The archive format. + /// Optional path filter. + /// Optional archive prefix. + /// Token to cancel the operation. + /// Archive bytes. public async Task GetProjectRepositoryArchiveAsync(string projectKey, string repositorySlug, string at, string fileName, @@ -610,6 +767,17 @@ public async Task GetProjectRepositoryArchiveAsync(string projectKey, st .ConfigureAwait(false); } + /// + /// Retrieves group permissions for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of group permissions. public async Task> GetProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, string? filter = null, int? maxPages = null, @@ -633,6 +801,15 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") .ConfigureAwait(false); } + /// + /// Updates a group's permissions for a repository. + /// + /// The project key. + /// The repository slug. + /// The permission to grant. + /// The group name. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. public async Task UpdateProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -650,6 +827,14 @@ public async Task UpdateProjectRepositoryGroupPermissionsAsync(string proj return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Removes a group's permissions from a repository. + /// + /// The project key. + /// The repository slug. + /// The group name. + /// Token to cancel the operation. + /// true if the permissions were removed; otherwise, false. public async Task DeleteProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, string name, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") @@ -660,6 +845,17 @@ public async Task DeleteProjectRepositoryGroupPermissionsAsync(string proj return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves groups or users without permissions on a repository. + /// + /// The project key. + /// The repository slug. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of removable group or user entries. public async Task> GetProjectRepositoryGroupPermissionsNoneAsync(string projectKey, string repositorySlug, string? filter = null, int? maxPages = null, @@ -683,6 +879,18 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups/none" .ConfigureAwait(false); } + /// + /// Retrieves user permissions for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// A collection of user permissions. public async Task> GetProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, string? filter = null, int? maxPages = null, @@ -708,6 +916,15 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") .ConfigureAwait(false); } + /// + /// Updates a user's permissions for a repository. + /// + /// The project key. + /// The repository slug. + /// The permission to grant. + /// The user name. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. public async Task UpdateProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -725,6 +942,15 @@ public async Task UpdateProjectRepositoryUserPermissionsAsync(string proje return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Removes a user's permissions from a repository. + /// + /// The project key. + /// The repository slug. + /// The user name. + /// Optional avatar size. + /// Token to cancel the operation. + /// true if the permissions were removed; otherwise, false. public async Task DeleteProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, string name, int? avatarSize = null, CancellationToken cancellationToken = default) @@ -738,6 +964,17 @@ public async Task DeleteProjectRepositoryUserPermissionsAsync(string proje return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves users who have no permissions on a repository. + /// + /// The project key. + /// The repository slug. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of users without repository permissions. public async Task> GetProjectRepositoryUserPermissionsNoneAsync(string projectKey, string repositorySlug, string? filter = null, int? maxPages = null, @@ -761,6 +998,20 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users/none") .ConfigureAwait(false); } + /// + /// Retrieves branches for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional base branch or tag filter. + /// Whether to include additional details. + /// Optional branch name filter. + /// Optional branch ordering. + /// Cancellation token. + /// A collection of branches. public async Task> GetBranchesAsync(string projectKey, string repositorySlug, int? maxPages = null, int? limit = null, @@ -821,6 +1072,14 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") .ConfigureAwait(false), cancellationToken); } + /// + /// Creates a branch in a repository. + /// + /// The project key. + /// The repository slug. + /// The branch information. + /// Cancellation token. + /// The created branch. public async Task CreateBranchAsync(string projectKey, string repositorySlug, BranchInfo branchInfo, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") @@ -830,6 +1089,13 @@ public async Task CreateBranchAsync(string projectKey, string repository return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves the default branch for a repository. + /// + /// The project key. + /// The repository slug. + /// Cancellation token. + /// The default branch. public async Task GetDefaultBranchAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { return await GetProjectsReposUrl(projectKey, repositorySlug, "/branches/default") @@ -837,6 +1103,14 @@ public async Task GetDefaultBranchAsync(string projectKey, string reposi .ConfigureAwait(false); } + /// + /// Sets the default branch for a repository. + /// + /// The project key. + /// The repository slug. + /// The target branch reference. + /// Cancellation token. + /// true if the default branch was updated; otherwise, false. public async Task SetDefaultBranchAsync(string projectKey, string repositorySlug, BranchRef branchRef, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") @@ -846,6 +1120,17 @@ public async Task SetDefaultBranchAsync(string projectKey, string reposito return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Browses repository content at a specific ref. + /// + /// The project key. + /// The repository slug. + /// The ref (branch, tag, or commit). + /// Whether to include type information. + /// Whether to include blame metadata. + /// If true and blame is requested, omit file content. + /// Cancellation token. + /// The browsed item metadata. public async Task BrowseProjectRepositoryAsync(string projectKey, string repositorySlug, string at, bool type = false, bool blame = false, bool noContent = false, @@ -872,6 +1157,18 @@ public async Task BrowseProjectRepositoryAsync(string projectKey, st .ConfigureAwait(false); } + /// + /// Browses a specific path within a repository at a given ref. + /// + /// The project key. + /// The repository slug. + /// The path to browse. + /// The ref (branch, tag, or commit). + /// Whether to include type information. + /// Whether to include blame metadata. + /// If true and blame is requested, omit file content. + /// Cancellation token. + /// The browsed path item metadata. public async Task BrowseProjectRepositoryPathAsync(string projectKey, string repositorySlug, string path, string at, bool type = false, bool blame = false, bool noContent = false, @@ -1009,6 +1306,18 @@ public async Task UpdateProjectRepositoryPathAsync(string projectKey, st } } + /// + /// Retrieves changes for a repository between two refs. + /// + /// The project key. + /// The repository slug. + /// The target ref. + /// Optional starting ref. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of changes. public async Task> GetChangesAsync(string projectKey, string repositorySlug, string until, string? since = null, int? maxPages = null, int? limit = null, @@ -1032,6 +1341,23 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") .ConfigureAwait(false); } + /// + /// Retrieves commits for a repository. + /// + /// The project key. + /// The repository slug. + /// The ref to retrieve commits until. + /// Whether to follow renames. + /// Whether to ignore missing commits. + /// Merge commit inclusion policy. + /// Optional path filter. + /// Optional starting ref. + /// Whether to include commit counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of commits. public async Task> GetCommitsAsync(string projectKey, string repositorySlug, string until, bool followRenames = false, @@ -1104,6 +1430,15 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") .ConfigureAwait(false), cancellationToken); } + /// + /// Retrieves a commit by ID. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Optional path filter. + /// Cancellation token. + /// The requested commit. public async Task GetCommitAsync(string projectKey, string repositorySlug, string commitId, string? path = null, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -1118,6 +1453,19 @@ public async Task GetCommitAsync(string projectKey, string repositorySlu .ConfigureAwait(false); } + /// + /// Retrieves the list of file changes for a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Optional starting commit ID. + /// Whether to include comment counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of changes. public async Task> GetCommitChangesAsync(string projectKey, string repositorySlug, string commitId, string? since = null, bool withComments = true, @@ -1143,6 +1491,19 @@ await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/chan .ConfigureAwait(false); } + /// + /// Retrieves comments for a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The file path within the commit. + /// Optional starting comment ID. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of comments. public async Task> GetCommitCommentsAsync(string projectKey, string repositorySlug, string commitId, string path, string? since = null, @@ -1168,6 +1529,16 @@ await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comm .ConfigureAwait(false); } + /// + /// Creates a comment on a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The comment payload. + /// Optional starting comment ID for context. + /// Cancellation token. + /// The created comment reference. public async Task CreateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, CommentInfo commentInfo, string? since = null, CancellationToken cancellationToken = default) { @@ -1185,6 +1556,16 @@ public async Task CreateCommitCommentAsync(string projectKey, string return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a specific commit comment by ID. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The comment ID. + /// Optional avatar size. + /// Cancellation token. + /// The requested comment reference. public async Task GetCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, int? avatarSize = null, CancellationToken cancellationToken = default) @@ -1195,6 +1576,16 @@ public async Task GetCommitCommentAsync(string projectKey, string re .ConfigureAwait(false); } + /// + /// Updates the text of a commit comment. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The comment ID. + /// The updated comment text. + /// Cancellation token. + /// The updated comment reference. public async Task UpdateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, CommentText commentText, CancellationToken cancellationToken = default) { @@ -1205,6 +1596,16 @@ public async Task UpdateCommitCommentAsync(string projectKey, string return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a commit comment. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The comment ID. + /// Optional comment version for concurrency. + /// Cancellation token. + /// true if deleted; otherwise, false. public async Task DeleteCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, int version = -1, CancellationToken cancellationToken = default) @@ -1223,6 +1624,20 @@ public async Task DeleteCommitCommentAsync(string projectKey, string repos return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a diff for a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Whether to auto-detect source path. + /// Context lines to include. + /// Optional since commit. + /// Optional source path filter. + /// Whitespace handling strategy. + /// Whether to include comments. + /// Cancellation token. + /// The diff result. public async Task GetCommitDiffAsync(string projectKey, string repositorySlug, string commitId, bool autoSrcPath = false, int contextLines = -1, @@ -1298,6 +1713,14 @@ public async IAsyncEnumerable GetCommitDiffStreamAsync(string projectKey, } } + /// + /// Starts watching a commit for notifications. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Cancellation token. + /// true if watch was created; otherwise, false. public async Task CreateCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") @@ -1307,6 +1730,14 @@ public async Task CreateCommitWatchAsync(string projectKey, string reposit return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Stops watching a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Cancellation token. + /// true if the watch was removed; otherwise, false. public async Task DeleteCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") @@ -1316,6 +1747,19 @@ public async Task DeleteCommitWatchAsync(string projectKey, string reposit return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Compares two refs and returns the list of changes. + /// + /// The project key. + /// The repository slug. + /// The source ref. + /// The target ref. + /// Optional source repository key for cross-repo compare. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of changes between the refs. public async Task> GetRepositoryCompareChangesAsync(string projectKey, string repositorySlug, string from, string to, string? fromRepo = null, int? maxPages = null, @@ -1341,6 +1785,19 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/changes") .ConfigureAwait(false); } + /// + /// Compares two refs and returns a diff. + /// + /// The project key. + /// The repository slug. + /// The source ref. + /// The target ref. + /// Optional source repository key for cross-repo compare. + /// Optional source path filter. + /// Number of context lines. + /// Whitespace handling strategy. + /// Cancellation token. + /// The diff between the refs. public async Task GetRepositoryCompareDiffAsync(string projectKey, string repositorySlug, string from, string to, string? fromRepo = null, string? srcPath = null, @@ -1411,6 +1868,19 @@ public async IAsyncEnumerable GetRepositoryCompareDiffStreamAsync(string p } } + /// + /// Compares two refs and returns the commits between them. + /// + /// The project key. + /// The repository slug. + /// The source ref. + /// The target ref. + /// Optional source repository key for cross-repo compare. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of commits between the refs. public async Task> GetRepositoryCompareCommitsAsync(string projectKey, string repositorySlug, string from, string to, string? fromRepo = null, int? maxPages = null, @@ -1436,6 +1906,18 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/commits") .ConfigureAwait(false); } + /// + /// Retrieves a repository diff between two commits. + /// + /// The project key. + /// The repository slug. + /// The target commit ID. + /// Number of context lines. + /// Optional starting commit ID. + /// Optional source path filter. + /// Whitespace handling strategy. + /// Cancellation token. + /// The diff result. public async Task GetRepositoryDiffAsync(string projectKey, string repositorySlug, string until, int contextLines = -1, string? since = null, @@ -1503,6 +1985,17 @@ public async IAsyncEnumerable GetRepositoryDiffStreamAsync(string projectK } } + /// + /// Retrieves file paths in a repository at the specified ref. + /// + /// The project key. + /// The repository slug. + /// Optional ref (branch, tag, commit). + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of file paths. public async Task> GetRepositoryFilesAsync(string projectKey, string repositorySlug, string? at = null, int? maxPages = null, int? limit = null, @@ -1525,6 +2018,14 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/files") .ConfigureAwait(false); } + /// + /// Retrieves last-modified metadata for a repository at a ref. + /// + /// The project key. + /// The repository slug. + /// The ref (branch, tag, or commit). + /// Cancellation token. + /// Last modified information. public async Task GetProjectRepositoryLastModifiedAsync(string projectKey, string repositorySlug, string at, CancellationToken cancellationToken = default) { return await GetProjectsReposUrl(projectKey, repositorySlug, "/last-modified") @@ -1533,6 +2034,19 @@ public async Task GetProjectRepositoryLastModifiedAsync(string pro .ConfigureAwait(false); } + /// + /// Retrieves participants related to pull requests in a repository. + /// + /// The project key. + /// The repository slug. + /// Direction of pull requests to consider. + /// Optional filter string. + /// Optional role filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of identities. public async Task> GetRepositoryParticipantsAsync(string projectKey, string repositorySlug, PullRequestDirections direction = PullRequestDirections.Incoming, string? filter = null, @@ -1560,6 +2074,22 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/participants") .ConfigureAwait(false); } + /// + /// Retrieves pull requests for a repository with optional filtering. + /// + /// The project key. + /// The repository slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Pull request direction filter. + /// Optional branch filter. + /// Pull request state. + /// Ordering option. + /// Whether to include attributes. + /// Whether to include properties. + /// Cancellation token. + /// A collection of pull requests. public async Task> GetPullRequestsAsync(string projectKey, string repositorySlug, int? maxPages = null, int? limit = null, @@ -1628,6 +2158,14 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") .ConfigureAwait(false), cancellationToken); } + /// + /// Creates a pull request in a repository. + /// + /// The project key. + /// The repository slug. + /// The pull request payload. + /// Cancellation token. + /// The created pull request. public async Task CreatePullRequestAsync(string projectKey, string repositorySlug, PullRequestInfo pullRequestInfo, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") @@ -1637,6 +2175,14 @@ public async Task CreatePullRequestAsync(string projectKey, string return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a pull request by ID. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The requested pull request. public async Task GetPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { return await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") @@ -1644,6 +2190,15 @@ public async Task GetPullRequestAsync(string projectKey, string rep .ConfigureAwait(false); } + /// + /// Updates a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The update payload. + /// Cancellation token. + /// The updated pull request. public async Task UpdatePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, PullRequestUpdate pullRequestUpdate, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") @@ -1653,6 +2208,15 @@ public async Task UpdatePullRequestAsync(string projectKey, string return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Version info for optimistic concurrency. + /// Cancellation token. + /// true if the pull request was deleted; otherwise, false. public async Task DeletePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, VersionInfo versionInfo, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") @@ -1662,6 +2226,20 @@ public async Task DeletePullRequestAsync(string projectKey, string reposit return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves activities for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional starting activity ID. + /// Optional activity type filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of pull request activities. public async Task> GetPullRequestActivitiesAsync(string projectKey, string repositorySlug, long pullRequestId, long? fromId = null, PullRequestFromTypes? fromType = null, @@ -1689,6 +2267,15 @@ await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequ .ConfigureAwait(false); } + /// + /// Declines a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// true if the pull request was declined; otherwise, false. public async Task DeclinePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -1705,6 +2292,15 @@ public async Task DeclinePullRequestAsync(string projectKey, string reposi return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves the merge state for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// The merge state. public async Task GetPullRequestMergeStateAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -1749,6 +2345,15 @@ public async Task GetPullRequestMergeStateAsync(string pr return await response.GetJsonAsync().ConfigureAwait(false); } + /// + /// Merges a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// The merged pull request. public async Task MergePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -1765,6 +2370,15 @@ public async Task MergePullRequestAsync(string projectKey, string r return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Reopens a declined pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// The reopened pull request. public async Task ReopenPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -1781,6 +2395,14 @@ public async Task ReopenPullRequestAsync(string projectKey, string return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Approves a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The reviewer entry reflecting the approval. public async Task ApprovePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") @@ -1790,6 +2412,14 @@ public async Task ApprovePullRequestAsync(string projectKey, string re return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Removes an approval from a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The reviewer entry after removal. public async Task DeletePullRequestApprovalAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") @@ -1799,6 +2429,21 @@ public async Task DeletePullRequestApprovalAsync(string projectKey, st return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves changes for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Scope of changes to include. + /// Optional since commit ID. + /// Optional until commit ID. + /// Whether to include comment counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of changes. public async Task> GetPullRequestChangesAsync(string projectKey, string repositorySlug, long pullRequestId, ChangeScopes changeScope = ChangeScopes.All, string? sinceId = null, @@ -1828,6 +2473,24 @@ await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequ .ConfigureAwait(false); } + /// + /// Creates a comment on a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The comment text. + /// Optional parent comment ID to create a reply. + /// Optional diff type. + /// Optional from commit hash for anchoring. + /// Optional file path for anchoring. + /// Optional source path for move/rename anchors. + /// Optional to commit hash for anchoring. + /// Optional line number for anchoring. + /// Optional file type for anchoring. + /// Optional line type for anchoring. + /// Cancellation token. + /// The created comment reference. public async Task CreatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, string text, string? parentId = null, @@ -1889,6 +2552,23 @@ public async Task CreatePullRequestCommentAsync(string projectKey, s return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves comments for a pull request path. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The file path to filter comments. + /// Anchor state filter. + /// Diff type filter. + /// Optional from commit hash. + /// Optional to commit hash. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of pull request comments. public async Task> GetPullRequestCommentsAsync(string projectKey, string repositorySlug, long pullRequestId, string path, AnchorStates anchorState = AnchorStates.Active, @@ -1922,6 +2602,16 @@ await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequ .ConfigureAwait(false); } + /// + /// Retrieves a single pull request comment by ID. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The comment ID. + /// Optional avatar size. + /// Cancellation token. + /// The requested comment reference. public async Task GetPullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, int? avatarSize = null, CancellationToken cancellationToken = default) @@ -1933,6 +2623,17 @@ public async Task GetPullRequestCommentAsync(string projectKey, stri .ConfigureAwait(false); } + /// + /// Updates a pull request comment. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The comment ID. + /// The comment version for optimistic concurrency. + /// The updated comment text. + /// Cancellation token. + /// The updated comment reference. public async Task UpdatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, int version, string text, CancellationToken cancellationToken = default) { @@ -1951,6 +2652,16 @@ public async Task UpdatePullRequestCommentAsync(string projectKey, s return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a pull request comment. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The comment ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// true if the comment was deleted; otherwise, false. public async Task DeletePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, int version = -1, CancellationToken cancellationToken = default) @@ -1964,6 +2675,18 @@ public async Task DeletePullRequestCommentAsync(string projectKey, string return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves commits associated with a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Whether to include change counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of commits. public async Task> GetPullRequestCommitsAsync(string projectKey, string repositorySlug, long pullRequestId, bool withCounts = false, int? maxPages = null, @@ -2012,6 +2735,21 @@ await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequ .ConfigureAwait(false), cancellationToken); } + /// + /// Retrieves the diff for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Number of context lines to include. + /// Diff type. + /// Optional since commit ID. + /// Optional source path filter. + /// Optional until commit ID. + /// Whitespace handling option. + /// Whether to include comments. + /// Cancellation token. + /// Differences for the pull request. public async Task GetPullRequestDiffAsync(string projectKey, string repositorySlug, long pullRequestId, int contextLines = -1, DiffTypes diffType = DiffTypes.Effective, @@ -2031,6 +2769,21 @@ public async Task GetPullRequestDiffAsync(string projectKey, string .ConfigureAwait(false); } + /// + /// Streams diff entries for a pull request as an . + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Number of context lines to include. + /// Diff type. + /// Optional since commit ID. + /// Optional source path filter. + /// Optional until commit ID. + /// Whitespace handling option. + /// Whether to include comments. + /// Cancellation token. + /// A stream of diff entries. public async IAsyncEnumerable GetPullRequestDiffStreamAsync(string projectKey, string repositorySlug, long pullRequestId, int contextLines = -1, DiffTypes diffType = DiffTypes.Effective, @@ -2057,10 +2810,26 @@ public async IAsyncEnumerable GetPullRequestDiffStreamAsync(string project } finally { - await responseStream.DisposeAsync(); + await responseStream.DisposeAsync().ConfigureAwait(false); } } + /// + /// Retrieves the diff for a specific path within a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The file path to filter by. + /// Number of context lines to include. + /// Diff type. + /// Optional since commit ID. + /// Optional source path filter. + /// Optional until commit ID. + /// Whitespace handling option. + /// Whether to include comments. + /// Cancellation token. + /// Differences for the specified path. public async Task GetPullRequestDiffPathAsync(string projectKey, string repositorySlug, long pullRequestId, string path, int contextLines = -1, @@ -2136,6 +2905,18 @@ private static async IAsyncEnumerable DeserializeDiffsFromStreamAsync(Stre // Note: MoveToDiffArrayAsync is no longer needed with System.Text.Json approach + /// + /// Retrieves participants for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of participants. public async Task> GetPullRequestParticipantsAsync(string projectKey, string repositorySlug, long pullRequestId, int? maxPages = null, int? limit = null, @@ -2160,6 +2941,16 @@ await GetProjectsReposUrl(projectKey, repositorySlug) .ConfigureAwait(false); } + /// + /// Assigns a role to a user in a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The user to assign. + /// The role to assign. + /// Cancellation token. + /// The created participant entry. public async Task AssignUserRoleToPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, Named named, Roles role, @@ -2179,6 +2970,15 @@ public async Task AssignUserRoleToPullRequestAsync(string projectKe return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a participant from a pull request by username. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The username to remove. + /// Cancellation token. + /// true if removal succeeded; otherwise, false. public async Task DeletePullRequestParticipantAsync(string projectKey, string repositorySlug, long pullRequestId, string userName, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug) @@ -2190,6 +2990,18 @@ public async Task DeletePullRequestParticipantAsync(string projectKey, str return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Updates a participant's approval status on a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The user slug to update. + /// The user identity. + /// Whether the participant approves the PR. + /// The participant status. + /// Cancellation token. + /// The updated participant entry. public async Task UpdatePullRequestParticipantStatus(string projectKey, string repositorySlug, long pullRequestId, string userSlug, Named named, @@ -2212,6 +3024,15 @@ public async Task UpdatePullRequestParticipantStatus(string project return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Removes a participant from a pull request by user slug. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The user slug to remove. + /// Cancellation token. + /// true if removal succeeded; otherwise, false. public async Task UnassignUserFromPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, string userSlug, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug) @@ -2584,6 +3405,14 @@ public async Task> GetPullRequestTasksWithFallbackAsync( #endregion + /// + /// Subscribes the current user to watch a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// true if the watch was added; otherwise, false. public async Task WatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug) @@ -2594,6 +3423,14 @@ public async Task WatchPullRequestAsync(string projectKey, string reposito return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Unsubscribes the current user from watching a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// true if the watch was removed; otherwise, false. public async Task UnwatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug) @@ -2604,6 +3441,18 @@ public async Task UnwatchPullRequestAsync(string projectKey, string reposi return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves raw content from a file path in a repository. + /// + /// The project key. + /// The repository slug. + /// The file path to fetch. + /// Optional ref (branch, tag, commit). + /// Whether to render markup. + /// Whether to hard wrap the output. + /// Whether to HTML-escape the output. + /// Cancellation token. + /// A stream containing the raw content. public async Task RetrieveRawContentAsync(string projectKey, string repositorySlug, string path, string? at = null, bool markup = false, @@ -2627,6 +3476,13 @@ public async Task RetrieveRawContentAsync(string projectKey, string repo .ConfigureAwait(false); } + /// + /// Retrieves pull request settings for a repository. + /// + /// The project key. + /// The repository slug. + /// Cancellation token. + /// The pull request settings. public async Task GetProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { return await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") @@ -2634,6 +3490,14 @@ public async Task GetProjectRepositoryPullRequestSettingsAs .ConfigureAwait(false); } + /// + /// Updates pull request settings for a repository. + /// + /// The project key. + /// The repository slug. + /// The settings payload. + /// Cancellation token. + /// The updated pull request settings. public async Task UpdateProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, PullRequestSettings pullRequestSettings, CancellationToken cancellationToken = default) { @@ -2644,6 +3508,17 @@ public async Task UpdateProjectRepositoryPullRequestSetting return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves repository hooks. + /// + /// The project key. + /// The repository slug. + /// Optional hook type filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of hooks. public async Task> GetProjectRepositoryHooksSettingsAsync(string projectKey, string repositorySlug, HookTypes? hookType = null, int? maxPages = null, @@ -2667,6 +3542,14 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/hooks") .ConfigureAwait(false); } + /// + /// Retrieves a specific hook's settings. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Cancellation token. + /// The hook configuration. public async Task GetProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) { return await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") @@ -2674,6 +3557,14 @@ public async Task GetProjectRepositoryHookSettingsAsync(string projectKey, .ConfigureAwait(false); } + /// + /// Deletes a repository hook. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Cancellation token. + /// true if deletion succeeded; otherwise, false. public async Task DeleteProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") @@ -2683,6 +3574,15 @@ public async Task DeleteProjectRepositoryHookSettingsAsync(string projectK return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Enables a repository hook, optionally providing settings. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Optional hook settings. + /// Cancellation token. + /// The enabled hook. public async Task EnableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, object? hookSettings = null, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") @@ -2692,6 +3592,14 @@ public async Task EnableProjectRepositoryHookAsync(string projectKey, stri return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Disables a repository hook. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Cancellation token. + /// The disabled hook. public async Task DisableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") @@ -2701,6 +3609,14 @@ public async Task DisableProjectRepositoryHookAsync(string projectKey, str return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves all settings for a repository hook. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Cancellation token. + /// A dictionary of hook settings. public async Task> GetProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) { return await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") @@ -2708,6 +3624,15 @@ public async Task DisableProjectRepositoryHookAsync(string projectKey, str .ConfigureAwait(false); } + /// + /// Updates all settings for a repository hook. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// The settings payload. + /// Cancellation token. + /// The updated settings. public async Task> UpdateProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, Dictionary allSettings, CancellationToken cancellationToken = default) { @@ -2718,6 +3643,13 @@ public async Task DisableProjectRepositoryHookAsync(string projectKey, str return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves merge strategies for pull requests within a project SCM. + /// + /// The project key. + /// The SCM identifier. + /// Cancellation token. + /// The pull request settings. public async Task GetProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, CancellationToken cancellationToken = default) { return await GetProjectUrl(projectKey) @@ -2726,6 +3658,14 @@ public async Task GetProjectPullRequestsMergeStrategiesAsyn .ConfigureAwait(false); } + /// + /// Updates merge strategies for pull requests within a project SCM. + /// + /// The project key. + /// The SCM identifier. + /// The merge strategies payload. + /// Cancellation token. + /// The updated merge strategies. public async Task UpdateProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, MergeStrategies mergeStrategies, CancellationToken cancellationToken = default) { var response = await GetProjectUrl(projectKey) @@ -2736,6 +3676,18 @@ public async Task UpdateProjectPullRequestsMergeStrategiesAsync return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves tags from a repository. + /// + /// The project key. + /// The repository slug. + /// Filter text for tag names. + /// Ordering option. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of tags. public async Task> GetProjectRepositoryTagsAsync(string projectKey, string repositorySlug, string filterText, BranchOrderBy orderBy, @@ -2761,6 +3713,16 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") .ConfigureAwait(false); } + /// + /// Creates a tag in a repository. + /// + /// The project key. + /// The repository slug. + /// The tag name. + /// The starting commit or ref. + /// The tag message. + /// Cancellation token. + /// The created tag. public async Task CreateProjectRepositoryTagAsync(string projectKey, string repositorySlug, string name, string startPoint, @@ -2781,6 +3743,14 @@ public async Task CreateProjectRepositoryTagAsync(string projectKey, string return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a tag by name. + /// + /// The project key. + /// The repository slug. + /// The tag name. + /// Cancellation token. + /// The requested tag. public async Task GetProjectRepositoryTagAsync(string projectKey, string repositorySlug, string tagName, CancellationToken cancellationToken = default) { return await GetProjectsReposUrl(projectKey, repositorySlug, $"/tags/{tagName}") @@ -2788,6 +3758,18 @@ public async Task GetProjectRepositoryTagAsync(string projectKey, string re .ConfigureAwait(false); } + /// + /// Retrieves webhooks for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional event filter. + /// Whether to include statistics. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of webhooks. public async Task> GetProjectRepositoryWebHooksAsync(string projectKey, string repositorySlug, string? @event = null, bool statistics = false, @@ -2813,6 +3795,14 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") .ConfigureAwait(false); } + /// + /// Creates a webhook for a repository. + /// + /// The project key. + /// The repository slug. + /// The webhook payload. + /// Cancellation token. + /// The created webhook. public async Task CreateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, WebHook webHook, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") @@ -2822,6 +3812,14 @@ public async Task CreateProjectRepositoryWebHookAsync(string projectKey return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Tests a webhook delivery for a repository. + /// + /// The project key. + /// The repository slug. + /// The URL to test. + /// Cancellation token. + /// The webhook test response. public async Task TestProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, string url, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks/test") @@ -2832,6 +3830,15 @@ public async Task TestProjectRepositoryWebHookAsync( return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves a webhook by ID. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Whether to include statistics. + /// Cancellation token. + /// The webhook. public async Task GetProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, string webHookId, bool statistics = false, @@ -2849,6 +3856,15 @@ public async Task GetProjectRepositoryWebHookAsync(string projectKey, s .ConfigureAwait(false); } + /// + /// Updates a webhook. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// The webhook payload. + /// Cancellation token. + /// The updated webhook. public async Task UpdateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, string webHookId, WebHook webHook, CancellationToken cancellationToken = default) { @@ -2859,6 +3875,14 @@ public async Task UpdateProjectRepositoryWebHookAsync(string projectKey return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a webhook. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Cancellation token. + /// true if deletion succeeded; otherwise, false. public async Task DeleteProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, string webHookId, CancellationToken cancellationToken = default) { @@ -2870,6 +3894,16 @@ public async Task DeleteProjectRepositoryWebHookAsync(string projectKey, s } //public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, + /// + /// Retrieves the latest webhook invocation summary as a string. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Optional event filter. + /// Optional outcome filter. + /// Cancellation token. + /// The latest invocation payload. public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, string webHookId, string? @event = null, @@ -2890,6 +3924,15 @@ public async Task GetProjectRepositoryWebHookLatestAsync(string projectK .ConfigureAwait(false); } + /// + /// Retrieves webhook statistics. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Optional event filter. + /// Cancellation token. + /// Webhook statistics. public async Task GetProjectRepositoryWebHookStatisticsAsync(string projectKey, string repositorySlug, string webHookId, string? @event = null, @@ -2901,6 +3944,14 @@ public async Task GetProjectRepositoryWebHookStatisticsAsync( .ConfigureAwait(false); } + /// + /// Retrieves a statistics summary for a webhook. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Cancellation token. + /// A dictionary of webhook statistics counts. public async Task> GetProjectRepositoryWebHookStatisticsSummaryAsync(string projectKey, string repositorySlug, string webHookId, CancellationToken cancellationToken = default) { From 3d07929dc08f551002063b3df939dbc1f43c5154 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:01:05 +0000 Subject: [PATCH 13/50] refactor: enhance documentation for BitbucketClient methods --- .../Core/Admin/BitbucketClient.cs | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) diff --git a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs index 8b97b4e..7695bd8 100644 --- a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs @@ -11,6 +11,9 @@ namespace Bitbucket.Net; +/// +/// Provides administrative operations for Bitbucket Server, including user, group, permissions, license, and mail server management. +/// public partial class BitbucketClient { private IFlurlRequest GetAdminUrl() => GetBaseUrl() @@ -19,6 +22,15 @@ private IFlurlRequest GetAdminUrl() => GetBaseUrl() private IFlurlRequest GetAdminUrl(string path) => GetAdminUrl() .AppendPathSegment(path); + /// + /// Retrieves groups with optional filtering. + /// + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of groups. public async Task> GetAdminGroupsAsync(string? filter = null, int? maxPages = null, int? limit = null, @@ -41,6 +53,12 @@ await GetAdminUrl("/groups") .ConfigureAwait(false); } + /// + /// Creates a group. + /// + /// The group name. + /// Cancellation token. + /// The created group. public async Task CreateAdminGroupAsync(string name, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/groups") @@ -51,6 +69,12 @@ public async Task CreateAdminGroupAsync(string name, Cance return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a group. + /// + /// The group name. + /// Cancellation token. + /// The deleted group info. public async Task DeleteAdminGroupAsync(string name, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/groups") @@ -61,6 +85,12 @@ public async Task DeleteAdminGroupAsync(string name, Cance return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Adds users to a group. + /// + /// The group and user payload. + /// Cancellation token. + /// true if users were added; otherwise, false. public async Task AddAdminGroupUsersAsync(GroupUsers groupUsers, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/groups/add-users") @@ -70,6 +100,17 @@ public async Task AddAdminGroupUsersAsync(GroupUsers groupUsers, Cancellat return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves members of a group beyond the initial page. + /// + /// The group context. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of group members. public async Task> GetAdminGroupMoreMembersAsync(string context, string? filter = null, int? maxPages = null, int? limit = null, @@ -95,6 +136,17 @@ await GetAdminUrl("/groups/more-members") .ConfigureAwait(false); } + /// + /// Retrieves non-members for a group beyond the initial page. + /// + /// The group context. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of non-members. public async Task> GetAdminGroupMoreNonMembersAsync(string context, string? filter = null, int? maxPages = null, int? limit = null, @@ -120,6 +172,16 @@ await GetAdminUrl("/groups/more-non-members") .ConfigureAwait(false); } + /// + /// Retrieves users with optional filtering. + /// + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of users. public async Task> GetAdminUsersAsync(string? filter = null, int? maxPages = null, int? limit = null, @@ -144,6 +206,17 @@ await GetAdminUrl("/users") .ConfigureAwait(false); } + /// + /// Creates a user. + /// + /// The username. + /// The password. + /// The display name. + /// The email address. + /// Whether to add to the default group. + /// Whether to notify the user. + /// Cancellation token. + /// true if creation succeeded; otherwise, false. public async Task CreateAdminUserAsync(string name, string password, string displayName, string emailAddress, bool addToDefaultGroup = true, string notify = "false", CancellationToken cancellationToken = default) { @@ -166,6 +239,14 @@ public async Task CreateAdminUserAsync(string name, string password, strin return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Updates user details. + /// + /// Optional username to update. + /// Optional display name. + /// Optional email address. + /// Cancellation token. + /// The updated user. public async Task UpdateAdminUserAsync(string? name = null, string? displayName = null, string? emailAddress = null, CancellationToken cancellationToken = default) { var data = new @@ -182,6 +263,12 @@ public async Task UpdateAdminUserAsync(string? name = null, string? di return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes a user. + /// + /// The username. + /// Cancellation token. + /// The deleted user info. public async Task DeleteAdminUserAsync(string name, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/users") @@ -192,6 +279,12 @@ public async Task DeleteAdminUserAsync(string name, CancellationToken return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Adds groups to a user. + /// + /// The user groups payload. + /// Cancellation token. + /// true if groups were added; otherwise, false. public async Task AddAdminUserGroupsAsync(UserGroups userGroups, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/users/add-groups") @@ -201,6 +294,12 @@ public async Task AddAdminUserGroupsAsync(UserGroups userGroups, Cancellat return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Deletes captcha for a user. + /// + /// The username. + /// Cancellation token. + /// true if deletion succeeded; otherwise, false. public async Task DeleteAdminUserCaptcha(string name, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/users/captcha") @@ -211,6 +310,12 @@ public async Task DeleteAdminUserCaptcha(string name, CancellationToken ca return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Updates user credentials. + /// + /// The password change payload. + /// Cancellation token. + /// true if update succeeded; otherwise, false. public async Task UpdateAdminUserCredentialsAsync(PasswordChange passwordChange, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/users/credentials") @@ -220,6 +325,16 @@ public async Task UpdateAdminUserCredentialsAsync(PasswordChange passwordC return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves additional groups for a user (memberships) beyond the first page. + /// + /// The username context. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of group memberships. public async Task> GetAdminUserMoreMembersAsync(string context, string? filter = null, int? maxPages = null, int? limit = null, @@ -243,6 +358,16 @@ await GetAdminUrl("/users/more-members") .ConfigureAwait(false); } + /// + /// Retrieves additional groups that a user is not a member of. + /// + /// The username context. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of non-member groups. public async Task> GetAdminUserMoreNonMembersAsync(string context, string? filter = null, int? maxPages = null, int? limit = null, @@ -266,6 +391,13 @@ await GetAdminUrl("/users/more-non-members") .ConfigureAwait(false); } + /// + /// Removes a user from a group. + /// + /// The username. + /// The group name. + /// Cancellation token. + /// true if removal succeeded; otherwise, false. public async Task RemoveAdminUserFromGroupAsync(string userName, string groupName, CancellationToken cancellationToken = default) { var data = new @@ -281,6 +413,13 @@ public async Task RemoveAdminUserFromGroupAsync(string userName, string gr return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Renames a user. + /// + /// The rename payload. + /// Optional avatar size. + /// Cancellation token. + /// The updated user info. public async Task RenameAdminUserAsync(UserRename userRename, int? avatarSize = null, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("users/rename") @@ -291,6 +430,11 @@ public async Task RenameAdminUserAsync(UserRename userRename, int? ava return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves cluster information. + /// + /// Cancellation token. + /// The cluster details. public async Task GetAdminClusterAsync(CancellationToken cancellationToken = default) { return await GetAdminUrl("/cluster") @@ -298,6 +442,11 @@ public async Task GetAdminClusterAsync(CancellationToken cancellationTo .ConfigureAwait(false); } + /// + /// Retrieves license details. + /// + /// Cancellation token. + /// The license details. public async Task GetAdminLicenseAsync(CancellationToken cancellationToken = default) { return await GetAdminUrl("/license") @@ -305,6 +454,12 @@ public async Task GetAdminLicenseAsync(CancellationToken cancell .ConfigureAwait(false); } + /// + /// Updates license information. + /// + /// The license payload. + /// Cancellation token. + /// The updated license details. public async Task UpdateAdminLicenseAsync(LicenseInfo licenseInfo, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/license") @@ -314,6 +469,11 @@ public async Task UpdateAdminLicenseAsync(LicenseInfo licenseInf return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves mail server configuration. + /// + /// Cancellation token. + /// The mail server configuration. public async Task GetAdminMailServerAsync(CancellationToken cancellationToken = default) { return await GetAdminUrl("/mail-server") @@ -321,6 +481,12 @@ public async Task GetAdminMailServerAsync(CancellationT .ConfigureAwait(false); } + /// + /// Updates mail server configuration. + /// + /// The configuration payload. + /// Cancellation token. + /// The updated configuration. public async Task UpdateAdminMailServerAsync(MailServerConfiguration mailServerConfiguration, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/mail-server") @@ -330,6 +496,11 @@ public async Task UpdateAdminMailServerAsync(MailServer return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } + /// + /// Deletes mail server configuration. + /// + /// Cancellation token. + /// true if deletion succeeded; otherwise, false. public async Task DeleteAdminMailServerAsync(CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/mail-server") @@ -339,6 +510,11 @@ public async Task DeleteAdminMailServerAsync(CancellationToken cancellatio return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves the mail server sender address. + /// + /// Cancellation token. + /// The sender address. public async Task GetAdminMailServerSenderAddressAsync(CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/mail-server/sender-address") @@ -348,6 +524,12 @@ public async Task GetAdminMailServerSenderAddressAsync(CancellationToken return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); } + /// + /// Updates the mail server sender address. + /// + /// The sender address. + /// Cancellation token. + /// The updated sender address. public async Task UpdateAdminMailServerSenderAddressAsync(string senderAddress, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/mail-server/sender-address") @@ -357,6 +539,11 @@ public async Task UpdateAdminMailServerSenderAddressAsync(string senderA return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); } + /// + /// Deletes the mail server sender address. + /// + /// Cancellation token. + /// true if deletion succeeded; otherwise, false. public async Task DeleteAdminMailServerSenderAddressAsync(CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/mail-server/sender-address") From 2e6740070d90366f8fc20e6bb5f55e86862298bf Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 18:23:22 +0000 Subject: [PATCH 14/50] refactor: enhance documentation for admin permissions methods and improve project streaming summary --- .../Core/Admin/BitbucketClient.cs | 86 +++++++++++++++++++ .../Core/Projects/BitbucketClient.cs | 6 +- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs index 7695bd8..d6fb57e 100644 --- a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs @@ -16,9 +16,18 @@ namespace Bitbucket.Net; /// public partial class BitbucketClient { + /// + /// Gets the base admin URL for Bitbucket Server operations. + /// + /// An targeting the admin endpoint. private IFlurlRequest GetAdminUrl() => GetBaseUrl() .AppendPathSegment("/admin"); + /// + /// Gets the admin URL for a specific path. + /// + /// The path to append. + /// An targeting the admin path. private IFlurlRequest GetAdminUrl(string path) => GetAdminUrl() .AppendPathSegment(path); @@ -553,6 +562,15 @@ public async Task DeleteAdminMailServerSenderAddressAsync(CancellationToke return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves admin group permissions with optional filtering. + /// + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of group permissions. public async Task> GetAdminGroupPermissionsAsync(string? filter = null, int? maxPages = null, int? limit = null, @@ -575,6 +593,13 @@ await GetAdminUrl("/permissions/groups") .ConfigureAwait(false); } + /// + /// Updates a group's permissions. + /// + /// The permission to grant. + /// The group name. + /// Cancellation token. + /// true if the update succeeded; otherwise, false. public async Task UpdateAdminGroupPermissionsAsync(Permissions permission, string name, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -592,6 +617,12 @@ public async Task UpdateAdminGroupPermissionsAsync(Permissions permission, return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Removes a group's permissions. + /// + /// The group name. + /// Cancellation token. + /// true if the permissions were removed; otherwise, false. public async Task DeleteAdminGroupPermissionsAsync(string name, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/permissions/groups") @@ -602,6 +633,15 @@ public async Task DeleteAdminGroupPermissionsAsync(string name, Cancellati return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves groups that currently have no admin permissions. + /// + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of groups without permissions. public async Task> GetAdminGroupPermissionsNoneAsync(string? filter = null, int? maxPages = null, int? limit = null, @@ -624,6 +664,16 @@ await GetAdminUrl("/permissions/groups/none") .ConfigureAwait(false); } + /// + /// Retrieves admin user permissions with optional filtering. + /// + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of user permissions. public async Task> GetAdminUserPermissionsAsync(string? filter = null, int? maxPages = null, int? limit = null, @@ -648,6 +698,13 @@ await GetAdminUrl("/permissions/users") .ConfigureAwait(false); } + /// + /// Updates a user's permissions. + /// + /// The permission to grant. + /// The username. + /// Cancellation token. + /// true if the update succeeded; otherwise, false. public async Task UpdateAdminUserPermissionsAsync(Permissions permission, string name, CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary @@ -665,6 +722,12 @@ public async Task UpdateAdminUserPermissionsAsync(Permissions permission, return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Removes a user's permissions. + /// + /// The username. + /// Cancellation token. + /// true if the permissions were removed; otherwise, false. public async Task DeleteAdminUserPermissionsAsync(string name, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/permissions/users") @@ -675,6 +738,16 @@ public async Task DeleteAdminUserPermissionsAsync(string name, Cancellatio return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); } + /// + /// Retrieves users that currently have no admin permissions. + /// + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of users without permissions. public async Task> GetAdminUserPermissionsNoneAsync(string? filter = null, int? maxPages = null, int? limit = null, @@ -699,6 +772,12 @@ await GetAdminUrl("/permissions/users/none") .ConfigureAwait(false); } + /// + /// Retrieves merge strategies for pull requests for a specific SCM. + /// + /// The SCM identifier. + /// Cancellation token. + /// The merge strategies configuration. public async Task GetAdminPullRequestsMergeStrategiesAsync(string scmId, CancellationToken cancellationToken = default) { return await GetAdminUrl($"/pull-requests/{scmId}") @@ -706,6 +785,13 @@ public async Task GetAdminPullRequestsMergeStrategiesAsync(stri .ConfigureAwait(false); } + /// + /// Updates merge strategies for pull requests for a specific SCM. + /// + /// The SCM identifier. + /// The merge strategies payload. + /// Cancellation token. + /// The updated merge strategies. public async Task UpdateAdminPullRequestsMergeStrategiesAsync(string scmId, MergeStrategies mergeStrategies, CancellationToken cancellationToken = default) { var response = await GetAdminUrl($"/pull-requests/{scmId}") diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs index 204a237..1f11ca3 100644 --- a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs @@ -102,11 +102,7 @@ await GetProjectsUrl() } /// - /// Streams all projects as an IAsyncEnumerable, yielding items as they are retrieved. - /// This is more memory-efficient for large result sets. - /// - /// - /// Streams projects accessible to the current user. + /// Streams projects accessible to the current user as they are retrieved, improving memory efficiency for large result sets. /// /// Optional maximum number of pages to retrieve. /// Optional page size. From fdf37cb729f71db7edb8e66785be28db40ed1712 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:10:30 +0000 Subject: [PATCH 15/50] refactor: streamline response handling and improve JSON serialization methods --- src/Bitbucket.Net/BitbucketClient.cs | 92 ++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/src/Bitbucket.Net/BitbucketClient.cs b/src/Bitbucket.Net/BitbucketClient.cs index e687081..cae08bf 100644 --- a/src/Bitbucket.Net/BitbucketClient.cs +++ b/src/Bitbucket.Net/BitbucketClient.cs @@ -8,9 +8,11 @@ using Flurl.Http.Configuration; using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Net.Http; using System.Runtime.CompilerServices; +using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; @@ -55,14 +57,6 @@ public partial class BitbucketClient private static readonly ISerializer s_serializer = new DefaultJsonSerializer(s_jsonOptions); - static BitbucketClient() - { - // Configure Flurl to use System.Text.Json globally - FlurlHttp.Clients.WithDefaults(builder => - builder.WithSettings(settings => - settings.JsonSerializer = s_serializer)); - } - private readonly Url _url; private readonly Func? _getToken; private readonly string? _userName; @@ -167,28 +161,69 @@ public BitbucketClient(IFlurlClient flurlClient, Func? getToken = null) /// An configured with authentication and serialization. private IFlurlRequest GetBaseUrl(string root = "/api", string version = "1.0") { + IFlurlRequest request; + // If using injected client, use it directly if (_injectedClient != null) { - var request = _injectedClient + request = _injectedClient .Request() - .AppendPathSegment($"/rest{root}/{version}") - .WithSettings(settings => settings.JsonSerializer = s_serializer); + .AppendPathSegment($"/rest{root}/{version}"); // Apply token authentication if provided if (_getToken != null) { request = request.WithOAuthBearerToken(_getToken()); } + } + else + { + // Original behavior for non-DI scenarios + var fullUrl = new Url(_url) + .AppendPathSegment($"/rest{root}/{version}"); + request = new FlurlRequest(fullUrl) + .WithAuthentication(_getToken, _userName, _password); + } + + return request + .AllowAnyHttpStatus() + .WithSettings(settings => settings.JsonSerializer = s_serializer); + } + + private static async Task ReadResponseStringAsync(IFlurlResponse response, CancellationToken cancellationToken) + { + if (response.ResponseMessage?.Content is null) + { + return string.Empty; + } + + return await response.ResponseMessage.Content.ReadAsStringAsync(cancellationToken).ConfigureAwait(false); + } + + private static StringContent CreateJsonContent(TValue value) + { + var json = JsonSerializer.Serialize(value, s_jsonOptions); + return new StringContent(json, Encoding.UTF8, "application/json"); + } - return request; + private static async Task ReadResponseBytesAsync(IFlurlResponse response, CancellationToken cancellationToken) + { + if (response.ResponseMessage?.Content is null) + { + return Array.Empty(); } - // Original behavior for non-DI scenarios - return new Url(_url) - .AppendPathSegment($"/rest{root}/{version}") - .WithSettings(settings => settings.JsonSerializer = s_serializer) - .WithAuthentication(_getToken, _userName, _password); + return await response.ResponseMessage.Content.ReadAsByteArrayAsync(cancellationToken).ConfigureAwait(false); + } + + private static async Task ReadResponseStreamAsync(IFlurlResponse response, CancellationToken cancellationToken) + { + if (response.ResponseMessage?.Content is null) + { + return Stream.Null; + } + + return await response.ResponseMessage.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false); } /// @@ -201,10 +236,19 @@ private IFlurlRequest GetBaseUrl(string root = "/api", string version = "1.0") /// The deserialized response content. private static async Task ReadResponseContentAsync(IFlurlResponse response, Func? contentHandler = null, CancellationToken cancellationToken = default) { - string content = await response.GetStringAsync().ConfigureAwait(false); - return contentHandler != null - ? contentHandler(content) - : JsonSerializer.Deserialize(content, s_jsonOptions)!; + string content = await ReadResponseStringAsync(response, cancellationToken).ConfigureAwait(false); + + if (contentHandler is not null) + { + return contentHandler(content); + } + + if (string.IsNullOrWhiteSpace(content)) + { + return default!; + } + + return JsonSerializer.Deserialize(content, s_jsonOptions)!; } /// @@ -215,8 +259,8 @@ private static async Task ReadResponseContentAsync(IFlurlRespo /// true if the response body is empty; otherwise, false. private static async Task ReadResponseContentAsync(IFlurlResponse response, CancellationToken cancellationToken = default) { - string content = await response.GetStringAsync().ConfigureAwait(false); - return content == ""; + string content = await ReadResponseStringAsync(response, cancellationToken).ConfigureAwait(false); + return string.IsNullOrWhiteSpace(content); } /// @@ -236,7 +280,7 @@ private static async Task HandleErrorsAsync(IFlurlResponse response, Cancellatio try { // Read the response body first so we can include it in the error if parsing fails - rawResponseBody = await response.GetStringAsync().ConfigureAwait(false); + rawResponseBody = await ReadResponseStringAsync(response, cancellationToken).ConfigureAwait(false); if (!string.IsNullOrWhiteSpace(rawResponseBody)) { From 5837c26021dcbe9a2c016858d94211b1d765abd6 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:11:15 +0000 Subject: [PATCH 16/50] refactor: enhance FlurlRequestExtensions with async Get method and improve MockSetupExtensions for error handling --- .../Common/FlurlRequestExtensions.cs | 8 +++ .../Infrastructure/MockSetupExtensions.cs | 68 +++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs b/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs index 49557d4..c83ac65 100644 --- a/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs +++ b/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs @@ -1,5 +1,8 @@ using Flurl.Http; using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; namespace Bitbucket.Net.Common; @@ -15,4 +18,9 @@ public static IFlurlRequest WithAuthentication(this IFlurlRequest request, Func< return request.WithBasicAuth(userName, password); } + + public static Task GetAsync(this IFlurlRequest request, CancellationToken cancellationToken) + { + return request.GetAsync(HttpCompletionOption.ResponseContentRead, cancellationToken); + } } \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs b/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs index 4412591..2fb9edb 100644 --- a/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs +++ b/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs @@ -1,5 +1,7 @@ +using Bitbucket.Net.Common.Models; using System.IO; using System.Net; +using System.Text.Json; using WireMock.Matchers; using WireMock.RequestBuilders; using WireMock.ResponseBuilders; @@ -149,6 +151,72 @@ public static WireMockServer SetupInternalServerError(this WireMockServer server return server; } + public static WireMockServer SetupBadRequest(this WireMockServer server, string path) + { + return server.SetupErrorWithJsonBody(path, HttpStatusCode.BadRequest, + new Error { Message = "Bad request" }); + } + + public static WireMockServer SetupForbidden(this WireMockServer server, string path) + { + return server.SetupErrorWithJsonBody(path, HttpStatusCode.Forbidden, + new Error { Message = "Forbidden" }); + } + + public static WireMockServer SetupConflict(this WireMockServer server, string path) + { + return server.SetupErrorWithJsonBody(path, HttpStatusCode.Conflict, + new Error { Message = "Conflict" }); + } + + public static WireMockServer SetupRateLimited(this WireMockServer server, string path) + { + return server.SetupErrorWithJsonBody(path, HttpStatusCode.TooManyRequests, + new Error { Message = "Rate limit exceeded" }); + } + + public static WireMockServer SetupErrorWithJsonBody(this WireMockServer server, string path, HttpStatusCode statusCode, params Error[] errors) + { + var json = JsonSerializer.Serialize(new { errors }, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase, + }); + + server.Given(Request.Create() + .WithPath(path) + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(statusCode) + .WithHeader("Content-Type", "application/json") + .WithBody(json)); + + return server; + } + + public static WireMockServer SetupErrorWithHtmlBody(this WireMockServer server, string path, HttpStatusCode statusCode, string htmlContent) + { + server.Given(Request.Create() + .WithPath(path) + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(statusCode) + .WithHeader("Content-Type", "text/html") + .WithBody(htmlContent)); + + return server; + } + + public static WireMockServer SetupErrorWithEmptyBody(this WireMockServer server, string path, HttpStatusCode statusCode) + { + server.Given(Request.Create() + .WithPath(path) + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(statusCode)); + + return server; + } + public static WireMockServer SetupCustomResponse( this WireMockServer server, string path, From 922ac3d5e723207a3a2fba5e4c6e1422f7c9f958 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:13:04 +0000 Subject: [PATCH 17/50] refactor: update BitbucketClient methods to use async response handling and improve error management --- src/Bitbucket.Net/Audit/BitbucketClient.cs | 21 ++- src/Bitbucket.Net/Builds/BitbucketClient.cs | 21 ++- .../CommentLikes/BitbucketClient.cs | 25 ++- .../Core/Admin/BitbucketClient.cs | 152 ++++++++++++------ .../ApplicationProperties/BitbucketClient.cs | 5 +- .../Core/Dashboard/BitbucketClient.cs | 20 ++- .../Core/Groups/BitbucketClient.cs | 11 +- .../Core/Hooks/BitbucketClient.cs | 8 +- .../Core/Inbox/BitbucketClient.cs | 12 +- .../Core/Markup/BitbucketClient.cs | 2 +- .../Core/Tasks/BitbucketClient.cs | 12 +- .../Core/Users/BitbucketClient.cs | 28 ++-- .../DefaultReviewers/BitbucketClient.cs | 9 +- .../PersonalAccessTokens/BitbucketClient.cs | 22 ++- .../RefRestrictions/BitbucketClient.cs | 41 +++-- src/Bitbucket.Net/RefSync/BitbucketClient.cs | 5 +- src/Bitbucket.Net/Ssh/BitbucketClient.cs | 62 ++++--- 17 files changed, 304 insertions(+), 152 deletions(-) diff --git a/src/Bitbucket.Net/Audit/BitbucketClient.cs b/src/Bitbucket.Net/Audit/BitbucketClient.cs index dd214c8..17a82b5 100644 --- a/src/Bitbucket.Net/Audit/BitbucketClient.cs +++ b/src/Bitbucket.Net/Audit/BitbucketClient.cs @@ -1,3 +1,4 @@ +using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Audit; using Flurl.Http; @@ -49,10 +50,14 @@ public async Task> GetProjectAuditEventsAsync(string pro }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAuditUrl($"/projects/{projectKey}/events") + { + var response = await GetAuditUrl($"/projects/{projectKey}/events") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -82,10 +87,14 @@ public async Task> GetProjectRepoAuditEventsAsync(string }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAuditUrl($"/projects/{projectKey}/repos/{repositorySlug}/events") + { + var response = await GetAuditUrl($"/projects/{projectKey}/repos/{repositorySlug}/events") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Builds/BitbucketClient.cs b/src/Bitbucket.Net/Builds/BitbucketClient.cs index 06a2f5f..39917f3 100644 --- a/src/Bitbucket.Net/Builds/BitbucketClient.cs +++ b/src/Bitbucket.Net/Builds/BitbucketClient.cs @@ -3,6 +3,7 @@ using Bitbucket.Net.Models.Builds; using Flurl.Http; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -36,10 +37,12 @@ private IFlurlRequest GetBuildsUrl(string path) => GetBuildsUrl() /// Build statistics for the commit. public async Task GetBuildStatsForCommitAsync(string commitId, bool includeUnique = false, CancellationToken cancellationToken = default) { - return await GetBuildsUrl($"/commits/stats/{commitId}") + var response = await GetBuildsUrl($"/commits/stats/{commitId}") .SetQueryParam("includeUnique", BitbucketHelpers.BoolToString(includeUnique)) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -51,7 +54,7 @@ public async Task GetBuildStatsForCommitAsync(string commitId, bool public async Task> GetBuildStatsForCommitsAsync(CancellationToken cancellationToken, params string[] commitIds) { var response = await GetBuildsUrl("/commits/stats") - .PostJsonAsync(commitIds, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(commitIds), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -90,9 +93,13 @@ public async Task> GetBuildStatusForCommitAsync(string }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetBuildsUrl($"/commits/{commitId}") - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetBuildsUrl($"/commits/{commitId}") + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -106,7 +113,7 @@ await GetBuildsUrl($"/commits/{commitId}") public async Task AssociateBuildStatusWithCommitAsync(string commitId, BuildStatus buildStatus, CancellationToken cancellationToken = default) { var response = await GetBuildsUrl($"/commits/{commitId}") - .PostJsonAsync(buildStatus, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(buildStatus), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); diff --git a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs index 74a6fc6..59908b3 100644 --- a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs +++ b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs @@ -1,3 +1,4 @@ +using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Users; using Flurl.Http; @@ -56,10 +57,14 @@ public async Task> GetCommitCommentLikesAsync(string projectKe }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") + { + var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -75,7 +80,7 @@ await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits public async Task LikeCommitCommentAsync(string projectKey, string repositorySlug, string commitId, string commentId, CancellationToken cancellationToken = default) { var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/commits/{commitId}/comments/{commentId}/likes") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -125,10 +130,14 @@ public async Task> GetPullRequestCommentLikesAsync(string proj }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") + { + var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -144,7 +153,7 @@ await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-re public async Task LikePullRequestCommentAsync(string projectKey, string repositorySlug, string pullRequestId, string commentId, CancellationToken cancellationToken = default) { var response = await GetCommentLikesUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/comments/{commentId}/likes") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); diff --git a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs index d6fb57e..3088dd3 100644 --- a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs @@ -55,10 +55,14 @@ public async Task> GetAdminGroupsAsync(string? }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/groups") + { + var response = await GetAdminUrl("/groups") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -72,7 +76,7 @@ public async Task CreateAdminGroupAsync(string name, Cance { var response = await GetAdminUrl("/groups") .SetQueryParam("name", name) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -103,7 +107,7 @@ public async Task DeleteAdminGroupAsync(string name, Cance public async Task AddAdminGroupUsersAsync(GroupUsers groupUsers, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/groups/add-users") - .PostJsonAsync(groupUsers, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(groupUsers), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -138,10 +142,14 @@ public async Task> GetAdminGroupMoreMembersAsync(string co }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/groups/more-members") + { + var response = await GetAdminUrl("/groups/more-members") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -174,10 +182,14 @@ public async Task> GetAdminGroupMoreNonMembersAsync(string }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/groups/more-non-members") + { + var response = await GetAdminUrl("/groups/more-non-members") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -208,10 +220,14 @@ public async Task> GetAdminUsersAsync(string? filter = nul }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/users") + { + var response = await GetAdminUrl("/users") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -242,7 +258,7 @@ public async Task CreateAdminUserAsync(string name, string password, strin var response = await GetAdminUrl("/users") .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -266,7 +282,7 @@ public async Task UpdateAdminUserAsync(string? name = null, string? di }; var response = await GetAdminUrl("/users") - .PutJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -297,7 +313,7 @@ public async Task DeleteAdminUserAsync(string name, CancellationToken public async Task AddAdminUserGroupsAsync(UserGroups userGroups, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/users/add-groups") - .PostJsonAsync(userGroups, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(userGroups), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -328,7 +344,7 @@ public async Task DeleteAdminUserCaptcha(string name, CancellationToken ca public async Task UpdateAdminUserCredentialsAsync(PasswordChange passwordChange, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/users/credentials") - .PutJsonAsync(passwordChange, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(passwordChange), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -360,10 +376,14 @@ public async Task> GetAdminUserMoreMembersAsyn }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/users/more-members") + { + var response = await GetAdminUrl("/users/more-members") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -393,10 +413,14 @@ public async Task> GetAdminUserMoreNonMembersA }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/users/more-non-members") + { + var response = await GetAdminUrl("/users/more-non-members") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -416,7 +440,7 @@ public async Task RemoveAdminUserFromGroupAsync(string userName, string gr }; var response = await GetAdminUrl("/users/remove-group") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -433,7 +457,7 @@ public async Task RenameAdminUserAsync(UserRename userRename, int? ava { var response = await GetAdminUrl("users/rename") .SetQueryParam("avatarSize", avatarSize) - .PostJsonAsync(userRename, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(userRename), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -446,9 +470,11 @@ public async Task RenameAdminUserAsync(UserRename userRename, int? ava /// The cluster details. public async Task GetAdminClusterAsync(CancellationToken cancellationToken = default) { - return await GetAdminUrl("/cluster") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetAdminUrl("/cluster") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -458,9 +484,11 @@ public async Task GetAdminClusterAsync(CancellationToken cancellationTo /// The license details. public async Task GetAdminLicenseAsync(CancellationToken cancellationToken = default) { - return await GetAdminUrl("/license") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetAdminUrl("/license") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -472,7 +500,7 @@ public async Task GetAdminLicenseAsync(CancellationToken cancell public async Task UpdateAdminLicenseAsync(LicenseInfo licenseInfo, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/license") - .PostJsonAsync(licenseInfo, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(licenseInfo), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -485,9 +513,11 @@ public async Task UpdateAdminLicenseAsync(LicenseInfo licenseInf /// The mail server configuration. public async Task GetAdminMailServerAsync(CancellationToken cancellationToken = default) { - return await GetAdminUrl("/mail-server") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetAdminUrl("/mail-server") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -499,7 +529,7 @@ public async Task GetAdminMailServerAsync(CancellationT public async Task UpdateAdminMailServerAsync(MailServerConfiguration mailServerConfiguration, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/mail-server") - .PutJsonAsync(mailServerConfiguration, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(mailServerConfiguration), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -542,7 +572,7 @@ public async Task GetAdminMailServerSenderAddressAsync(CancellationToken public async Task UpdateAdminMailServerSenderAddressAsync(string senderAddress, CancellationToken cancellationToken = default) { var response = await GetAdminUrl("/mail-server/sender-address") - .PutJsonAsync(senderAddress, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(senderAddress), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); @@ -586,10 +616,14 @@ public async Task> GetAdminGroupPermissionsAsync(st }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/permissions/groups") + { + var response = await GetAdminUrl("/permissions/groups") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -611,7 +645,7 @@ public async Task UpdateAdminGroupPermissionsAsync(Permissions permission, var response = await GetAdminUrl("/permissions/groups") .SetQueryParams(queryParamValues) - .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -657,10 +691,14 @@ public async Task> GetAdminGroupPermissionsNon }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/permissions/groups/none") + { + var response = await GetAdminUrl("/permissions/groups/none") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -691,10 +729,14 @@ public async Task> GetAdminUserPermissionsAsync(stri }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/permissions/users") + { + var response = await GetAdminUrl("/permissions/users") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -716,7 +758,7 @@ public async Task UpdateAdminUserPermissionsAsync(Permissions permission, var response = await GetAdminUrl("/permissions/users") .SetQueryParams(queryParamValues) - .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -765,10 +807,14 @@ public async Task> GetAdminUserPermissionsNoneAsync(string? fi }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetAdminUrl("/permissions/users/none") + { + var response = await GetAdminUrl("/permissions/users/none") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -780,9 +826,11 @@ await GetAdminUrl("/permissions/users/none") /// The merge strategies configuration. public async Task GetAdminPullRequestsMergeStrategiesAsync(string scmId, CancellationToken cancellationToken = default) { - return await GetAdminUrl($"/pull-requests/{scmId}") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetAdminUrl($"/pull-requests/{scmId}") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -795,7 +843,7 @@ public async Task GetAdminPullRequestsMergeStrategiesAsync(stri public async Task UpdateAdminPullRequestsMergeStrategiesAsync(string scmId, MergeStrategies mergeStrategies, CancellationToken cancellationToken = default) { var response = await GetAdminUrl($"/pull-requests/{scmId}") - .PostJsonAsync(mergeStrategies, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(mergeStrategies), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs b/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs index b797c09..4f281dc 100644 --- a/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs @@ -1,3 +1,4 @@ +using Bitbucket.Net.Common; using Flurl.Http; using System.Collections.Generic; using System.Linq; @@ -26,9 +27,9 @@ private IFlurlRequest GetApplicationPropertiesUrl() => GetBaseUrl() public async Task> GetApplicationPropertiesAsync(CancellationToken cancellationToken = default) { var response = await GetApplicationPropertiesUrl() - .GetJsonAsync>(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); - return response; + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs index dda680d..6175c4a 100644 --- a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs @@ -65,10 +65,14 @@ public async Task> GetDashboardPullRequestsAsync(PullRe }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetDashboardUrl("/pull-requests") + { + var response = await GetDashboardUrl("/pull-requests") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -96,10 +100,14 @@ public async Task> GetDashboardPullRequestSug }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetDashboardUrl("/pull-request-suggestions") + { + var response = await GetDashboardUrl("/pull-request-suggestions") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs index fe36850..aeffea3 100644 --- a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs @@ -1,3 +1,4 @@ +using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Flurl.Http; using System.Collections.Generic; @@ -42,10 +43,14 @@ public async Task> GetGroupNamesAsync(string? filter = null, }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetGroupsUrl() + { + var response = await GetGroupsUrl() .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs b/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs index 9a3d744..02b2e17 100644 --- a/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs @@ -1,3 +1,4 @@ +using Bitbucket.Net.Common; using Flurl.Http; using System.Threading; using System.Threading.Tasks; @@ -25,10 +26,13 @@ private IFlurlRequest GetHooksUrl() => GetBaseUrl() /// The avatar image bytes. public async Task GetProjectHooksAvatarAsync(string hookKey, string? version = null, CancellationToken cancellationToken = default) { - return await GetHooksUrl() + var response = await GetHooksUrl() .AppendPathSegment($"/{hookKey}/avatar") .SetQueryParam("version", version) - .GetBytesAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + return await ReadResponseBytesAsync(response, cancellationToken).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs index f6d7e21..cf8e7f9 100644 --- a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs @@ -54,10 +54,14 @@ public async Task> GetInboxPullRequestsAsync( }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetInboxUrl("/pull-requests") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetInboxUrl("/pull-requests") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } diff --git a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs index 6ccd570..66aaed1 100644 --- a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs @@ -54,7 +54,7 @@ public async Task PreviewMarkupAsync(string text, var response = await GetMarkupUrl("/preview") .WithHeader("X-Atlassian-Token", "no-check") .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(text), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(text), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, s => diff --git a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs index 82bdd75..2730285 100644 --- a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs @@ -1,5 +1,7 @@ +using Bitbucket.Net.Common; using Bitbucket.Net.Models.Core.Tasks; using Flurl.Http; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -34,7 +36,7 @@ private IFlurlRequest GetTasksUrl(string path) => GetTasksUrl() public async Task CreateTaskAsync(TaskInfo taskInfo, CancellationToken cancellationToken = default) { var response = await GetTasksUrl() - .PostJsonAsync(taskInfo, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(taskInfo), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -49,10 +51,12 @@ public async Task CreateTaskAsync(TaskInfo taskInfo, Cancellation /// The requested task. public async Task GetTaskAsync(long taskId, int? avatarSize = null, CancellationToken cancellationToken = default) { - return await GetTasksUrl($"/{taskId}") + var response = await GetTasksUrl($"/{taskId}") .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -71,7 +75,7 @@ public async Task UpdateTaskAsync(long taskId, string text, Cance }; var response = await GetTasksUrl($"/{taskId}") - .PutJsonAsync(obj, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(obj), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs index 2f0a2c9..9ab9177 100644 --- a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs @@ -1,8 +1,10 @@ +using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Users; using Flurl.Http; using System.Collections.Generic; using System.Linq; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -68,10 +70,14 @@ public async Task> GetUsersAsync(string? filter = null, string } return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetUsersUrl() + { + var response = await GetUsersUrl() .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -91,7 +97,7 @@ public async Task UpdateUserAsync(string? email = null, string? displayNam }; var response = await GetUsersUrl() - .PutJsonAsync(obj, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(obj), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -106,7 +112,7 @@ public async Task UpdateUserAsync(string? email = null, string? displayNam public async Task UpdateUserCredentialsAsync(PasswordChange passwordChange, CancellationToken cancellationToken = default) { var response = await GetUsersUrl("/credentials") - .PutJsonAsync(passwordChange, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(passwordChange), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -121,10 +127,12 @@ public async Task UpdateUserCredentialsAsync(PasswordChange passwordChange /// The requested user. public async Task GetUserAsync(string userSlug, int? avatarSize = null, CancellationToken cancellationToken = default) { - return await GetUsersUrl($"/{userSlug}") + var response = await GetUsersUrl($"/{userSlug}") .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -151,10 +159,10 @@ public async Task DeleteUserAvatarAsync(string userSlug, CancellationToken public async Task> GetUserSettingsAsync(string userSlug, CancellationToken cancellationToken = default) { var response = await GetUsersUrl($"/{userSlug}/settings") - .GetJsonAsync>(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); - return response; + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -167,7 +175,7 @@ public async Task DeleteUserAvatarAsync(string userSlug, CancellationToken public async Task UpdateUserSettingsAsync(string userSlug, IDictionary userSettings, CancellationToken cancellationToken = default) { var response = await GetUsersUrl($"/{userSlug}/settings") - .PostJsonAsync(userSettings, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(userSettings), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); diff --git a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs index bb27026..b79821d 100644 --- a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs +++ b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs @@ -2,6 +2,7 @@ using Bitbucket.Net.Models.DefaultReviewers; using Flurl.Http; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -54,7 +55,7 @@ public async Task> GetDefaultRe public async Task CreateDefaultReviewerConditionAsync(string projectKey, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") - .PostJsonAsync(condition, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(condition), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -71,7 +72,7 @@ public async Task CreateDefaultReviewerCond public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") - .PutJsonAsync(condition, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(condition), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -123,7 +124,7 @@ public async Task> GetDefaultRe public async Task CreateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") - .PostJsonAsync(condition, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(condition), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -141,7 +142,7 @@ public async Task CreateDefaultReviewerCond public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) { var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") - .PutJsonAsync(condition, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(condition), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs index 0f4ee43..cd4faa4 100644 --- a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs +++ b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs @@ -1,7 +1,9 @@ +using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.PersonalAccessTokens; using Flurl.Http; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -52,10 +54,14 @@ public async Task> GetUserAccessTokensAsync(string user }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetPatUrl($"/users/{userSlug}") + { + var response = await GetPatUrl($"/users/{userSlug}") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -69,7 +75,7 @@ await GetPatUrl($"/users/{userSlug}") public async Task CreateAccessTokenAsync(string userSlug, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) { var response = await GetPatUrl($"/users/{userSlug}") - .PutJsonAsync(accessToken, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(accessToken), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -85,10 +91,12 @@ public async Task CreateAccessTokenAsync(string userSlug, Acces /// The access token details. public async Task GetUserAccessTokenAsync(string userSlug, string tokenId, int? avatarSize = null, CancellationToken cancellationToken = default) { - return await GetPatUrl($"/users/{userSlug}/{tokenId}") + var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -102,7 +110,7 @@ public async Task GetUserAccessTokenAsync(string userSlug, string t public async Task ChangeUserAccessTokenAsync(string userSlug, string tokenId, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) { var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") - .PostJsonAsync(accessToken, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(accessToken), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs index bf5c668..b20fb87 100644 --- a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs @@ -3,6 +3,7 @@ using Bitbucket.Net.Models.RefRestrictions; using Flurl.Http; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -62,10 +63,14 @@ public async Task> GetProjectRefRestrictionsAsync(st }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -80,7 +85,7 @@ public async Task> CreateProjectRefRestrictionsAsync { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") - .PostJsonAsync(refRestrictions, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(refRestrictions), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -107,7 +112,7 @@ public async Task> CreateProjectRefRestrictionsAsync public async Task CreateProjectRefRestrictionAsync(string projectKey, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") - .PostJsonAsync(refRestriction, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(refRestriction), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -123,10 +128,12 @@ public async Task CreateProjectRefRestrictionAsync(string projec /// The requested reference restriction. public async Task GetProjectRefRestrictionAsync(string projectKey, int refRestrictionId, int? avatarSize = null, CancellationToken cancellationToken = default) { - return await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -181,10 +188,14 @@ public async Task> GetRepositoryRefRestrictionsAsync }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -200,7 +211,7 @@ public async Task> CreateRepositoryRefRestrictionsAs { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") - .PostJsonAsync(refRestrictions, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(refRestrictions), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -229,7 +240,7 @@ public async Task> CreateRepositoryRefRestrictionsAs public async Task CreateRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) { var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") - .PostJsonAsync(refRestriction, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(refRestriction), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -247,10 +258,12 @@ public async Task CreateRepositoryRefRestrictionAsync(string pro public async Task GetRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, int? avatarSize = null, CancellationToken cancellationToken = default) { - return await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// diff --git a/src/Bitbucket.Net/RefSync/BitbucketClient.cs b/src/Bitbucket.Net/RefSync/BitbucketClient.cs index 3ad93fd..f0056ba 100644 --- a/src/Bitbucket.Net/RefSync/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefSync/BitbucketClient.cs @@ -1,6 +1,7 @@ using Bitbucket.Net.Common; using Bitbucket.Net.Models.RefSync; using Flurl.Http; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -60,7 +61,7 @@ public async Task EnableRepositorySynchronizati }; var response = await GetRefSyncUrl($"/projects/{projectKey}/repos/{repositorySlug}") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -77,7 +78,7 @@ public async Task EnableRepositorySynchronizati public async Task SynchronizeRepositoryAsync(string projectKey, string repositorySlug, Synchronize synchronize, CancellationToken cancellationToken = default) { var response = await GetRefSyncUrl($"/projects/{projectKey}/repos/{repositorySlug}") - .PostJsonAsync(synchronize, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(synchronize), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/src/Bitbucket.Net/Ssh/BitbucketClient.cs b/src/Bitbucket.Net/Ssh/BitbucketClient.cs index 235527e..d9e1135 100644 --- a/src/Bitbucket.Net/Ssh/BitbucketClient.cs +++ b/src/Bitbucket.Net/Ssh/BitbucketClient.cs @@ -98,10 +98,14 @@ public async Task> GetProjectKeysAsync(int keyId, }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetKeysUrl($"/ssh/{keyId}/projects") + { + var response = await GetKeysUrl($"/ssh/{keyId}/projects") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -134,10 +138,14 @@ public async Task> GetProjectKeysAsync(string projectKey }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetKeysUrl($"/projects/{projectKey}/ssh") + { + var response = await GetKeysUrl($"/projects/{projectKey}/ssh") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -158,7 +166,7 @@ public async Task CreateProjectKeyAsync(string projectKey, string ke }; var response = await GetKeysUrl($"/projects/{projectKey}/ssh") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -236,10 +244,14 @@ public async Task> GetRepoKeysAsync(int keyId, }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetKeysUrl($"/ssh/{keyId}/repos") + { + var response = await GetKeysUrl($"/ssh/{keyId}/repos") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -276,10 +288,14 @@ public async Task> GetRepoKeysAsync(string projectKey }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh") + { + var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -301,7 +317,7 @@ public async Task CreateRepoKeyAsync(string projectKey, string re }; var response = await GetKeysUrl($"/projects/{projectKey}/repos/{repositorySlug}/ssh") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -383,10 +399,14 @@ public async Task> GetUserKeysAsync(string? userSlug = null, }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetSshUrl("/keys") + { + var response = await GetSshUrl("/keys") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -401,7 +421,7 @@ public async Task CreateUserKeyAsync(string keyText, string? userSlug = nul { var response = await GetSshUrl("/keys") .SetQueryParam("user", userSlug) - .PostJsonAsync(new { text = keyText }, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(new { text = keyText }), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -445,8 +465,10 @@ public async Task DeleteUserKeyAsync(int keyId, CancellationToken cancella /// The SSH settings. public async Task GetSshSettingsAsync(CancellationToken cancellationToken = default) { - return await GetSshUrl("/settings") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetSshUrl("/settings") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } } \ No newline at end of file From 38ac6c94852a278170adfc6240cfe2009d3ed8e7 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:13:39 +0000 Subject: [PATCH 18/50] refactor: update error handling tests to use specific Bitbucket exceptions and improve assertions --- .../MockTests/ErrorHandlingMockTests.cs | 139 +++++++++++++++--- 1 file changed, 120 insertions(+), 19 deletions(-) diff --git a/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs index c020f18..104da16 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs @@ -1,5 +1,6 @@ +using Bitbucket.Net.Common.Exceptions; +using Bitbucket.Net.Common.Models; using Bitbucket.Net.Tests.Infrastructure; -using Flurl.Http; using System.Net; using System.Threading.Tasks; using Xunit; @@ -7,15 +8,9 @@ namespace Bitbucket.Net.Tests.MockTests; /// -/// Unit tests for error handling using WireMock. -/// Verifies that appropriate exceptions are thrown for HTTP error responses. +/// Tests that the typed exception hierarchy fires correctly for HTTP +/// error responses. /// -/// -/// NOTE: The current library implementation throws FlurlHttpException directly -/// rather than the documented BitbucketApiException types. This is because -/// Flurl throws before the custom error handling can intercept the response. -/// These tests verify the actual current behavior. -/// public class ErrorHandlingMockTests(BitbucketMockFixture fixture) : IClassFixture { private const string ApiBasePath = "/rest/api/1.0"; @@ -31,10 +26,11 @@ public async Task GetProjectAsync_WhenNotFound_ThrowsException() var client = _fixture.CreateClient(); // Act & Assert - var exception = await Assert.ThrowsAsync( + var exception = await Assert.ThrowsAsync( () => client.GetProjectAsync(projectKey)); - Assert.Equal((int)HttpStatusCode.NotFound, exception.StatusCode); + Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); + Assert.Contains(projectKey, exception.RequestUrl ?? string.Empty); } [Fact] @@ -47,10 +43,10 @@ public async Task GetProjectAsync_WhenUnauthorized_ThrowsException() var client = _fixture.CreateClient(); // Act & Assert - var exception = await Assert.ThrowsAsync( + var exception = await Assert.ThrowsAsync( () => client.GetProjectAsync(projectKey)); - Assert.Equal((int)HttpStatusCode.Unauthorized, exception.StatusCode); + Assert.Equal(HttpStatusCode.Unauthorized, exception.StatusCode); } [Fact] @@ -63,10 +59,10 @@ public async Task GetProjectAsync_WhenServerError_ThrowsException() var client = _fixture.CreateClient(); // Act & Assert - var exception = await Assert.ThrowsAsync( + var exception = await Assert.ThrowsAsync( () => client.GetProjectAsync(projectKey)); - Assert.Equal((int)HttpStatusCode.InternalServerError, exception.StatusCode); + Assert.Equal(HttpStatusCode.InternalServerError, exception.StatusCode); } [Fact] @@ -80,10 +76,10 @@ public async Task GetProjectRepositoryAsync_WhenNotFound_ThrowsException() var client = _fixture.CreateClient(); // Act & Assert - var exception = await Assert.ThrowsAsync( + var exception = await Assert.ThrowsAsync( () => client.GetProjectRepositoryAsync(projectKey, repoSlug)); - Assert.Equal((int)HttpStatusCode.NotFound, exception.StatusCode); + Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); } [Fact] @@ -98,9 +94,114 @@ public async Task GetPullRequestAsync_WhenNotFound_ThrowsException() var client = _fixture.CreateClient(); // Act & Assert - var exception = await Assert.ThrowsAsync( + var exception = await Assert.ThrowsAsync( () => client.GetPullRequestAsync(projectKey, repoSlug, pullRequestId)); - Assert.Equal((int)HttpStatusCode.NotFound, exception.StatusCode); + Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); + } + + [Fact] + public async Task GetProjectAsync_WhenForbidden_ThrowsException() + { + _fixture.Reset(); + var projectKey = "TEST"; + _fixture.Server.SetupForbidden($"{ApiBasePath}/projects/{projectKey}"); + var client = _fixture.CreateClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal(HttpStatusCode.Forbidden, exception.StatusCode); + } + + [Fact] + public async Task GetProjectAsync_WhenBadRequest_ThrowsException() + { + _fixture.Reset(); + var projectKey = "TEST"; + _fixture.Server.SetupBadRequest($"{ApiBasePath}/projects/{projectKey}"); + var client = _fixture.CreateClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal(HttpStatusCode.BadRequest, exception.StatusCode); + } + + [Fact] + public async Task GetProjectAsync_WhenConflict_ThrowsException() + { + _fixture.Reset(); + var projectKey = "TEST"; + _fixture.Server.SetupConflict($"{ApiBasePath}/projects/{projectKey}"); + var client = _fixture.CreateClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal(HttpStatusCode.Conflict, exception.StatusCode); + } + + [Fact] + public async Task GetProjectAsync_WhenRateLimited_ThrowsException() + { + _fixture.Reset(); + var projectKey = "TEST"; + _fixture.Server.SetupRateLimited($"{ApiBasePath}/projects/{projectKey}"); + var client = _fixture.CreateClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal(HttpStatusCode.TooManyRequests, exception.StatusCode); + } + + [Fact] + public async Task GetProjectAsync_WhenErrorHasJsonBody_PopulatesErrorsAndContext() + { + _fixture.Reset(); + var projectKey = "CTX"; + var error = new Error { Context = "projectKey", Message = "Invalid project key", ExceptionName = "TestException" }; + _fixture.Server.SetupErrorWithJsonBody($"{ApiBasePath}/projects/{projectKey}", HttpStatusCode.NotFound, error); + var client = _fixture.CreateClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); + Assert.NotEmpty(exception.Errors); + Assert.Equal("projectKey", exception.Context); + } + + [Fact] + public async Task GetProjectAsync_WhenErrorHasHtmlBody_PreservesMessage() + { + _fixture.Reset(); + var projectKey = "HTML"; + var htmlBody = "Bad Gateway"; + _fixture.Server.SetupErrorWithHtmlBody($"{ApiBasePath}/projects/{projectKey}", HttpStatusCode.BadGateway, htmlBody); + var client = _fixture.CreateClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal(HttpStatusCode.BadGateway, exception.StatusCode); + Assert.Single(exception.Errors); + Assert.Contains("Bad Gateway", exception.Errors[0].Message); + } + + [Fact] + public async Task GetProjectAsync_WhenErrorHasEmptyBody_UsesEmptyErrors() + { + _fixture.Reset(); + var projectKey = "EMPTY"; + _fixture.Server.SetupErrorWithEmptyBody($"{ApiBasePath}/projects/{projectKey}", HttpStatusCode.InternalServerError); + var client = _fixture.CreateClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal(HttpStatusCode.InternalServerError, exception.StatusCode); + Assert.Empty(exception.Errors); } } \ No newline at end of file From 68b48602faf59d8184d6abdef9ddec3f614152d8 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:15:03 +0000 Subject: [PATCH 19/50] refactor: update branch retrieval methods to use async response handling and improve error management --- src/Bitbucket.Net/Branches/BitbucketClient.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/Bitbucket.Net/Branches/BitbucketClient.cs b/src/Bitbucket.Net/Branches/BitbucketClient.cs index c42a4b4..c466284 100644 --- a/src/Bitbucket.Net/Branches/BitbucketClient.cs +++ b/src/Bitbucket.Net/Branches/BitbucketClient.cs @@ -56,10 +56,14 @@ public async Task> GetCommitBranchInfoAsync(string proje }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches/info/{fullSha}") + { + var response = await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches/info/{fullSha}") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -72,9 +76,11 @@ await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches/info /// The branch model configuration. public async Task GetRepoBranchModelAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { - return await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branchmodel") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branchmodel") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -95,7 +101,7 @@ public async Task CreateRepoBranchAsync(string projectKey, string reposi }; var response = await GetBranchUrl($"/projects/{projectKey}/repos/{repositorySlug}/branches") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); From 3d40f23b472dcc1e8dbb27960cded014444e3687 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:16:38 +0000 Subject: [PATCH 20/50] refactor: replace PostJsonAsync with SendAsync for improved request handling in BitbucketClient --- src/Bitbucket.Net/Git/BitbucketClient.cs | 5 +++-- src/Bitbucket.Net/Jira/BitbucketClient.cs | 14 ++++++++++---- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/src/Bitbucket.Net/Git/BitbucketClient.cs b/src/Bitbucket.Net/Git/BitbucketClient.cs index 0e27d9e..0bda1b2 100644 --- a/src/Bitbucket.Net/Git/BitbucketClient.cs +++ b/src/Bitbucket.Net/Git/BitbucketClient.cs @@ -2,6 +2,7 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Git; using Flurl.Http; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -56,7 +57,7 @@ public async Task RebasePullRequestAsync(string projectKey, string { var data = new { version }; var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/pull-requests/{pullRequestId}/rebase") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -82,7 +83,7 @@ public async Task CreateTagAsync(string projectKey, string repositorySlug, }; var response = await GetGitUrl($"/projects/{projectKey}/repos/{repositorySlug}/tags") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); diff --git a/src/Bitbucket.Net/Jira/BitbucketClient.cs b/src/Bitbucket.Net/Jira/BitbucketClient.cs index 98797cd..7864373 100644 --- a/src/Bitbucket.Net/Jira/BitbucketClient.cs +++ b/src/Bitbucket.Net/Jira/BitbucketClient.cs @@ -1,8 +1,10 @@ +using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Builds; using Bitbucket.Net.Models.Jira; using Flurl.Http; using System.Collections.Generic; +using System.Net.Http; using System.Threading; using System.Threading.Tasks; @@ -52,10 +54,14 @@ public async Task> GetChangeSetsAsync(string issueKey, in }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetJiraUrl($"/issues/{issueKey}/commits") + { + var response = await GetJiraUrl($"/issues/{issueKey}/commits") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -79,7 +85,7 @@ public async Task CreateJiraIssueAsync(string pullRequestCommentId, s var response = await GetJiraUrl($"/comments/{pullRequestCommentId}/issues") .SetQueryParam("applicationId", applicationId) - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); From b0c8c98459bf77537e648eaf5651279847bb0aed Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:18:16 +0000 Subject: [PATCH 21/50] refactor: update Projects BitbucketClient to use async response handling and improve error management --- .../Core/Projects/BitbucketClient.cs | 679 ++++++++++++------ 1 file changed, 452 insertions(+), 227 deletions(-) diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs index 1f11ca3..ecd94a4 100644 --- a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs @@ -94,10 +94,14 @@ public async Task> GetProjectsAsync( }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetProjectsUrl() + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -129,10 +133,14 @@ public IAsyncEnumerable GetProjectsStreamAsync( }; return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); + { + var response = await GetProjectsUrl() + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); } /// @@ -144,7 +152,7 @@ await GetProjectsUrl() public async Task CreateProjectAsync(ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) { var response = await GetProjectsUrl() - .PostJsonAsync(projectDefinition, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(projectDefinition), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -175,7 +183,7 @@ public async Task DeleteProjectAsync(string projectKey, CancellationToken public async Task UpdateProjectAsync(string projectKey, ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) { var response = await GetProjectsUrl($"/{projectKey}") - .PutJsonAsync(projectDefinition, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(projectDefinition), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -224,10 +232,14 @@ public async Task> GetProjectUserPermissionsAsync(st }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/permissions/users") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/users") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -304,10 +316,14 @@ public async Task> GetProjectUserPermissionsNoneAsync( }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/permissions/users/none") + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/users/none") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -336,10 +352,14 @@ public async Task> GetProjectGroupPermissionsAsync( }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/permissions/groups") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -416,10 +436,14 @@ public async Task> GetProjectGroupPermissionsNoneAsync }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/permissions/groups/none") + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/groups/none") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -454,7 +478,7 @@ private async Task SetProjectDefaultPermissionAsync(string projectKey, Per var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -507,10 +531,14 @@ public async Task> GetProjectRepositoriesAsync(string pr }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/repos") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetProjectsUrl($"/{projectKey}/repos") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -531,10 +559,14 @@ public IAsyncEnumerable GetProjectRepositoriesStreamAsync(string pro }; return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsUrl($"/{projectKey}/repos") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); + { + var response = await GetProjectsUrl($"/{projectKey}/repos") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); } /// @@ -554,7 +586,7 @@ public async Task CreateProjectRepositoryAsync(string projectKey, st }; var response = await GetProjectsUrl($"/{projectKey}/repos") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -569,9 +601,11 @@ public async Task CreateProjectRepositoryAsync(string projectKey, st /// The requested repository. public async Task GetProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug) - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -594,7 +628,7 @@ public async Task CreateProjectRepositoryForkAsync(string projec }; var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -643,7 +677,7 @@ public async Task UpdateProjectRepositoryAsync(string projectKey, st }; var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .PutJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -673,10 +707,14 @@ public async Task> GetProjectRepositoryForksAsync(st }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/forks") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/forks") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -690,7 +728,7 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/forks") public async Task RecreateProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/recreate") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -720,10 +758,14 @@ public async Task> GetRelatedProjectRepositoriesAsyn }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/related") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/related") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -757,10 +799,13 @@ public async Task GetProjectRepositoryArchiveAsync(string projectKey, st ["prefix"] = prefix, }; - return await GetProjectsReposUrl(projectKey, repositorySlug, "/archive") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/archive") .SetQueryParams(queryParamValues) - .GetBytesAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + return await ReadResponseBytesAsync(response, cancellationToken).ConfigureAwait(false); } /// @@ -790,10 +835,14 @@ public async Task> GetProjectRepositoryGroupPermiss }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -817,7 +866,7 @@ public async Task UpdateProjectRepositoryGroupPermissionsAsync(string proj var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") .SetQueryParams(queryParamValues) - .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -868,10 +917,14 @@ public async Task> GetProjectRepositoryGroupPe }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups/none") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups/none") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -905,10 +958,14 @@ public async Task> GetProjectRepositoryUserPermissio }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -932,7 +989,7 @@ public async Task UpdateProjectRepositoryUserPermissionsAsync(string proje var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") .SetQueryParams(queryParamValues) - .PutJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -987,10 +1044,14 @@ public async Task> GetProjectRepositoryUserPermissionsNoneAsyn }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users/none") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users/none") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -1030,10 +1091,14 @@ public async Task> GetBranchesAsync(string projectKey, strin }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -1062,10 +1127,14 @@ public IAsyncEnumerable GetBranchesStreamAsync(string projectKey, string }; return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); } /// @@ -1079,7 +1148,7 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") public async Task CreateBranchAsync(string projectKey, string repositorySlug, BranchInfo branchInfo, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .PostJsonAsync(branchInfo, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(branchInfo), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -1094,9 +1163,11 @@ public async Task CreateBranchAsync(string projectKey, string repository /// The default branch. public async Task GetDefaultBranchAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, "/branches/default") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches/default") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -1110,7 +1181,7 @@ public async Task GetDefaultBranchAsync(string projectKey, string reposi public async Task SetDefaultBranchAsync(string projectKey, string repositorySlug, BranchRef branchRef, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .PutJsonAsync(branchRef, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(branchRef), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -1147,10 +1218,12 @@ public async Task BrowseProjectRepositoryAsync(string projectKey, st queryParamValues.Add("noContent", value: null); } - return await GetProjectsReposUrl(projectKey, repositorySlug, "/browse") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/browse") .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -1185,10 +1258,12 @@ public async Task BrowseProjectRepositoryPathAsync(string projec queryParamValues.Add("noContent", value: null); } - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -1211,9 +1286,12 @@ public async Task GetRawFileContentStreamAsync(string projectKey, string request = request.SetQueryParam("at", at); } - return await request - .GetStreamAsync(cancellationToken: cancellationToken) + var response = await request + .GetAsync(cancellationToken) .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + return await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); } /// @@ -1330,10 +1408,14 @@ public async Task> GetChangesAsync(string projectKey, string }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -1382,10 +1464,14 @@ public async Task> GetCommitsAsync(string projectKey, string }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -1420,10 +1506,14 @@ public IAsyncEnumerable GetCommitsStreamAsync(string projectKey, string }; return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); } /// @@ -1443,10 +1533,12 @@ public async Task GetCommitAsync(string projectKey, string repositorySlu ["path"] = path, }; - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}") .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -1480,10 +1572,14 @@ public async Task> GetCommitChangesAsync(string projectKey, }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/changes") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/changes") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -1518,10 +1614,14 @@ public async Task> GetCommitCommentsAsync(string projectKey }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -1546,7 +1646,7 @@ public async Task CreateCommitCommentAsync(string projectKey, string var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") .SetQueryParams(queryParamValues) - .PostJsonAsync(commentInfo, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(commentInfo), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -1566,10 +1666,12 @@ public async Task GetCommitCommentAsync(string projectKey, string re int? avatarSize = null, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -1586,7 +1688,7 @@ public async Task UpdateCommitCommentAsync(string projectKey, string CommentText commentText, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") - .PutJsonAsync(commentText, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(commentText), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -1654,10 +1756,12 @@ public async Task GetCommitDiffAsync(string projectKey, string repo ["withComments"] = BitbucketHelpers.BoolToString(withComments), }; - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -1695,11 +1799,14 @@ public async IAsyncEnumerable GetCommitDiffStreamAsync(string projectKey, ["withComments"] = BitbucketHelpers.BoolToString(withComments), }; - var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + await using (responseStream.ConfigureAwait(false)) { await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) @@ -1720,7 +1827,7 @@ public async IAsyncEnumerable GetCommitDiffStreamAsync(string projectKey, public async Task CreateCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -1774,10 +1881,14 @@ public async Task> GetRepositoryCompareChangesAsync(string p }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/changes") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/changes") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -1812,10 +1923,12 @@ public async Task GetRepositoryCompareDiffAsync(string projectKey, ["whitespace"] = whitespace, }; - return await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -1850,11 +1963,14 @@ public async IAsyncEnumerable GetRepositoryCompareDiffStreamAsync(string p ["whitespace"] = whitespace, }; - var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + await using (responseStream.ConfigureAwait(false)) { await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) @@ -1895,10 +2011,14 @@ public async Task> GetRepositoryCompareCommitsAsync(string p }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/commits") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/commits") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -1931,10 +2051,12 @@ public async Task GetRepositoryDiffAsync(string projectKey, string ["whitespace"] = whitespace, }; - return await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -1967,11 +2089,14 @@ public async IAsyncEnumerable GetRepositoryDiffStreamAsync(string projectK ["whitespace"] = whitespace, }; - var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + await using (responseStream.ConfigureAwait(false)) { await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) @@ -2007,10 +2132,14 @@ public async Task> GetRepositoryFilesAsync(string projectKey }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/files") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/files") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -2024,10 +2153,12 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/files") /// Last modified information. public async Task GetProjectRepositoryLastModifiedAsync(string projectKey, string repositorySlug, string at, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, "/last-modified") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/last-modified") .SetQueryParam("at", at) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -2063,10 +2194,14 @@ public async Task> GetRepositoryParticipantsAsync(string p }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/participants") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/participants") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -2112,10 +2247,14 @@ public async Task> GetPullRequestsAsync(string projectK }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -2148,10 +2287,14 @@ public IAsyncEnumerable GetPullRequestsStreamAsync(string projectKe }; return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); } /// @@ -2165,7 +2308,7 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") public async Task CreatePullRequestAsync(string projectKey, string repositorySlug, PullRequestInfo pullRequestInfo, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") - .PostJsonAsync(pullRequestInfo, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(pullRequestInfo), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -2181,9 +2324,11 @@ public async Task CreatePullRequestAsync(string projectKey, string /// The requested pull request. public async Task GetPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -2198,7 +2343,7 @@ public async Task GetPullRequestAsync(string projectKey, string rep public async Task UpdatePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, PullRequestUpdate pullRequestUpdate, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") - .PutJsonAsync(pullRequestUpdate, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(pullRequestUpdate), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -2216,7 +2361,7 @@ public async Task UpdatePullRequestAsync(string projectKey, string public async Task DeletePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, VersionInfo versionInfo, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") - .SendJsonAsync(HttpMethod.Delete, versionInfo, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Delete, CreateJsonContent(versionInfo), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -2256,10 +2401,14 @@ public async Task> GetPullRequestActivitiesAsyn }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/activities") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/activities") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -2282,7 +2431,7 @@ public async Task DeclinePullRequestAsync(string projectKey, string reposi var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/decline") .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -2305,10 +2454,12 @@ public async Task GetPullRequestMergeStateAsync(string pr ["version"] = version, }; - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -2338,7 +2489,7 @@ public async Task GetPullRequestMergeStateAsync(string pr return null; } - return await response.GetJsonAsync().ConfigureAwait(false); + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -2360,7 +2511,7 @@ public async Task MergePullRequestAsync(string projectKey, string r var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -2385,7 +2536,7 @@ public async Task ReopenPullRequestAsync(string projectKey, string var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/reopen") .SetQueryParams(queryParamValues) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -2402,7 +2553,7 @@ public async Task ReopenPullRequestAsync(string projectKey, string public async Task ApprovePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -2462,10 +2613,14 @@ public async Task> GetPullRequestChangesAsync(string project }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/changes") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/changes") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -2542,7 +2697,7 @@ public async Task CreatePullRequestCommentAsync(string projectKey, s var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/comments") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -2591,10 +2746,14 @@ public async Task> GetPullRequestCommentsAsync(string pr }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/comments") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/comments") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -2612,11 +2771,13 @@ public async Task GetPullRequestCommentAsync(string projectKey, stri int? avatarSize = null, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug) + var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") .SetQueryParam("avatarSize", avatarSize) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -2642,7 +2803,7 @@ public async Task UpdatePullRequestCommentAsync(string projectKey, s var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") .SetQueryParam("version", version) - .PutJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -2699,10 +2860,14 @@ public async Task> GetPullRequestCommitsAsync(string project }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -2725,10 +2890,14 @@ public IAsyncEnumerable GetPullRequestCommitsStreamAsync(string projectK }; return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); } /// @@ -2758,11 +2927,13 @@ public async Task GetPullRequestDiffAsync(string projectKey, string { var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - return await GetProjectsReposUrl(projectKey, repositorySlug) + var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -2791,12 +2962,15 @@ public async IAsyncEnumerable GetPullRequestDiffStreamAsync(string project [EnumeratorCancellation] CancellationToken cancellationToken = default) { var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - var responseStream = await GetProjectsReposUrl(projectKey, repositorySlug) + var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + try { await foreach (var diff in DeserializePullRequestDiffsAsync(responseStream, cancellationToken).ConfigureAwait(false)) @@ -2839,11 +3013,13 @@ public async Task GetPullRequestDiffPathAsync(string projectKey, st { var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - return await GetProjectsReposUrl(projectKey, repositorySlug) + var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/diff/{path}") .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } private static Dictionary CreatePullRequestDiffQueryParams(int contextLines, DiffTypes diffType, string? sinceId, @@ -2929,11 +3105,15 @@ public async Task> GetPullRequestParticipantsAsync(stri }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -2960,7 +3140,7 @@ public async Task AssignUserRoleToPullRequestAsync(string projectKe var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3014,7 +3194,7 @@ public async Task UpdatePullRequestParticipantStatus(string project var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/participants/{userSlug}") - .PutJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3077,10 +3257,14 @@ public async Task> GetPullRequestTasksAsync(string pr }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/tasks") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/tasks") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -3103,10 +3287,12 @@ await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequ [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsAsync and count the results for 9.0+ compatibility.")] public async Task GetPullRequestTaskCountAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug) + var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/tasks/count") - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } #region Blocker Comments (Bitbucket Server 9.0+) @@ -3152,10 +3338,14 @@ public async Task> GetPullRequestBlockerCommentsAsyn }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/blocker-comments") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/blocker-comments") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -3176,10 +3366,12 @@ public async Task GetPullRequestBlockerCommentAsync( long blockerCommentId, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug) + var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -3210,7 +3402,7 @@ public async Task CreatePullRequestBlockerCommentAsync( var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3245,7 +3437,7 @@ public async Task UpdatePullRequestBlockerCommentAsync( var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") - .PutJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3413,7 +3605,7 @@ public async Task WatchPullRequestAsync(string projectKey, string reposito { var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/pull-requests/{pullRequestId}/watch") - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); @@ -3465,11 +3657,14 @@ public async Task RetrieveRawContentAsync(string projectKey, string repo ["htmlEscape"] = BitbucketHelpers.BoolToString(htmlEscape), }; - return await GetProjectsReposUrl(projectKey, repositorySlug) + var response = await GetProjectsReposUrl(projectKey, repositorySlug) .AppendPathSegment($"/raw/{path}") .SetQueryParams(queryParamValues) - .GetStreamAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + return await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); } /// @@ -3481,9 +3676,11 @@ public async Task RetrieveRawContentAsync(string projectKey, string repo /// The pull request settings. public async Task GetProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -3498,7 +3695,7 @@ public async Task UpdateProjectRepositoryPullRequestSetting PullRequestSettings pullRequestSettings, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") - .PostJsonAsync(pullRequestSettings, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(pullRequestSettings), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3531,10 +3728,14 @@ public async Task> GetProjectRepositoryHooksSettingsAsync(stri }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/hooks") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/hooks") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -3548,9 +3749,11 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/hooks") /// The hook configuration. public async Task GetProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -3582,7 +3785,7 @@ public async Task DeleteProjectRepositoryHookSettingsAsync(string projectK public async Task EnableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, object? hookSettings = null, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") - .PutJsonAsync(hookSettings, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(hookSettings), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3615,9 +3818,11 @@ public async Task DisableProjectRepositoryHookAsync(string projectKey, str /// A dictionary of hook settings. public async Task> GetProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") - .GetJsonAsync>(cancellationToken: cancellationToken) + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -3633,7 +3838,7 @@ public async Task DisableProjectRepositoryHookAsync(string projectKey, str Dictionary allSettings, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") - .PutJsonAsync(allSettings, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(allSettings), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3648,10 +3853,12 @@ public async Task DisableProjectRepositoryHookAsync(string projectKey, str /// The pull request settings. public async Task GetProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, CancellationToken cancellationToken = default) { - return await GetProjectUrl(projectKey) + var response = await GetProjectUrl(projectKey) .AppendPathSegment($"/settings/pull-requests/{scmId}") - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -3666,7 +3873,7 @@ public async Task UpdateProjectPullRequestsMergeStrategiesAsync { var response = await GetProjectUrl(projectKey) .AppendPathSegment($"/settings/pull-requests/{scmId}") - .PostJsonAsync(mergeStrategies, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(mergeStrategies), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3702,10 +3909,14 @@ public async Task> GetProjectRepositoryTagsAsync(string project }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -3733,7 +3944,7 @@ public async Task CreateProjectRepositoryTagAsync(string projectKey, string }; var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") - .PostJsonAsync(data, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3749,9 +3960,11 @@ public async Task CreateProjectRepositoryTagAsync(string projectKey, string /// The requested tag. public async Task GetProjectRepositoryTagAsync(string projectKey, string repositorySlug, string tagName, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/tags/{tagName}") - .GetJsonAsync(cancellationToken: cancellationToken) + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/tags/{tagName}") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -3784,10 +3997,14 @@ public async Task> GetProjectRepositoryWebHooksAsync(string }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -3802,7 +4019,7 @@ await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") public async Task CreateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, WebHook webHook, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") - .PostJsonAsync(webHook, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, CreateJsonContent(webHook), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3820,7 +4037,7 @@ public async Task TestProjectRepositoryWebHookAsync( { var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks/test") .SetQueryParam("url", url) - .PostJsonAsync(new StringContent(""), cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3846,10 +4063,12 @@ public async Task GetProjectRepositoryWebHookAsync(string projectKey, s ["statistics"] = BitbucketHelpers.BoolToString(statistics), }; - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") .SetQueryParams(queryParamValues) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -3865,7 +4084,7 @@ public async Task UpdateProjectRepositoryWebHookAsync(string projectKey string webHookId, WebHook webHook, CancellationToken cancellationToken = default) { var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") - .PutJsonAsync(webHook, cancellationToken: cancellationToken) + .SendAsync(HttpMethod.Put, CreateJsonContent(webHook), cancellationToken: cancellationToken) .ConfigureAwait(false); return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -3913,11 +4132,13 @@ public async Task GetProjectRepositoryWebHookLatestAsync(string projectK ["outcome"] = BitbucketHelpers.WebHookOutcomeToString(outcome), }; - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/latest") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/latest") .SetQueryParams(queryParamValues) //.GetJsonAsync() - .GetStringAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); } /// @@ -3934,10 +4155,12 @@ public async Task GetProjectRepositoryWebHookStatisticsAsync( string? @event = null, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics") + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics") .SetQueryParam("event", @event) - .GetJsonAsync(cancellationToken: cancellationToken) + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); } /// @@ -3951,8 +4174,10 @@ public async Task GetProjectRepositoryWebHookStatisticsAsync( public async Task> GetProjectRepositoryWebHookStatisticsSummaryAsync(string projectKey, string repositorySlug, string webHookId, CancellationToken cancellationToken = default) { - return await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics/summary") - .GetJsonAsync>(cancellationToken: cancellationToken) + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics/summary") + .GetAsync(cancellationToken) .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); } } \ No newline at end of file From f05ea47d1bb49f5a54746b9fadae1e0b2dc56afb Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 21:19:55 +0000 Subject: [PATCH 22/50] refactor: update repository methods to use async response handling and improve error management --- .../Core/Profile/BitbucketClient.cs | 12 ++++++---- .../Core/Repos/BitbucketClient.cs | 24 ++++++++++++------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs index be1dc1e..9025c71 100644 --- a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs @@ -53,10 +53,14 @@ public async Task> GetRecentReposAsync(Permissions? perm }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetProfileUrl("/recent/repos") - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetProfileUrl("/recent/repos") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs index e4e52af..8beb399 100644 --- a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs @@ -55,10 +55,14 @@ public async Task> GetRepositoriesAsync( }; return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetReposUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken) + { + var response = await GetReposUrl() + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) .ConfigureAwait(false); } @@ -99,9 +103,13 @@ public IAsyncEnumerable GetRepositoriesStreamAsync( }; return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - await GetReposUrl() - .SetQueryParams(qpv) - .GetJsonAsync>(cancellationToken: ct) - .ConfigureAwait(false), cancellationToken); + { + var response = await GetReposUrl() + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); } } \ No newline at end of file From 75a20edb34d08caf26cb5af523c70424c3d357e6 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 22:28:16 +0000 Subject: [PATCH 23/50] refactor: consolidate project properties and enhance build configurations across multiple project files --- Directory.Build.props | 15 +++++++++++++++ .../Bitbucket.Net.Benchmarks.csproj | 4 ---- global.json | 6 ++++++ src/Bitbucket.Net/Bitbucket.Net.csproj | 11 ++++++++--- .../Bitbucket.Net.Tests.csproj | 2 -- 5 files changed, 29 insertions(+), 9 deletions(-) create mode 100644 Directory.Build.props create mode 100644 global.json diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 0000000..572bf01 --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,15 @@ + + + net10.0 + latest + enable + enable + true + true + true + true + snupkg + true + true + + diff --git a/benchmarks/Bitbucket.Net.Benchmarks/Bitbucket.Net.Benchmarks.csproj b/benchmarks/Bitbucket.Net.Benchmarks/Bitbucket.Net.Benchmarks.csproj index aae63d2..267cf7f 100644 --- a/benchmarks/Bitbucket.Net.Benchmarks/Bitbucket.Net.Benchmarks.csproj +++ b/benchmarks/Bitbucket.Net.Benchmarks/Bitbucket.Net.Benchmarks.csproj @@ -2,10 +2,6 @@ Exe - net10.0 - enable - latest - enable Release diff --git a/global.json b/global.json new file mode 100644 index 0000000..6ce09c7 --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.100", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Bitbucket.Net.csproj b/src/Bitbucket.Net/Bitbucket.Net.csproj index e15653e..758233f 100644 --- a/src/Bitbucket.Net/Bitbucket.Net.csproj +++ b/src/Bitbucket.Net/Bitbucket.Net.csproj @@ -1,9 +1,6 @@  - net10.0 - enable - latest BitbucketServer.Net 0.1.0-beta.1 Diogo Carvalho @@ -14,6 +11,10 @@ git MIT README.md + See https://github.com/diomonogatari/Bitbucket.Net/blob/main/CHANGELOG.md + Copyright (c) Diogo Carvalho + true + $(NoWarn);CS1591 true @@ -25,6 +26,10 @@ all runtime; build; native; contentfiles; analyzers + + all + runtime; build; native; contentfiles; analyzers + diff --git a/test/Bitbucket.Net.Tests/Bitbucket.Net.Tests.csproj b/test/Bitbucket.Net.Tests/Bitbucket.Net.Tests.csproj index efb6b0a..7b08685 100644 --- a/test/Bitbucket.Net.Tests/Bitbucket.Net.Tests.csproj +++ b/test/Bitbucket.Net.Tests/Bitbucket.Net.Tests.csproj @@ -2,10 +2,8 @@ Library - net10.0 - latest From bb85548cc00ac527bcb33536cc550ca7d7c9a93e Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 22:29:40 +0000 Subject: [PATCH 24/50] docs: update changelog and README for clarity and consistency --- CHANGELOG.md | 2 +- README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee69eff..59cc093 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## [0.1.0-beta.1] - 2026-02-06 +## [0.1.0-beta.1] - 2026-02-06 (pre-release) ### Notes diff --git a/README.md b/README.md index 22f4aca..d146665 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -![Icon](https://i.imgur.com/OsDAzyV.png) # Bitbucket.Net [![NuGet](https://img.shields.io/nuget/v/BitbucketServer.Net.svg)](https://www.nuget.org/packages/BitbucketServer.Net) [![NuGet Downloads](https://img.shields.io/nuget/dt/BitbucketServer.Net.svg)](https://www.nuget.org/packages/BitbucketServer.Net) [![CI](https://github.com/diomonogatari/Bitbucket.Net/actions/workflows/ci.yml/badge.svg)](https://github.com/diomonogatari/Bitbucket.Net/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/diomonogatari/Bitbucket.Net/branch/main/graph/badge.svg)](https://codecov.io/gh/diomonogatari/Bitbucket.Net) [![license](https://img.shields.io/github/license/diomonogatari/Bitbucket.Net.svg?maxAge=2592000)](https://github.com/diomonogatari/Bitbucket.Net/blob/main/LICENSE) ![](https://img.shields.io/badge/.net-10.0-yellowgreen.svg) ![](https://img.shields.io/badge/status-beta-orange.svg) From e33530e511c38eb09e2e91da01c8b56dfc84dd85 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 22:41:52 +0000 Subject: [PATCH 25/50] refactor: update .editorconfig and settings.json for improved code quality and organization --- .editorconfig | 3 +++ .vscode/settings.json | 1 + 2 files changed, 4 insertions(+) diff --git a/.editorconfig b/.editorconfig index 565f5f1..b2a57d4 100644 --- a/.editorconfig +++ b/.editorconfig @@ -236,6 +236,9 @@ dotnet_naming_style.begins_with_i.capitalization = pascal_case # IDE0055: Fix formatting — disabled (handled by dotnet format on demand) dotnet_diagnostic.IDE0055.severity = none +# IDE0005: Using directive is unnecessary +dotnet_diagnostic.IDE0005.severity = warning + # IDE0130: Namespace does not match folder structure — this codebase uses # partial classes across folders, so namespace != folder is expected dotnet_diagnostic.IDE0130.severity = none diff --git a/.vscode/settings.json b/.vscode/settings.json index 8983790..8f89ae2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "editor.formatOnType": false, "editor.codeActionsOnSave": { "source.organizeImports": "always", + "source.removeUnusedImports": "always", "source.fixAll": "always" } } From ef2e8f94959fce955ef1d698121acbb62517c2f9 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 22:49:27 +0000 Subject: [PATCH 26/50] chore: remove unused usings --- src/Bitbucket.Net/Audit/BitbucketClient.cs | 4 --- src/Bitbucket.Net/BitbucketClient.cs | 7 ----- src/Bitbucket.Net/Branches/BitbucketClient.cs | 4 --- src/Bitbucket.Net/Builds/BitbucketClient.cs | 4 --- .../CommentLikes/BitbucketClient.cs | 4 --- src/Bitbucket.Net/Common/BitbucketHelpers.cs | 3 --- .../Common/Converters/JsonEnumConverter.cs | 4 +-- .../Converters/UnixDateTimeOffsetConverter.cs | 3 +-- .../Common/DynamicMultipartFormDataContent.cs | 2 -- .../Exceptions/BitbucketApiException.cs | 2 -- .../BitbucketAuthenticationException.cs | 2 -- .../BitbucketBadRequestException.cs | 2 -- .../Exceptions/BitbucketConflictException.cs | 2 -- .../Exceptions/BitbucketForbiddenException.cs | 2 -- .../Exceptions/BitbucketNotFoundException.cs | 2 -- .../Exceptions/BitbucketRateLimitException.cs | 2 -- .../Exceptions/BitbucketServerException.cs | 2 -- .../BitbucketValidationException.cs | 2 -- .../Common/FlurlRequestExtensions.cs | 4 --- .../Common/Mcp/DiffStreamingExtensions.cs | 5 ---- src/Bitbucket.Net/Common/Mcp/McpExtensions.cs | 3 --- .../Common/Models/ErrorResponse.cs | 4 +-- .../Common/Models/PagedResults.cs | 4 +-- src/Bitbucket.Net/Common/TypeExtensions.cs | 3 +-- .../Common/UnixDateTimeExtensions.cs | 4 +-- .../Core/Admin/BitbucketClient.cs | 4 --- .../ApplicationProperties/BitbucketClient.cs | 4 --- .../Core/Dashboard/BitbucketClient.cs | 4 --- .../Core/Groups/BitbucketClient.cs | 3 --- .../Core/Hooks/BitbucketClient.cs | 2 -- .../Core/Inbox/BitbucketClient.cs | 3 --- .../Core/Logs/BitbucketClient.cs | 3 --- .../Core/Markup/BitbucketClient.cs | 4 --- .../Core/Profile/BitbucketClient.cs | 3 --- .../Core/Projects/BitbucketClient.cs | 8 ------ .../Core/Repos/BitbucketClient.cs | 3 --- .../Core/Tasks/BitbucketClient.cs | 3 --- .../Core/Users/BitbucketClient.cs | 5 ---- .../Core/WhoAmI/BitbucketClient.cs | 2 -- .../DefaultReviewers/BitbucketClient.cs | 4 --- src/Bitbucket.Net/Git/BitbucketClient.cs | 3 --- src/Bitbucket.Net/Jira/BitbucketClient.cs | 4 --- .../Models/Branches/BranchModel.cs | 1 - .../Models/Core/Admin/Cluster.cs | 4 +-- .../Models/Core/Admin/GroupUsers.cs | 4 +-- .../Models/Core/Admin/LicenseDetails.cs | 1 - .../Models/Core/Admin/MergeStrategies.cs | 4 +-- .../Models/Core/Admin/UserGroups.cs | 4 +-- .../Models/Core/Projects/BlockerComment.cs | 1 - .../Models/Core/Projects/BrowsePathItem.cs | 1 - .../Models/Core/Projects/CloneLinks.cs | 4 +-- .../Models/Core/Projects/Comment.cs | 2 -- .../Models/Core/Projects/CommentRef.cs | 2 -- .../Models/Core/Projects/Commit.cs | 2 -- .../Models/Core/Projects/Diff.cs | 4 +-- .../Models/Core/Projects/DiffHunk.cs | 4 +-- .../Models/Core/Projects/Differences.cs | 4 +-- .../Models/Core/Projects/HookDetails.cs | 1 - .../Models/Core/Projects/LastModified.cs | 4 +-- .../Models/Core/Projects/Links.cs | 4 +-- .../Models/Core/Projects/Path.cs | 4 +-- .../Core/Projects/PullRequestActivity.cs | 1 - .../Models/Core/Projects/PullRequestInfo.cs | 1 - .../Core/Projects/PullRequestMergeState.cs | 4 +-- .../Core/Projects/PullRequestSuggestion.cs | 1 - .../Models/Core/Projects/PullRequestUpdate.cs | 4 +-- .../Models/Core/Projects/Segment.cs | 4 +-- .../Models/Core/Projects/TimeWindow.cs | 1 - .../Models/Core/Projects/WebHook.cs | 2 -- .../Models/Core/Projects/WebHookInvocation.cs | 1 - .../Core/Projects/WebHookTestRequest.cs | 4 +-- .../Core/Projects/WebHookTestResponse.cs | 4 +-- .../Models/Core/Tasks/TaskAnchor.cs | 2 -- .../Models/Core/Tasks/TaskRef.cs | 1 - .../DefaultReviewerPullRequestCondition.cs | 1 - .../Models/Git/RebasePullRequestCondition.cs | 4 +-- src/Bitbucket.Net/Models/Jira/Changes.cs | 1 - .../PersonalAccessTokens/AccessToken.cs | 1 - .../PersonalAccessTokens/AccessTokenCreate.cs | 1 - .../Models/RefRestrictions/RefRestriction.cs | 1 - .../RefRestrictions/RefRestrictionBase.cs | 1 - .../RefRestrictions/RefRestrictionCreate.cs | 4 +-- .../RepositorySynchronizationStatus.cs | 2 -- .../PersonalAccessTokens/BitbucketClient.cs | 4 --- .../RefRestrictions/BitbucketClient.cs | 4 --- src/Bitbucket.Net/RefSync/BitbucketClient.cs | 3 --- .../Serialization/BitbucketJsonContext.cs | 27 +++++-------------- src/Bitbucket.Net/Ssh/BitbucketClient.cs | 4 --- .../Mcp/DiffStreamingExtensionsTests.cs | 10 +++---- .../Common/Mcp/McpExtensionsTests.cs | 5 ---- .../Infrastructure/BitbucketMockFixture.cs | 1 - .../Infrastructure/MockSetupExtensions.cs | 1 - .../MockTests/AdminExtendedMockTests.cs | 1 - .../MockTests/AdminMockTests.cs | 19 +++++++------ .../MockTests/AdminPermissionsMockTests.cs | 2 -- .../ApplicationPropertiesMockTests.cs | 1 - .../MockTests/AuditMockTests.cs | 2 -- .../MockTests/BranchExtendedMockTests.cs | 2 -- .../MockTests/BranchMockTests.cs | 2 -- .../MockTests/BuildExtendedMockTests.cs | 4 --- .../MockTests/BuildMockTests.cs | 2 -- .../MockTests/ChangesAndFilesMockTests.cs | 2 -- .../MockTests/CommentLikesMockTests.cs | 2 -- .../MockTests/CommitMockTests.cs | 2 -- .../MockTests/CoreExtendedMockTests.cs | 2 -- .../MockTests/DashboardMockTests.cs | 4 +-- .../DefaultReviewersExtendedMockTests.cs | 1 - .../MockTests/DefaultReviewersMockTests.cs | 2 -- .../MockTests/DiffAndTagMockTests.cs | 2 -- .../MockTests/ErrorHandlingMockTests.cs | 1 - .../MockTests/GitAndTagMockTests.cs | 1 - .../MockTests/GitMockTests.cs | 1 - .../MockTests/GroupsMockTests.cs | 2 -- .../MockTests/HooksMockTests.cs | 1 - .../MockTests/InboxMockTests.cs | 2 -- .../MockTests/JiraMockTests.cs | 4 +-- .../MockTests/LogsMockTests.cs | 1 - .../MockTests/MarkupMockTests.cs | 1 - .../PersonalAccessTokensMockTests.cs | 3 --- .../MockTests/ProfileMockTests.cs | 2 -- .../MockTests/ProjectCrudMockTests.cs | 1 - .../MockTests/ProjectMockTests.cs | 2 -- .../MockTests/ProjectPermissionsMockTests.cs | 3 --- .../MockTests/ProjectSettingsMockTests.cs | 3 --- .../MockTests/PullRequestActionsMockTests.cs | 1 - .../MockTests/PullRequestBlockerMockTests.cs | 2 -- .../MockTests/PullRequestCommentMockTests.cs | 1 - .../MockTests/PullRequestCrudMockTests.cs | 1 - .../MockTests/PullRequestExtendedMockTests.cs | 2 -- .../MockTests/PullRequestMockTests.cs | 2 -- .../PullRequestParticipantsMockTests.cs | 2 -- .../MockTests/PullRequestWatchMockTests.cs | 2 -- .../MockTests/RefRestrictionsMockTests.cs | 2 -- .../MockTests/RefSyncMockTests.cs | 2 -- .../MockTests/RepositoryCrudMockTests.cs | 2 -- .../MockTests/RepositoryMockTests.cs | 2 -- .../RepositoryOperationsMockTests.cs | 2 -- .../RepositoryPermissionsMockTests.cs | 3 --- .../MockTests/SshKeyMockTests.cs | 2 -- .../MockTests/TasksMockTests.cs | 1 - .../MockTests/UserMockTests.cs | 3 --- .../MockTests/UsersMockTests.cs | 3 --- .../MockTests/WebhookAndCompareMockTests.cs | 3 --- .../MockTests/WebhookMockTests.cs | 1 - .../MockTests/WhoAmIMockTests.cs | 1 - .../BitbucketClientConstructorTests.cs | 2 -- .../UnitTests/BitbucketHelpersTests.cs | 1 - .../UnitTests/CommonModelTests.cs | 2 -- .../UnitTests/DiffStreamingExtensionsTests.cs | 8 +++--- .../UnitTests/ExceptionTests.cs | 1 - .../UnitTests/JsonConverterTests.cs | 1 - .../UnitTests/ModelSerializationTests.cs | 2 -- 152 files changed, 48 insertions(+), 391 deletions(-) diff --git a/src/Bitbucket.Net/Audit/BitbucketClient.cs b/src/Bitbucket.Net/Audit/BitbucketClient.cs index 17a82b5..03de159 100644 --- a/src/Bitbucket.Net/Audit/BitbucketClient.cs +++ b/src/Bitbucket.Net/Audit/BitbucketClient.cs @@ -2,10 +2,6 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Audit; using Flurl.Http; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/BitbucketClient.cs b/src/Bitbucket.Net/BitbucketClient.cs index cae08bf..0a72cf0 100644 --- a/src/Bitbucket.Net/BitbucketClient.cs +++ b/src/Bitbucket.Net/BitbucketClient.cs @@ -6,18 +6,11 @@ using Flurl; using Flurl.Http; using Flurl.Http.Configuration; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; using System.Runtime.CompilerServices; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Branches/BitbucketClient.cs b/src/Bitbucket.Net/Branches/BitbucketClient.cs index c466284..c4ad2ec 100644 --- a/src/Bitbucket.Net/Branches/BitbucketClient.cs +++ b/src/Bitbucket.Net/Branches/BitbucketClient.cs @@ -3,12 +3,8 @@ using Bitbucket.Net.Models.Branches; using Bitbucket.Net.Models.Core.Projects; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; using System.Text; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Builds/BitbucketClient.cs b/src/Bitbucket.Net/Builds/BitbucketClient.cs index 39917f3..7e0ad0e 100644 --- a/src/Bitbucket.Net/Builds/BitbucketClient.cs +++ b/src/Bitbucket.Net/Builds/BitbucketClient.cs @@ -2,10 +2,6 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Builds; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs index 59908b3..2d0f22c 100644 --- a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs +++ b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs @@ -2,10 +2,6 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Users; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Common/BitbucketHelpers.cs b/src/Bitbucket.Net/Common/BitbucketHelpers.cs index 9a26f06..3302cc6 100644 --- a/src/Bitbucket.Net/Common/BitbucketHelpers.cs +++ b/src/Bitbucket.Net/Common/BitbucketHelpers.cs @@ -4,9 +4,6 @@ using Bitbucket.Net.Models.Git; using Bitbucket.Net.Models.RefRestrictions; using Bitbucket.Net.Models.RefSync; -using System; -using System.Collections.Generic; -using System.Linq; namespace Bitbucket.Net.Common; diff --git a/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs b/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs index a95bdcc..a5059a9 100644 --- a/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Serialization; namespace Bitbucket.Net.Common.Converters; diff --git a/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs b/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs index 79c6bf2..1e7eb28 100644 --- a/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs @@ -1,5 +1,4 @@ -using System; -using System.Text.Json; +using System.Text.Json; using System.Text.Json.Serialization; namespace Bitbucket.Net.Common.Converters; diff --git a/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs b/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs index ba01a54..3080606 100644 --- a/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs +++ b/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs @@ -1,6 +1,4 @@ using System.Collections; -using System.Collections.Generic; -using System.Net.Http; namespace Bitbucket.Net.Common; diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketApiException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketApiException.cs index a12fd73..2406a2d 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketApiException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketApiException.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Models; -using System; -using System.Collections.Generic; using System.Net; namespace Bitbucket.Net.Common.Exceptions; diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketAuthenticationException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketAuthenticationException.cs index 31a3a8d..f9cdaab 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketAuthenticationException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketAuthenticationException.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Models; -using System; -using System.Collections.Generic; using System.Net; namespace Bitbucket.Net.Common.Exceptions; diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketBadRequestException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketBadRequestException.cs index 8350993..b586898 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketBadRequestException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketBadRequestException.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Models; -using System; -using System.Collections.Generic; using System.Net; namespace Bitbucket.Net.Common.Exceptions; diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketConflictException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketConflictException.cs index e863348..e9c97ff 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketConflictException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketConflictException.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Models; -using System; -using System.Collections.Generic; using System.Net; namespace Bitbucket.Net.Common.Exceptions; diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketForbiddenException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketForbiddenException.cs index 6daf64f..87fe27f 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketForbiddenException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketForbiddenException.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Models; -using System; -using System.Collections.Generic; using System.Net; namespace Bitbucket.Net.Common.Exceptions; diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketNotFoundException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketNotFoundException.cs index f141c9d..726246d 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketNotFoundException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketNotFoundException.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Models; -using System; -using System.Collections.Generic; using System.Net; namespace Bitbucket.Net.Common.Exceptions; diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketRateLimitException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketRateLimitException.cs index 48b2757..5e72612 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketRateLimitException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketRateLimitException.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Models; -using System; -using System.Collections.Generic; using System.Net; namespace Bitbucket.Net.Common.Exceptions; diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketServerException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketServerException.cs index 8a6cf03..ab9a05d 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketServerException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketServerException.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Models; -using System; -using System.Collections.Generic; using System.Net; namespace Bitbucket.Net.Common.Exceptions; diff --git a/src/Bitbucket.Net/Common/Exceptions/BitbucketValidationException.cs b/src/Bitbucket.Net/Common/Exceptions/BitbucketValidationException.cs index 59b68c2..e61481a 100644 --- a/src/Bitbucket.Net/Common/Exceptions/BitbucketValidationException.cs +++ b/src/Bitbucket.Net/Common/Exceptions/BitbucketValidationException.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Models; -using System; -using System.Collections.Generic; using System.Net; namespace Bitbucket.Net.Common.Exceptions; diff --git a/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs b/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs index c83ac65..5c43e7c 100644 --- a/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs +++ b/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs @@ -1,8 +1,4 @@ using Flurl.Http; -using System; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net.Common; diff --git a/src/Bitbucket.Net/Common/Mcp/DiffStreamingExtensions.cs b/src/Bitbucket.Net/Common/Mcp/DiffStreamingExtensions.cs index 422f478..a87b214 100644 --- a/src/Bitbucket.Net/Common/Mcp/DiffStreamingExtensions.cs +++ b/src/Bitbucket.Net/Common/Mcp/DiffStreamingExtensions.cs @@ -1,10 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; -using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net.Common.Mcp; diff --git a/src/Bitbucket.Net/Common/Mcp/McpExtensions.cs b/src/Bitbucket.Net/Common/Mcp/McpExtensions.cs index 26f3dcc..0aa9ad3 100644 --- a/src/Bitbucket.Net/Common/Mcp/McpExtensions.cs +++ b/src/Bitbucket.Net/Common/Mcp/McpExtensions.cs @@ -1,7 +1,4 @@ -using System.Collections.Generic; using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net.Common.Mcp; diff --git a/src/Bitbucket.Net/Common/Models/ErrorResponse.cs b/src/Bitbucket.Net/Common/Models/ErrorResponse.cs index ac06484..19b4117 100644 --- a/src/Bitbucket.Net/Common/Models/ErrorResponse.cs +++ b/src/Bitbucket.Net/Common/Models/ErrorResponse.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Common.Models; +namespace Bitbucket.Net.Common.Models; /// /// Represents the error response returned by the Bitbucket Server API. diff --git a/src/Bitbucket.Net/Common/Models/PagedResults.cs b/src/Bitbucket.Net/Common/Models/PagedResults.cs index 413a3a4..5698614 100644 --- a/src/Bitbucket.Net/Common/Models/PagedResults.cs +++ b/src/Bitbucket.Net/Common/Models/PagedResults.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Common.Models; +namespace Bitbucket.Net.Common.Models; public class PagedResults : PagedResultsBase { diff --git a/src/Bitbucket.Net/Common/TypeExtensions.cs b/src/Bitbucket.Net/Common/TypeExtensions.cs index 582e871..da895e3 100644 --- a/src/Bitbucket.Net/Common/TypeExtensions.cs +++ b/src/Bitbucket.Net/Common/TypeExtensions.cs @@ -1,5 +1,4 @@ -using System; -using System.Reflection; +using System.Reflection; namespace Bitbucket.Net.Common; diff --git a/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs b/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs index b9ca21d..0cdc1a6 100644 --- a/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs +++ b/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs @@ -1,6 +1,4 @@ -using System; - -namespace Bitbucket.Net.Common; +namespace Bitbucket.Net.Common; public static class UnixDateTimeExtensions { diff --git a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs index 3088dd3..a96e670 100644 --- a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs @@ -3,10 +3,6 @@ using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Users; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using PasswordChange = Bitbucket.Net.Models.Core.Admin.PasswordChange; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs b/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs index 4f281dc..555fa16 100644 --- a/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/ApplicationProperties/BitbucketClient.cs @@ -1,9 +1,5 @@ using Bitbucket.Net.Common; using Flurl.Http; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs index 6175c4a..69921b2 100644 --- a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs @@ -2,10 +2,6 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Projects; using Flurl.Http; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs index aeffea3..54b62e2 100644 --- a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs @@ -1,9 +1,6 @@ using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Flurl.Http; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs b/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs index 02b2e17..878ea2b 100644 --- a/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Hooks/BitbucketClient.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Common; using Flurl.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs index cf8e7f9..3b6aed7 100644 --- a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs @@ -2,10 +2,7 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Projects; using Flurl.Http; -using System.Collections.Generic; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs b/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs index 95dc4ba..69e42fd 100644 --- a/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Logs/BitbucketClient.cs @@ -1,10 +1,7 @@ using Bitbucket.Net.Common; using Bitbucket.Net.Models.Core.Logs; using Flurl.Http; -using System.Net.Http; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs index 66aaed1..659c637 100644 --- a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs @@ -1,10 +1,6 @@ using Bitbucket.Net.Common; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs index 9025c71..b9ad22a 100644 --- a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs @@ -3,9 +3,6 @@ using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Projects; using Flurl.Http; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs index ecd94a4..372e24b 100644 --- a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs @@ -6,17 +6,9 @@ using Bitbucket.Net.Models.Core.Tasks; using Bitbucket.Net.Models.Core.Users; using Flurl.Http; -using System; using System.Buffers; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Net.Http; using System.Runtime.CompilerServices; -using System.Text; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs index 8beb399..02cd6f9 100644 --- a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs @@ -3,9 +3,6 @@ using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.Core.Projects; using Flurl.Http; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs index 2730285..43ef07a 100644 --- a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs @@ -1,9 +1,6 @@ using Bitbucket.Net.Common; using Bitbucket.Net.Models.Core.Tasks; using Flurl.Http; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs index 9ab9177..20dc770 100644 --- a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs @@ -2,11 +2,6 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.Core.Users; using Flurl.Http; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs b/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs index 14c899a..e58901a 100644 --- a/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/WhoAmI/BitbucketClient.cs @@ -1,8 +1,6 @@ using Bitbucket.Net.Common; using Flurl; using Flurl.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs index b79821d..3eff7ee 100644 --- a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs +++ b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs @@ -1,10 +1,6 @@ using Bitbucket.Net.Models.Core.Users; using Bitbucket.Net.Models.DefaultReviewers; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Git/BitbucketClient.cs b/src/Bitbucket.Net/Git/BitbucketClient.cs index 0bda1b2..3e6a7b1 100644 --- a/src/Bitbucket.Net/Git/BitbucketClient.cs +++ b/src/Bitbucket.Net/Git/BitbucketClient.cs @@ -2,9 +2,6 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Git; using Flurl.Http; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Jira/BitbucketClient.cs b/src/Bitbucket.Net/Jira/BitbucketClient.cs index 7864373..aba88af 100644 --- a/src/Bitbucket.Net/Jira/BitbucketClient.cs +++ b/src/Bitbucket.Net/Jira/BitbucketClient.cs @@ -3,10 +3,6 @@ using Bitbucket.Net.Models.Builds; using Bitbucket.Net.Models.Jira; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Models/Branches/BranchModel.cs b/src/Bitbucket.Net/Models/Branches/BranchModel.cs index 17e8704..737cd00 100644 --- a/src/Bitbucket.Net/Models/Branches/BranchModel.cs +++ b/src/Bitbucket.Net/Models/Branches/BranchModel.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Models.Core.Projects; -using System.Collections.Generic; namespace Bitbucket.Net.Models.Branches; diff --git a/src/Bitbucket.Net/Models/Core/Admin/Cluster.cs b/src/Bitbucket.Net/Models/Core/Admin/Cluster.cs index 74fef8f..531f9cd 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/Cluster.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/Cluster.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Admin; +namespace Bitbucket.Net.Models.Core.Admin; public class Cluster { diff --git a/src/Bitbucket.Net/Models/Core/Admin/GroupUsers.cs b/src/Bitbucket.Net/Models/Core/Admin/GroupUsers.cs index d114582..2253d00 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/GroupUsers.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/GroupUsers.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Admin; +namespace Bitbucket.Net.Models.Core.Admin; public class GroupUsers { diff --git a/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs b/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs index 505c4cb..8b4f906 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Admin; diff --git a/src/Bitbucket.Net/Models/Core/Admin/MergeStrategies.cs b/src/Bitbucket.Net/Models/Core/Admin/MergeStrategies.cs index 1659b9b..ddec15c 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/MergeStrategies.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/MergeStrategies.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Admin; +namespace Bitbucket.Net.Models.Core.Admin; public class MergeStrategies { diff --git a/src/Bitbucket.Net/Models/Core/Admin/UserGroups.cs b/src/Bitbucket.Net/Models/Core/Admin/UserGroups.cs index 9ef172d..3a1757a 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/UserGroups.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/UserGroups.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Admin; +namespace Bitbucket.Net.Models.Core.Admin; public class UserGroups { diff --git a/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs b/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs index 3b3f6a9..8ac0612 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Users; -using System; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/BrowsePathItem.cs b/src/Bitbucket.Net/Models/Core/Projects/BrowsePathItem.cs index e0014d6..a7a8781 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BrowsePathItem.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BrowsePathItem.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Common.Models; -using System.Collections.Generic; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/CloneLinks.cs b/src/Bitbucket.Net/Models/Core/Projects/CloneLinks.cs index 241d3ae..09f8e3c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CloneLinks.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CloneLinks.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class CloneLinks : Links { diff --git a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs index cb77c29..f374dcd 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs @@ -1,8 +1,6 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Tasks; using Bitbucket.Net.Models.Core.Users; -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs index 396db4a..d9a8908 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs @@ -1,8 +1,6 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Tasks; using Bitbucket.Net.Models.Core.Users; -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/Commit.cs b/src/Bitbucket.Net/Models/Core/Projects/Commit.cs index 6b16560..be31aeb 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Commit.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Commit.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/Diff.cs b/src/Bitbucket.Net/Models/Core/Projects/Diff.cs index 571692d..2bd1f8c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Diff.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Diff.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class Diff : DiffInfo { diff --git a/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs b/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs index be15712..7b45a17 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class DiffHunk { diff --git a/src/Bitbucket.Net/Models/Core/Projects/Differences.cs b/src/Bitbucket.Net/Models/Core/Projects/Differences.cs index ae9a102..0ed6755 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Differences.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Differences.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class Differences : DiffInfo { diff --git a/src/Bitbucket.Net/Models/Core/Projects/HookDetails.cs b/src/Bitbucket.Net/Models/Core/Projects/HookDetails.cs index 9851963..43ffb54 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/HookDetails.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/HookDetails.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/LastModified.cs b/src/Bitbucket.Net/Models/Core/Projects/LastModified.cs index a2d581a..0a68846 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/LastModified.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/LastModified.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class LastModified { diff --git a/src/Bitbucket.Net/Models/Core/Projects/Links.cs b/src/Bitbucket.Net/Models/Core/Projects/Links.cs index 95800e7..be82168 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Links.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Links.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class Links { diff --git a/src/Bitbucket.Net/Models/Core/Projects/Path.cs b/src/Bitbucket.Net/Models/Core/Projects/Path.cs index c69a911..78393e0 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Path.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Path.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; /// /// Represents a file path in a Bitbucket repository. diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs index 80c1420..7f5879d 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Users; -using System; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs index a28c07a..fcf8084 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestMergeState.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestMergeState.cs index 27265bf..f13b2c7 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestMergeState.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestMergeState.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class PullRequestMergeState { diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestSuggestion.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestSuggestion.cs index 3a6fa79..626a6ac 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestSuggestion.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestSuggestion.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestUpdate.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestUpdate.cs index df0aa36..e82c7bf 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestUpdate.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestUpdate.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class PullRequestUpdate { diff --git a/src/Bitbucket.Net/Models/Core/Projects/Segment.cs b/src/Bitbucket.Net/Models/Core/Projects/Segment.cs index e77a071..3b72f7a 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Segment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Segment.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class Segment { diff --git a/src/Bitbucket.Net/Models/Core/Projects/TimeWindow.cs b/src/Bitbucket.Net/Models/Core/Projects/TimeWindow.cs index d15d41b..9d10a11 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/TimeWindow.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/TimeWindow.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHook.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHook.cs index 3f11611..efe2355 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHook.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHook.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookInvocation.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookInvocation.cs index c6a50e4..3feec75 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookInvocation.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookInvocation.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequest.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequest.cs index 3c123fe..5a09992 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequest.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestRequest.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class WebHookTestRequest : WebHookRequest { diff --git a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestResponse.cs b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestResponse.cs index fc5bb59..51cc559 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WebHookTestResponse.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WebHookTestResponse.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Core.Projects; +namespace Bitbucket.Net.Models.Core.Projects; public class WebHookTestResponse { diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs index 05f6ae7..cf32816 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Projects; -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Tasks; diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs index 566705a..3864198 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs @@ -1,7 +1,6 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Core.Users; -using System; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Tasks; diff --git a/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestCondition.cs b/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestCondition.cs index bcb426b..05212f2 100644 --- a/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestCondition.cs +++ b/src/Bitbucket.Net/Models/DefaultReviewers/DefaultReviewerPullRequestCondition.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Models.Core.Users; -using System.Collections.Generic; namespace Bitbucket.Net.Models.DefaultReviewers; diff --git a/src/Bitbucket.Net/Models/Git/RebasePullRequestCondition.cs b/src/Bitbucket.Net/Models/Git/RebasePullRequestCondition.cs index b925c30..43607e6 100644 --- a/src/Bitbucket.Net/Models/Git/RebasePullRequestCondition.cs +++ b/src/Bitbucket.Net/Models/Git/RebasePullRequestCondition.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.Git; +namespace Bitbucket.Net.Models.Git; public class RebasePullRequestCondition { diff --git a/src/Bitbucket.Net/Models/Jira/Changes.cs b/src/Bitbucket.Net/Models/Jira/Changes.cs index b931d55..223dcb7 100644 --- a/src/Bitbucket.Net/Models/Jira/Changes.cs +++ b/src/Bitbucket.Net/Models/Jira/Changes.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Models.Core.Projects; -using System.Collections.Generic; namespace Bitbucket.Net.Models.Jira; diff --git a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessToken.cs b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessToken.cs index 82325a8..84a597a 100644 --- a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessToken.cs +++ b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessToken.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Users; -using System; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.PersonalAccessTokens; diff --git a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs index e0ae487..233e5bf 100644 --- a/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs +++ b/src/Bitbucket.Net/Models/PersonalAccessTokens/AccessTokenCreate.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Admin; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.PersonalAccessTokens; diff --git a/src/Bitbucket.Net/Models/RefRestrictions/RefRestriction.cs b/src/Bitbucket.Net/Models/RefRestrictions/RefRestriction.cs index 616c355..428c99d 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/RefRestriction.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/RefRestriction.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Core.Users; -using System.Collections.Generic; namespace Bitbucket.Net.Models.RefRestrictions; diff --git a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionBase.cs b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionBase.cs index 92ebada..63ecddc 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionBase.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionBase.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.DefaultReviewers; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.RefRestrictions; diff --git a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionCreate.cs b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionCreate.cs index beaca41..97a467c 100644 --- a/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionCreate.cs +++ b/src/Bitbucket.Net/Models/RefRestrictions/RefRestrictionCreate.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; - -namespace Bitbucket.Net.Models.RefRestrictions; +namespace Bitbucket.Net.Models.RefRestrictions; public class RefRestrictionCreate : RefRestrictionBase { diff --git a/src/Bitbucket.Net/Models/RefSync/RepositorySynchronizationStatus.cs b/src/Bitbucket.Net/Models/RefSync/RepositorySynchronizationStatus.cs index d11ce84..a3b1f2e 100644 --- a/src/Bitbucket.Net/Models/RefSync/RepositorySynchronizationStatus.cs +++ b/src/Bitbucket.Net/Models/RefSync/RepositorySynchronizationStatus.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.RefSync; diff --git a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs index cd4faa4..d2424bd 100644 --- a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs +++ b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs @@ -2,10 +2,6 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.PersonalAccessTokens; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs index b20fb87..9cc7980 100644 --- a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs @@ -2,10 +2,6 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Models.RefRestrictions; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/RefSync/BitbucketClient.cs b/src/Bitbucket.Net/RefSync/BitbucketClient.cs index f0056ba..abbb0ab 100644 --- a/src/Bitbucket.Net/RefSync/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefSync/BitbucketClient.cs @@ -1,9 +1,6 @@ using Bitbucket.Net.Common; using Bitbucket.Net.Models.RefSync; using Flurl.Http; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/src/Bitbucket.Net/Serialization/BitbucketJsonContext.cs b/src/Bitbucket.Net/Serialization/BitbucketJsonContext.cs index eb6249c..07b49db 100644 --- a/src/Bitbucket.Net/Serialization/BitbucketJsonContext.cs +++ b/src/Bitbucket.Net/Serialization/BitbucketJsonContext.cs @@ -8,7 +8,6 @@ // Core - Admin using Bitbucket.Net.Models.Core.Admin; // Core - Logs -using Bitbucket.Net.Models.Core.Logs; // Core - Projects using Bitbucket.Net.Models.Core.Projects; // Core - Tasks @@ -29,7 +28,6 @@ using Bitbucket.Net.Models.RefSync; // Ssh using Bitbucket.Net.Models.Ssh; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Serialization; @@ -132,7 +130,7 @@ namespace Bitbucket.Net.Serialization; [JsonSerializable(typeof(MergeStrategy))] [JsonSerializable(typeof(Node))] [JsonSerializable(typeof(PasswordBasic))] -[JsonSerializable(typeof(Bitbucket.Net.Models.Core.Admin.PasswordChange))] +[JsonSerializable(typeof(Bitbucket.Net.Models.Core.Admin.PasswordChange), TypeInfoPropertyName = "AdminPasswordChange")] [JsonSerializable(typeof(UserGroups))] [JsonSerializable(typeof(UserInfo))] [JsonSerializable(typeof(UserPermission))] @@ -182,7 +180,7 @@ namespace Bitbucket.Net.Serialization; [JsonSerializable(typeof(MergeCommits))] [JsonSerializable(typeof(MergeHookRequiredApprovers))] [JsonSerializable(typeof(Participant))] -[JsonSerializable(typeof(Path))] +[JsonSerializable(typeof(Bitbucket.Net.Models.Core.Projects.Path))] [JsonSerializable(typeof(Permittedoperations))] [JsonSerializable(typeof(Project))] [JsonSerializable(typeof(ProjectDefinition))] @@ -207,7 +205,7 @@ namespace Bitbucket.Net.Serialization; [JsonSerializable(typeof(Tag))] [JsonSerializable(typeof(TimeWindow))] [JsonSerializable(typeof(VersionInfo))] -[JsonSerializable(typeof(Bitbucket.Net.Models.Core.Projects.Veto))] +[JsonSerializable(typeof(Bitbucket.Net.Models.Core.Projects.Veto), TypeInfoPropertyName = "ProjectVeto")] [JsonSerializable(typeof(WebHook))] [JsonSerializable(typeof(WebHookInvocation))] [JsonSerializable(typeof(WebHookRequest))] @@ -235,7 +233,7 @@ namespace Bitbucket.Net.Serialization; // ============================================================================ [JsonSerializable(typeof(Identity))] [JsonSerializable(typeof(Named))] -[JsonSerializable(typeof(Bitbucket.Net.Models.Core.Users.PasswordChange))] +[JsonSerializable(typeof(Bitbucket.Net.Models.Core.Users.PasswordChange), TypeInfoPropertyName = "UserPasswordChange")] [JsonSerializable(typeof(User))] // ============================================================================ @@ -249,7 +247,7 @@ namespace Bitbucket.Net.Serialization; // Git Models // ============================================================================ [JsonSerializable(typeof(RebasePullRequestCondition))] -[JsonSerializable(typeof(Bitbucket.Net.Models.Git.Veto))] +[JsonSerializable(typeof(Bitbucket.Net.Models.Git.Veto), TypeInfoPropertyName = "GitVeto")] // ============================================================================ // Jira Models @@ -304,6 +302,8 @@ namespace Bitbucket.Net.Serialization; [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] +[JsonSerializable(typeof(List), TypeInfoPropertyName = "ProjectVetoList")] +[JsonSerializable(typeof(List), TypeInfoPropertyName = "GitVetoList")] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] [JsonSerializable(typeof(List))] @@ -311,19 +311,6 @@ namespace Bitbucket.Net.Serialization; [JsonSerializable(typeof(List))] [JsonSerializable(typeof(Dictionary))] -/// -/// Provides the source-generated JSON serialization context for Bitbucket.Net. -/// This context enables AOT compilation, trimming support, and improved serialization performance. -/// -/// -/// Consumers can use this context directly for advanced scenarios: -/// -/// var options = new JsonSerializerOptions -/// { -/// TypeInfoResolver = BitbucketJsonContext.Default -/// }; -/// -/// public partial class BitbucketJsonContext : JsonSerializerContext { } \ No newline at end of file diff --git a/src/Bitbucket.Net/Ssh/BitbucketClient.cs b/src/Bitbucket.Net/Ssh/BitbucketClient.cs index d9e1135..331f74e 100644 --- a/src/Bitbucket.Net/Ssh/BitbucketClient.cs +++ b/src/Bitbucket.Net/Ssh/BitbucketClient.cs @@ -4,12 +4,8 @@ using Bitbucket.Net.Models.RefRestrictions; using Bitbucket.Net.Models.Ssh; using Flurl.Http; -using System.Collections.Generic; -using System.Net.Http; using System.Text; using System.Text.Json; -using System.Threading; -using System.Threading.Tasks; namespace Bitbucket.Net; diff --git a/test/Bitbucket.Net.Tests/Common/Mcp/DiffStreamingExtensionsTests.cs b/test/Bitbucket.Net.Tests/Common/Mcp/DiffStreamingExtensionsTests.cs index 0558fde..8cfc5d2 100644 --- a/test/Bitbucket.Net.Tests/Common/Mcp/DiffStreamingExtensionsTests.cs +++ b/test/Bitbucket.Net.Tests/Common/Mcp/DiffStreamingExtensionsTests.cs @@ -1,12 +1,8 @@ using Bitbucket.Net.Common.Mcp; using Bitbucket.Net.Models.Core.Projects; -using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; using Xunit; +using ProjectPath = Bitbucket.Net.Models.Core.Projects.Path; namespace Bitbucket.Net.Tests.Common.Mcp; @@ -233,8 +229,8 @@ private static Diff CreateDiff(int hunks = 1, int segmentsPerHunk = 1, int lines { return new Diff { - Source = new Path { toString = "source.cs" }, - Destination = new Path { toString = "dest.cs" }, + Source = new ProjectPath { toString = "source.cs" }, + Destination = new ProjectPath { toString = "dest.cs" }, Hunks = [.. Enumerable.Range(0, hunks).Select(_ => new DiffHunk { SourceLine = 1, diff --git a/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs b/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs index 1d3f00c..1c1ee97 100644 --- a/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs +++ b/test/Bitbucket.Net.Tests/Common/Mcp/McpExtensionsTests.cs @@ -1,10 +1,5 @@ using Bitbucket.Net.Common.Mcp; -using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.Common.Mcp; diff --git a/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs b/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs index 9b0b2c4..f1bf273 100644 --- a/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs +++ b/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs @@ -1,4 +1,3 @@ -using System.Threading.Tasks; using WireMock.Server; using Xunit; diff --git a/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs b/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs index 2fb9edb..e315557 100644 --- a/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs +++ b/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Common.Models; -using System.IO; using System.Net; using System.Text.Json; using WireMock.Matchers; diff --git a/test/Bitbucket.Net.Tests/MockTests/AdminExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/AdminExtendedMockTests.cs index 812417d..8f21b9b 100644 --- a/test/Bitbucket.Net.Tests/MockTests/AdminExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/AdminExtendedMockTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/AdminMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/AdminMockTests.cs index 373de83..5d428be 100644 --- a/test/Bitbucket.Net.Tests/MockTests/AdminMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/AdminMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; @@ -97,10 +95,11 @@ public async Task GetAdminClusterAsync_ReturnsClusterInfo() Assert.NotNull(cluster); Assert.True(cluster.Running); Assert.NotNull(cluster.LocalNode); - Assert.Equal("node-1", cluster.LocalNode.Id); - Assert.Equal("bitbucket-node-1", cluster.LocalNode.Name); - Assert.True(cluster.LocalNode.Local); - Assert.Equal(2, cluster.Nodes.Count); + Assert.Equal("node-1", cluster.LocalNode!.Id); + Assert.Equal("bitbucket-node-1", cluster.LocalNode!.Name); + Assert.True(cluster.LocalNode!.Local); + Assert.NotNull(cluster.Nodes); + Assert.Equal(2, cluster.Nodes!.Count); } [Fact] @@ -132,8 +131,8 @@ public async Task GetAdminGroupPermissionsAsync_ReturnsPermissions() var permList = permissions.ToList(); Assert.NotEmpty(permList); Assert.Equal(2, permList.Count); - Assert.Equal("administrators", permList[0].Group.Name); - Assert.Equal("developers", permList[1].Group.Name); + Assert.Equal("administrators", permList[0].Group!.Name); + Assert.Equal("developers", permList[1].Group!.Name); } [Fact] @@ -148,7 +147,7 @@ public async Task GetAdminUserPermissionsAsync_ReturnsPermissions() var permList = permissions.ToList(); Assert.NotEmpty(permList); Assert.Equal(2, permList.Count); - Assert.Equal("admin", permList[0].User.Name); - Assert.Equal("jsmith", permList[1].User.Name); + Assert.Equal("admin", permList[0].User!.Name); + Assert.Equal("jsmith", permList[1].User!.Name); } } \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/AdminPermissionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/AdminPermissionsMockTests.cs index 89060bb..9d3953f 100644 --- a/test/Bitbucket.Net.Tests/MockTests/AdminPermissionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/AdminPermissionsMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/ApplicationPropertiesMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ApplicationPropertiesMockTests.cs index cb60a5c..9e93e58 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ApplicationPropertiesMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ApplicationPropertiesMockTests.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/AuditMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/AuditMockTests.cs index a8133f0..dd95269 100644 --- a/test/Bitbucket.Net.Tests/MockTests/AuditMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/AuditMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/BranchExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/BranchExtendedMockTests.cs index 615b026..4a8d8e6 100644 --- a/test/Bitbucket.Net.Tests/MockTests/BranchExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/BranchExtendedMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/BranchMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/BranchMockTests.cs index f7db308..6a92523 100644 --- a/test/Bitbucket.Net.Tests/MockTests/BranchMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/BranchMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/BuildExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/BuildExtendedMockTests.cs index 6970eb3..ed0df34 100644 --- a/test/Bitbucket.Net.Tests/MockTests/BuildExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/BuildExtendedMockTests.cs @@ -1,8 +1,4 @@ -using Bitbucket.Net.Models.Builds; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/BuildMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/BuildMockTests.cs index 0544561..59b6ba5 100644 --- a/test/Bitbucket.Net.Tests/MockTests/BuildMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/BuildMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Builds; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/ChangesAndFilesMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ChangesAndFilesMockTests.cs index fc8cf77..3e5378d 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ChangesAndFilesMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ChangesAndFilesMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/CommentLikesMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/CommentLikesMockTests.cs index a87532e..e9c5d60 100644 --- a/test/Bitbucket.Net.Tests/MockTests/CommentLikesMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/CommentLikesMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/CommitMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/CommitMockTests.cs index b77cb96..8522a0f 100644 --- a/test/Bitbucket.Net.Tests/MockTests/CommitMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/CommitMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/CoreExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/CoreExtendedMockTests.cs index aaa5cf4..65c25c4 100644 --- a/test/Bitbucket.Net.Tests/MockTests/CoreExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/CoreExtendedMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/DashboardMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DashboardMockTests.cs index 0b1ac86..d6fe92c 100644 --- a/test/Bitbucket.Net.Tests/MockTests/DashboardMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/DashboardMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; @@ -39,6 +37,6 @@ public async Task GetDashboardPullRequestSuggestionsAsync_ReturnsSuggestions() var suggestions = result.ToList(); Assert.Single(suggestions); Assert.NotNull(suggestions[0].FromRef); - Assert.Equal("feature/branch", suggestions[0].FromRef.DisplayId); + Assert.Equal("feature/branch", suggestions[0].FromRef!.DisplayId); } } \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersExtendedMockTests.cs index b7ca864..8558750 100644 --- a/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersExtendedMockTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.DefaultReviewers; using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersMockTests.cs index 74df521..8ce9910 100644 --- a/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/DefaultReviewersMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/DiffAndTagMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DiffAndTagMockTests.cs index 84b6892..ac33e84 100644 --- a/test/Bitbucket.Net.Tests/MockTests/DiffAndTagMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/DiffAndTagMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs index 104da16..eb3ff38 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ErrorHandlingMockTests.cs @@ -2,7 +2,6 @@ using Bitbucket.Net.Common.Models; using Bitbucket.Net.Tests.Infrastructure; using System.Net; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs index a8752dc..b17eb88 100644 --- a/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/GitMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/GitMockTests.cs index 4f0847d..b45f2de 100644 --- a/test/Bitbucket.Net.Tests/MockTests/GitMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/GitMockTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.Git; using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/GroupsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/GroupsMockTests.cs index b7bf1f2..8ec8b2b 100644 --- a/test/Bitbucket.Net.Tests/MockTests/GroupsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/GroupsMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/HooksMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/HooksMockTests.cs index a9cd18e..a07135a 100644 --- a/test/Bitbucket.Net.Tests/MockTests/HooksMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/HooksMockTests.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/InboxMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/InboxMockTests.cs index e636875..786e203 100644 --- a/test/Bitbucket.Net.Tests/MockTests/InboxMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/InboxMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/JiraMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/JiraMockTests.cs index 5c39c93..853cfe5 100644 --- a/test/Bitbucket.Net.Tests/MockTests/JiraMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/JiraMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; @@ -55,7 +53,7 @@ public async Task GetChangeSetsAsync_ReturnsChangeSets() var changeSets = result.ToList(); Assert.Single(changeSets); Assert.NotNull(changeSets[0].ToCommit); - Assert.Equal("def456abc789", changeSets[0].ToCommit.Id); + Assert.Equal("def456abc789", changeSets[0].ToCommit!.Id); } private const string CommentId = "100"; diff --git a/test/Bitbucket.Net.Tests/MockTests/LogsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/LogsMockTests.cs index 18caa6e..cfe5f15 100644 --- a/test/Bitbucket.Net.Tests/MockTests/LogsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/LogsMockTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.Core.Logs; using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/MarkupMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/MarkupMockTests.cs index 8d1bccf..bd3ba2f 100644 --- a/test/Bitbucket.Net.Tests/MockTests/MarkupMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/MarkupMockTests.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/PersonalAccessTokensMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PersonalAccessTokensMockTests.cs index d44e916..2078788 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PersonalAccessTokensMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PersonalAccessTokensMockTests.cs @@ -1,9 +1,6 @@ using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Models.PersonalAccessTokens; using Bitbucket.Net.Tests.Infrastructure; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/ProfileMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProfileMockTests.cs index eb1e341..0091453 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProfileMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProfileMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/ProjectCrudMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProjectCrudMockTests.cs index b97695c..3a2ac0b 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProjectCrudMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProjectCrudMockTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/ProjectMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProjectMockTests.cs index c0264d8..01fae1f 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProjectMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProjectMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/ProjectPermissionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProjectPermissionsMockTests.cs index 05bda12..a29a22a 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProjectPermissionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProjectPermissionsMockTests.cs @@ -1,8 +1,5 @@ using Bitbucket.Net.Models.Core.Admin; -using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs index 4526a24..53fa007 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs @@ -1,8 +1,5 @@ using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Tests.Infrastructure; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestActionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestActionsMockTests.cs index 23617f7..a81c561 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestActionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestActionsMockTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestBlockerMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestBlockerMockTests.cs index 986b8f9..8aa5221 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestBlockerMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestBlockerMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestCommentMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestCommentMockTests.cs index 16e2300..6d0d2fa 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestCommentMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestCommentMockTests.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestCrudMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestCrudMockTests.cs index e816a1d..421a183 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestCrudMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestCrudMockTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestExtendedMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestExtendedMockTests.cs index add9aef..a09f417 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestExtendedMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestExtendedMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestMockTests.cs index 39b8621..2407fc1 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestParticipantsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestParticipantsMockTests.cs index 343cb56..c1a541f 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestParticipantsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestParticipantsMockTests.cs @@ -1,8 +1,6 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Core.Users; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/PullRequestWatchMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/PullRequestWatchMockTests.cs index d753909..610769f 100644 --- a/test/Bitbucket.Net.Tests/MockTests/PullRequestWatchMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/PullRequestWatchMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/RefRestrictionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RefRestrictionsMockTests.cs index 4cc7f1a..b007765 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RefRestrictionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RefRestrictionsMockTests.cs @@ -1,8 +1,6 @@ using Bitbucket.Net.Models.DefaultReviewers; using Bitbucket.Net.Models.RefRestrictions; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/RefSyncMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RefSyncMockTests.cs index 9e76b18..31b086e 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RefSyncMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RefSyncMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.RefSync; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/RepositoryCrudMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RepositoryCrudMockTests.cs index 3ed692b..b976e99 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RepositoryCrudMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RepositoryCrudMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/RepositoryMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RepositoryMockTests.cs index 02707f3..4cb53ef 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RepositoryMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RepositoryMockTests.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/RepositoryOperationsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RepositoryOperationsMockTests.cs index 2925edf..8d0d993 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RepositoryOperationsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RepositoryOperationsMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/RepositoryPermissionsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/RepositoryPermissionsMockTests.cs index bd5a099..95dd421 100644 --- a/test/Bitbucket.Net.Tests/MockTests/RepositoryPermissionsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/RepositoryPermissionsMockTests.cs @@ -1,8 +1,5 @@ using Bitbucket.Net.Models.Core.Admin; -using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/SshKeyMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/SshKeyMockTests.cs index 15c6cb3..e0ca529 100644 --- a/test/Bitbucket.Net.Tests/MockTests/SshKeyMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/SshKeyMockTests.cs @@ -1,7 +1,5 @@ using Bitbucket.Net.Models.Core.Admin; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/TasksMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/TasksMockTests.cs index 24ae724..672b89a 100644 --- a/test/Bitbucket.Net.Tests/MockTests/TasksMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/TasksMockTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.Core.Tasks; using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/UserMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/UserMockTests.cs index 903220e..f1cc626 100644 --- a/test/Bitbucket.Net.Tests/MockTests/UserMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/UserMockTests.cs @@ -1,9 +1,6 @@ #nullable enable using Bitbucket.Net.Tests.Infrastructure; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/UsersMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/UsersMockTests.cs index 9886ad0..4348ac1 100644 --- a/test/Bitbucket.Net.Tests/MockTests/UsersMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/UsersMockTests.cs @@ -2,9 +2,6 @@ using Bitbucket.Net.Models.Core.Users; using Bitbucket.Net.Tests.Infrastructure; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/WebhookAndCompareMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/WebhookAndCompareMockTests.cs index a623280..caaf49c 100644 --- a/test/Bitbucket.Net.Tests/MockTests/WebhookAndCompareMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/WebhookAndCompareMockTests.cs @@ -1,7 +1,4 @@ -using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/WebhookMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/WebhookMockTests.cs index 6aa0882..df64309 100644 --- a/test/Bitbucket.Net.Tests/MockTests/WebhookMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/WebhookMockTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/MockTests/WhoAmIMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/WhoAmIMockTests.cs index 9623636..97890a7 100644 --- a/test/Bitbucket.Net.Tests/MockTests/WhoAmIMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/WhoAmIMockTests.cs @@ -1,5 +1,4 @@ using Bitbucket.Net.Tests.Infrastructure; -using System.Threading.Tasks; using Xunit; namespace Bitbucket.Net.Tests.MockTests; diff --git a/test/Bitbucket.Net.Tests/UnitTests/BitbucketClientConstructorTests.cs b/test/Bitbucket.Net.Tests/UnitTests/BitbucketClientConstructorTests.cs index 90ea6a9..0009687 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/BitbucketClientConstructorTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/BitbucketClientConstructorTests.cs @@ -1,8 +1,6 @@ #nullable enable using Flurl.Http; -using System; -using System.Net.Http; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; diff --git a/test/Bitbucket.Net.Tests/UnitTests/BitbucketHelpersTests.cs b/test/Bitbucket.Net.Tests/UnitTests/BitbucketHelpersTests.cs index ddc8702..3b72b29 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/BitbucketHelpersTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/BitbucketHelpersTests.cs @@ -6,7 +6,6 @@ using Bitbucket.Net.Models.Git; using Bitbucket.Net.Models.RefRestrictions; using Bitbucket.Net.Models.RefSync; -using System; using Xunit; namespace Bitbucket.Net.Tests.UnitTests; diff --git a/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs b/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs index ab8c9d9..de706e5 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs @@ -3,8 +3,6 @@ using Bitbucket.Net.Common; using Bitbucket.Net.Common.Models; using Bitbucket.Net.Serialization; -using System; -using System.Collections.Generic; using System.Text.Json; using Xunit; diff --git a/test/Bitbucket.Net.Tests/UnitTests/DiffStreamingExtensionsTests.cs b/test/Bitbucket.Net.Tests/UnitTests/DiffStreamingExtensionsTests.cs index e3ef054..e980557 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/DiffStreamingExtensionsTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/DiffStreamingExtensionsTests.cs @@ -2,10 +2,8 @@ using Bitbucket.Net.Common.Mcp; using Bitbucket.Net.Models.Core.Projects; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Xunit; +using ProjectPath = Bitbucket.Net.Models.Core.Projects.Path; namespace Bitbucket.Net.Tests.UnitTests; @@ -239,8 +237,8 @@ private static Diff CreateDiffWithLines(int lineCount) { return new Diff { - Source = new Path { Name = "test.cs" }, - Destination = new Path { Name = "test.cs" }, + Source = new ProjectPath { Name = "test.cs" }, + Destination = new ProjectPath { Name = "test.cs" }, Hunks = [CreateHunkWithLines(lineCount)] }; } diff --git a/test/Bitbucket.Net.Tests/UnitTests/ExceptionTests.cs b/test/Bitbucket.Net.Tests/UnitTests/ExceptionTests.cs index 2644d17..f093539 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/ExceptionTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/ExceptionTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Common.Exceptions; using Bitbucket.Net.Common.Models; -using System.Collections.Generic; using System.Net; using Xunit; diff --git a/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs b/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs index f48e8d9..f9afa7e 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs @@ -1,6 +1,5 @@ using Bitbucket.Net.Common.Converters; using Bitbucket.Net.Models.Core.Projects; -using System; using System.Text.Json; using Xunit; diff --git a/test/Bitbucket.Net.Tests/UnitTests/ModelSerializationTests.cs b/test/Bitbucket.Net.Tests/UnitTests/ModelSerializationTests.cs index 287a057..3536e2a 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/ModelSerializationTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/ModelSerializationTests.cs @@ -5,8 +5,6 @@ using Bitbucket.Net.Models.Core.Projects; using Bitbucket.Net.Models.Core.Users; using Bitbucket.Net.Serialization; -using System; -using System.Collections.Generic; using System.Text.Json; using Xunit; From 5b19662e91c2725a5068c6fbdf4b0b08cc5cc578 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 22:52:30 +0000 Subject: [PATCH 27/50] refactor: update ToString methods to handle null values gracefully --- src/Bitbucket.Net/Models/Core/Projects/Branch.cs | 2 +- src/Bitbucket.Net/Models/Core/Projects/Link.cs | 2 +- src/Bitbucket.Net/Models/Core/Projects/Participant.cs | 2 +- src/Bitbucket.Net/Models/Core/Projects/Project.cs | 2 +- src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs | 4 +--- src/Bitbucket.Net/Models/Core/Projects/Repository.cs | 2 +- src/Bitbucket.Net/Models/Core/Users/Named.cs | 2 +- src/Bitbucket.Net/Models/Core/Users/User.cs | 2 +- 8 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs index e60ab1a..b0b05e4 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs @@ -68,5 +68,5 @@ public BranchMetaData? BranchMetadata [JsonPropertyName("metadata")] public JsonElement? Metadata { get; set; } - public override string ToString() => DisplayId; + public override string ToString() => DisplayId ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Link.cs b/src/Bitbucket.Net/Models/Core/Projects/Link.cs index da1b6fc..51d7bef 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Link.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Link.cs @@ -4,5 +4,5 @@ public class Link { public string? Href { get; set; } - public override string ToString() => Href; + public override string ToString() => Href ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs index 916a744..ce68d16 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs @@ -13,5 +13,5 @@ public class Participant [JsonConverter(typeof(ParticipantStatusConverter))] public ParticipantStatus Status { get; set; } - public override string ToString() => User.DisplayName; + public override string ToString() => User?.DisplayName ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Project.cs b/src/Bitbucket.Net/Models/Core/Projects/Project.cs index de8308b..3a1b36a 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Project.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Project.cs @@ -7,5 +7,5 @@ public class Project : ProjectDefinition public string? Type { get; set; } public Links? Links { get; set; } - public override string ToString() => Name; + public override string ToString() => Name ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs index 4869439..3f43aca 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs @@ -1,6 +1,4 @@ using Bitbucket.Net.Common.Converters; -using System; -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Bitbucket.Net.Models.Core.Projects; @@ -17,5 +15,5 @@ public class PullRequest : PullRequestInfo public List? Participants { get; set; } public Links? Links { get; set; } - public override string ToString() => $"{Author.User.DisplayName}: {Title ?? "(untitled)"}"; + public override string ToString() => $"{Author?.User?.DisplayName ?? "(unknown)"}: {Title ?? "(untitled)"}"; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Repository.cs b/src/Bitbucket.Net/Models/Core/Projects/Repository.cs index 36eb550..0a46f93 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Repository.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Repository.cs @@ -10,5 +10,5 @@ public class Repository : RepositoryRef public bool Public { get; set; } public CloneLinks? Links { get; set; } - public override string ToString() => Name; + public override string ToString() => Name ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Users/Named.cs b/src/Bitbucket.Net/Models/Core/Users/Named.cs index 51250fc..fb5b069 100644 --- a/src/Bitbucket.Net/Models/Core/Users/Named.cs +++ b/src/Bitbucket.Net/Models/Core/Users/Named.cs @@ -4,5 +4,5 @@ public class Named { public string? Name { get; set; } - public override string ToString() => Name; + public override string ToString() => Name ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Users/User.cs b/src/Bitbucket.Net/Models/Core/Users/User.cs index 00564ef..f146118 100644 --- a/src/Bitbucket.Net/Models/Core/Users/User.cs +++ b/src/Bitbucket.Net/Models/Core/Users/User.cs @@ -9,5 +9,5 @@ public class User : Identity public string? Type { get; set; } public string? AvatarUrl { get; set; } - public override string ToString() => DisplayName; + public override string ToString() => DisplayName ?? string.Empty; } \ No newline at end of file From b126cbaf0c3ffb7d0df85b3140bbe7134d561404 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 22:55:59 +0000 Subject: [PATCH 28/50] ci: enhance test coverage reporting and improve publish workflow --- .github/workflows/ci.yml | 12 +++++++++++- .github/workflows/publish.yml | 9 ++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 531d6a5..451b222 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,4 +38,14 @@ jobs: run: dotnet build --no-restore -c Release - name: Test - run: dotnet test --no-build -c Release --verbosity normal + run: > + dotnet test --no-build -c Release --verbosity normal + --collect:"XPlat Code Coverage" + --results-directory ./coverage + + - name: Upload coverage + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + uses: codecov/codecov-action@v4 + with: + directory: ./coverage + fail_ci_if_error: false diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 891aeef..82abe25 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -42,7 +42,6 @@ jobs: - name: Test run: dotnet test --no-build -c Release --verbosity normal - # Extract version from tag (v0.1.0-beta.1 -> 0.1.0-beta.1) - name: Extract version from tag id: version run: echo "VERSION=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT" @@ -51,7 +50,11 @@ jobs: run: dotnet pack src/Bitbucket.Net/Bitbucket.Net.csproj -c Release --no-build -o ./artifacts -p:PackageVersion=${{ steps.version.outputs.VERSION }} - name: Push to NuGet.org - run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate + run: | + dotnet nuget push ./artifacts/*.nupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate + dotnet nuget push ./artifacts/*.snupkg --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate - name: Push to GitHub Packages - run: dotnet nuget push ./artifacts/*.nupkg --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/diomonogatari/index.json --skip-duplicate + run: | + dotnet nuget push ./artifacts/*.nupkg --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/diomonogatari/index.json --skip-duplicate + dotnet nuget push ./artifacts/*.snupkg --api-key ${{ secrets.GITHUB_TOKEN }} --source https://nuget.pkg.github.com/diomonogatari/index.json --skip-duplicate From 5e1ea52b004f990f0b1885948ef131f325f2e1db Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:19:23 +0000 Subject: [PATCH 29/50] refactor: remove inheritance from PullRequestInfo in Comment class --- src/Bitbucket.Net/Models/Core/Projects/Comment.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs index f374dcd..4d5eb84 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs @@ -5,7 +5,7 @@ namespace Bitbucket.Net.Models.Core.Projects; -public class Comment : PullRequestInfo +public class Comment { public int Id { get; set; } public int Version { get; set; } @@ -15,11 +15,7 @@ public class Comment : PullRequestInfo /// Bitbucket Server comment state. /// Common values: OPEN, PENDING, RESOLVED. /// - /// - /// This intentionally hides . Although inheriting from - /// is not ideal for a comment model, using the same CLR member name avoids System.Text.Json property-name collisions. - /// - public new string? State { get; set; } + public string? State { get; set; } /// /// Indicates whether the whole comment thread is resolved. From 2c7ae388311f75748ea01b540d021e9df54223dc Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:20:19 +0000 Subject: [PATCH 30/50] refactor: remove commented-out Avatar property from ProjectDefinition class --- src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs b/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs index 3dc5785..b882e9a 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs @@ -4,5 +4,4 @@ public class ProjectDefinition : ProjectRef { public string? Name { get; set; } public string? Description { get; set; } - //public string Avatar { get; set; } } \ No newline at end of file From c3d7d2294afa384fcff63fe00b7cf1bae51180e0 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:21:34 +0000 Subject: [PATCH 31/50] refactor: update ToString methods to return "Unknown" for null values --- src/Bitbucket.Net/Core/Repos/BitbucketClient.cs | 5 +---- src/Bitbucket.Net/Models/Core/Projects/Participant.cs | 2 +- src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs index 02cd6f9..10ea955 100644 --- a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs @@ -64,10 +64,7 @@ public async Task> GetRepositoriesAsync( } /// - /// Streams all repositories as an IAsyncEnumerable, yielding items as they are retrieved. - /// - /// - /// Streams repositories accessible to the current user. + /// Streams all repositories accessible to the current user, yielding items as they are retrieved. /// /// Optional maximum number of pages to retrieve. /// Optional page size. diff --git a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs index ce68d16..0e63c5b 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs @@ -13,5 +13,5 @@ public class Participant [JsonConverter(typeof(ParticipantStatusConverter))] public ParticipantStatus Status { get; set; } - public override string ToString() => User?.DisplayName ?? string.Empty; + public override string ToString() => User?.DisplayName ?? "Unknown"; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs index 3f43aca..ef6260e 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs @@ -15,5 +15,5 @@ public class PullRequest : PullRequestInfo public List? Participants { get; set; } public Links? Links { get; set; } - public override string ToString() => $"{Author?.User?.DisplayName ?? "(unknown)"}: {Title ?? "(untitled)"}"; + public override string ToString() => $"{Author?.User?.DisplayName ?? "Unknown"}: {Title ?? "(untitled)"}"; } \ No newline at end of file From c0a0a51751a32e97ee14bf74f08066e963f3e01e Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sat, 7 Feb 2026 23:22:27 +0000 Subject: [PATCH 32/50] chore: update CHANGELOG with breaking changes, fixes, and enhancements --- CHANGELOG.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 59cc093..360a197 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Breaking Changes + +- `Comment` no longer inherits from `PullRequestInfo`. Properties such as `Title`, `Description`, `FromRef`, `ToRef`, `Locked`, and `Reviewers` are no longer available on `Comment` objects. These properties were always null/default on comments and should not have been exposed. + +### Fixed + +- `PullRequest.ToString()` and `Participant.ToString()` now return `"Unknown"` instead of throwing `NullReferenceException` when `Author`/`User` is null. + +### Changed + +- Removed commented-out `Avatar` property from `ProjectDefinition`. +- Fixed duplicate `` XML doc tag on `GetRepositoriesStreamAsync`. + ## [0.1.0-beta.1] - 2026-02-06 (pre-release) ### Notes @@ -35,11 +48,13 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. ### Added #### CancellationToken Support + - All async methods now accept an optional `CancellationToken` parameter - Enables graceful cancellation of long-running operations - Fully propagated to underlying HTTP calls #### IAsyncEnumerable Streaming + - New streaming variants for paginated endpoints that yield items as they arrive: - `GetProjectsStreamAsync()` - `GetProjectRepositoriesStreamAsync()` @@ -54,6 +69,7 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. - Native `await foreach` support #### Diff and File Content Streaming + - New streaming methods for large diff responses: - `GetCommitDiffStreamAsync()` - Stream diffs for a specific commit - `GetRepositoryDiffStreamAsync()` - Stream repository diffs between refs @@ -68,6 +84,7 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. - Reduced memory pressure for large file downloads #### Dependency Injection Support + - New constructor: `BitbucketClient(HttpClient httpClient, string baseUrl, Func getToken = null)` - Enables use with `IHttpClientFactory` - Supports Polly resilience policies (retry, circuit breaker, etc.) @@ -77,6 +94,7 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. - Supports `IFlurlClientCache` for named client management #### Typed Exception Hierarchy + - New `BitbucketApiException` base class with rich error information: - `StatusCode`: HTTP status code as `HttpStatusCode` enum - `Context`: The field or resource that caused the error @@ -93,12 +111,14 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. - `BitbucketServerException` (HTTP 5xx) #### Code Quality Enforcement + - Added `Meziantou.Analyzer` to enforce library best practices - ConfigureAwait(false) requirement enforced via MA0004 (warning level) - Nullable reference types enabled project-wide - EditorConfig configured with library-appropriate analyzer rules #### Performance Benchmarks + - New benchmark project (`benchmarks/Bitbucket.Net.Benchmarks`) using BenchmarkDotNet - Benchmark categories: - **JSON Serialization**: Measure System.Text.Json performance for serialization/deserialization @@ -126,6 +146,7 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. #### Updating from 1.x to 2.0.0 1. **Update Target Framework** + ```xml netstandard1.4 @@ -137,6 +158,7 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. 2. **No Code Changes Required** for basic usage - the API remains backward compatible 3. **Optional: Use CancellationToken** + ```csharp // Before var projects = await client.GetProjectsAsync(); @@ -147,6 +169,7 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. ``` 4. **Optional: Use Streaming for Large Results** + ```csharp // Before - buffers all results in memory var allPRs = await client.GetPullRequestsAsync("PROJ", "repo"); @@ -159,6 +182,7 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. ``` 5. **Optional: Use Dependency Injection** + ```csharp // Configure with IHttpClientFactory + Polly services.AddHttpClient() @@ -173,6 +197,7 @@ shipped up to 0.5.0 on NuGet; this fork is versioned independently. ``` 6. **Update Exception Handling** (Breaking Change) + ```csharp // Before - catching generic InvalidOperationException try From e223cc59da45aa5c52c6b7851964041e5615e8c7 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 00:01:21 +0000 Subject: [PATCH 33/50] feat: add streaming methods for pull requests, changes, and comments in Dashboard, Inbox and Projects clients --- .../Core/Dashboard/BitbucketClient.cs | 49 ++- .../Core/Inbox/BitbucketClient.cs | 37 +- .../Core/Projects/BitbucketClient.cs | 391 ++++++++++++++++++ 3 files changed, 474 insertions(+), 3 deletions(-) diff --git a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs index 69921b2..3b5a713 100644 --- a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs @@ -49,7 +49,7 @@ public async Task> GetDashboardPullRequestsAsync(PullRe CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) +(StringComparer.Ordinal) { ["state"] = BitbucketHelpers.PullRequestStateToString(state), ["role"] = BitbucketHelpers.RoleToString(role), @@ -72,6 +72,51 @@ public async Task> GetDashboardPullRequestsAsync(PullRe .ConfigureAwait(false); } + /// + /// Streams pull requests for the current user's dashboard, yielding items as they are retrieved. + /// + /// Optional pull request state filter. + /// Optional participant role filter. + /// Optional participant status filters. + /// Optional sort order. + /// Optional filter for recently closed PRs (seconds). + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of pull requests. + public IAsyncEnumerable GetDashboardPullRequestsStreamAsync(PullRequestStates? state = null, + Roles? role = null, + List? status = null, + PullRequestOrders? order = PullRequestOrders.Newest, + int? closedSinceSeconds = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["state"] = BitbucketHelpers.PullRequestStateToString(state), + ["role"] = BitbucketHelpers.RoleToString(role), + ["status"] = status is not null ? string.Join(',', status.Select(BitbucketHelpers.ParticipantStatusToString)) : null, + ["order"] = BitbucketHelpers.PullRequestOrderToString(order), + ["closedSince"] = closedSinceSeconds, + ["limit"] = limit, + ["start"] = start, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetDashboardUrl("/pull-requests") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Retrieves pull request suggestions for the current user. /// @@ -88,7 +133,7 @@ public async Task> GetDashboardPullRequestSug CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) +(StringComparer.Ordinal) { ["changesSince"] = changesSinceSeconds, ["limit"] = limit, diff --git a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs index 3b6aed7..8690108 100644 --- a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs @@ -43,7 +43,7 @@ public async Task> GetInboxPullRequestsAsync( CancellationToken cancellationToken = default) { var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) +(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -62,6 +62,41 @@ public async Task> GetInboxPullRequestsAsync( .ConfigureAwait(false); } + /// + /// Streams pull requests from the user's inbox, yielding items as they are retrieved. + /// + /// Optional maximum number of pages to retrieve. + /// Optional page size (default 25). + /// Optional starting index for pagination. + /// The participant role filter (default reviewer). + /// Token to cancel the operation. + /// An async enumerable of pull requests. + public IAsyncEnumerable GetInboxPullRequestsStreamAsync( + int? maxPages = null, + int? limit = 25, + int? start = 0, + Roles role = Roles.Reviewer, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["role"] = BitbucketHelpers.RoleToString(role), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetInboxUrl("/pull-requests") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Retrieves the count of pull requests in the user's inbox. /// diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs index 372e24b..2d9f29e 100644 --- a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs @@ -1411,6 +1411,44 @@ public async Task> GetChangesAsync(string projectKey, string .ConfigureAwait(false); } + /// + /// Streams changes for a repository between refs, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The target ref. + /// Optional starting ref. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of changes. + public IAsyncEnumerable GetChangesStreamAsync(string projectKey, string repositorySlug, string until, string? since = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["since"] = since, + ["until"] = until, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Retrieves commits for a repository. /// @@ -1575,6 +1613,47 @@ public async Task> GetCommitChangesAsync(string projectKey, .ConfigureAwait(false); } + /// + /// Streams changes for a specific commit, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Optional starting commit ID. + /// Whether to include comment counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of changes. + public IAsyncEnumerable GetCommitChangesStreamAsync(string projectKey, string repositorySlug, string commitId, + string? since = null, + bool withComments = true, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["since"] = since, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Retrieves comments for a commit. /// @@ -2404,6 +2483,50 @@ public async Task> GetPullRequestActivitiesAsyn .ConfigureAwait(false); } + /// + /// Streams activities for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional starting activity ID. + /// Optional from type filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// An async enumerable of pull request activities. + public IAsyncEnumerable GetPullRequestActivitiesStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + long? fromId = null, + PullRequestFromTypes? fromType = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["fromId"] = fromId, + ["fromType"] = BitbucketHelpers.PullRequestFromTypeToString(fromType), + ["avatarSize"] = avatarSize, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/activities") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Declines a pull request. /// @@ -2616,6 +2739,53 @@ public async Task> GetPullRequestChangesAsync(string project .ConfigureAwait(false); } + /// + /// Streams changes for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The change scope filter. + /// Optional since commit ID. + /// Optional until commit ID. + /// Whether to include comment counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of changes. + public IAsyncEnumerable GetPullRequestChangesStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + ChangeScopes changeScope = ChangeScopes.All, + string? sinceId = null, + string? untilId = null, + bool withComments = true, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["changeScope"] = BitbucketHelpers.ChangeScopeToString(changeScope), + ["sinceId"] = sinceId, + ["untilId"] = untilId, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Creates a comment on a pull request. /// @@ -2749,6 +2919,59 @@ public async Task> GetPullRequestCommentsAsync(string pr .ConfigureAwait(false); } + /// + /// Streams comments for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The file path to filter comments for. + /// The anchor state filter. + /// The diff type filter. + /// Optional from commit hash. + /// Optional to commit hash. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// An async enumerable of comment references. + public IAsyncEnumerable GetPullRequestCommentsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + string path, + AnchorStates anchorState = AnchorStates.Active, + DiffTypes diffType = DiffTypes.Effective, + string? fromHash = null, + string? toHash = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + ["path"] = path, + ["anchorState"] = BitbucketHelpers.AnchorStateToString(anchorState), + ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), + ["fromHash"] = fromHash, + ["toHash"] = toHash, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/comments") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Retrieves a single pull request comment by ID. /// @@ -3109,6 +3332,45 @@ public async Task> GetPullRequestParticipantsAsync(stri .ConfigureAwait(false); } + /// + /// Streams participants for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// An async enumerable of participants. + public IAsyncEnumerable GetPullRequestParticipantsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Assigns a role to a user in a pull request. /// @@ -3260,6 +3522,53 @@ public async Task> GetPullRequestTasksAsync(string pr .ConfigureAwait(false); } + /// + /// Streams tasks for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// An async enumerable of tasks. + /// + /// + /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and returns 404 Not Found on servers version 9.0+. + /// + /// + /// For Bitbucket Server 9.0+, use instead. + /// + /// + [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsStreamAsync for 9.0+ compatibility.")] + public IAsyncEnumerable GetPullRequestTasksStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/tasks") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Gets the task count for a pull request using the legacy tasks endpoint. /// @@ -3341,6 +3650,48 @@ public async Task> GetPullRequestBlockerCommentsAsyn .ConfigureAwait(false); } + /// + /// Streams blocker comments for a pull request, yielding items as they are retrieved. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional blocker comment state filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of blocker comments. + public IAsyncEnumerable GetPullRequestBlockerCommentsStreamAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + BlockerCommentState? state = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["state"] = BitbucketHelpers.BlockerCommentStateToString(state), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/blocker-comments") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Gets a single blocker comment by ID. /// This endpoint is available in Bitbucket Server 9.0+. @@ -3912,6 +4263,46 @@ public async Task> GetProjectRepositoryTagsAsync(string project .ConfigureAwait(false); } + /// + /// Streams tags for a repository, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// Filter text for tag names. + /// Ordering option. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of tags. + public IAsyncEnumerable GetProjectRepositoryTagsStreamAsync(string projectKey, string repositorySlug, + string filterText, + BranchOrderBy orderBy, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary +(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filterText"] = filterText, + ["orderBy"] = BitbucketHelpers.BranchOrderByToString(orderBy), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + /// /// Creates a tag in a repository. /// From 1186734872e0057a2119dbc197dd8d238846a317 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 00:12:48 +0000 Subject: [PATCH 34/50] chore: update editorconfig and vscode settings for consistent line length and word wrap --- .editorconfig | 1 + .vscode/settings.json | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.editorconfig b/.editorconfig index b2a57d4..e516371 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,7 @@ indent_size = 4 indent_style = space tab_width = 4 +max_line_length = 170 # New line preferences end_of_line = crlf diff --git a/.vscode/settings.json b/.vscode/settings.json index 8f89ae2..02f5b73 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,8 @@ "editor.formatOnSave": true, "editor.formatOnSaveMode": "modificationsIfAvailable", "editor.formatOnType": false, + "editor.rulers": [170], + "editor.wordWrapColumn": 170, "editor.codeActionsOnSave": { "source.organizeImports": "always", "source.removeUnusedImports": "always", From 35018ef0897103f658c78f766378f528fb9daec0 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 00:17:31 +0000 Subject: [PATCH 35/50] refactor: simplify namespace usage in tests and Branch class --- src/Bitbucket.Net/Models/Core/Projects/Branch.cs | 6 +++--- test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs | 2 +- .../MockTests/ProjectSettingsMockTests.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs index b0b05e4..965be2c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs @@ -47,15 +47,15 @@ public BranchMetaData? BranchMetadata var valueJson = valueElement.GetRawText(); - if (string.Equals(name, "com.atlassian.bitbucket.server.bitbucket-branch:ahead-behind-metadata-provider", System.StringComparison.Ordinal)) + if (string.Equals(name, "com.atlassian.bitbucket.server.bitbucket-branch:ahead-behind-metadata-provider", StringComparison.Ordinal)) { _branchMetadata.AheadBehind = JsonSerializer.Deserialize(valueJson, s_jsonOptions); } - else if (string.Equals(name, "com.atlassian.bitbucket.server.bitbucket-build:build-status-metadata", System.StringComparison.Ordinal)) + else if (string.Equals(name, "com.atlassian.bitbucket.server.bitbucket-build:build-status-metadata", StringComparison.Ordinal)) { _branchMetadata.BuildStatus = JsonSerializer.Deserialize(valueJson, s_jsonOptions); } - else if (string.Equals(name, "com.atlassian.bitbucket.server.bitbucket-ref-metadata:outgoing-pull-request-metadata", System.StringComparison.Ordinal)) + else if (string.Equals(name, "com.atlassian.bitbucket.server.bitbucket-ref-metadata:outgoing-pull-request-metadata", StringComparison.Ordinal)) { _branchMetadata.OutgoingPullRequest = JsonSerializer.Deserialize(valueJson, s_jsonOptions); } diff --git a/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs index b17eb88..e7a8b15 100644 --- a/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/GitAndTagMockTests.cs @@ -56,7 +56,7 @@ public async Task CreateTagAsync_CreatesTag() var tag = await client.CreateTagAsync( TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, - Bitbucket.Net.Models.Git.TagTypes.LightWeight, + Models.Git.TagTypes.LightWeight, "v1.0.0", "abc123"); diff --git a/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs index 53fa007..5801cbf 100644 --- a/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs +++ b/test/Bitbucket.Net.Tests/MockTests/ProjectSettingsMockTests.cs @@ -90,7 +90,7 @@ public async Task GetProjectRepositoryTagsAsync_ReturnsTags() TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, filterText: "", - orderBy: Bitbucket.Net.Models.Core.Projects.BranchOrderBy.Alphabetical); + orderBy: Models.Core.Projects.BranchOrderBy.Alphabetical); Assert.NotNull(result); var tags = result.ToList(); From 14e7baebd820fc0610a0aec5dd6a1e5d4c8f967d Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 00:34:58 +0000 Subject: [PATCH 36/50] refactor: use StringComparer.Ordinal for dictionary initialization --- src/Bitbucket.Net/Audit/BitbucketClient.cs | 2 +- src/Bitbucket.Net/Branches/BitbucketClient.cs | 3 +- src/Bitbucket.Net/Builds/BitbucketClient.cs | 3 +- .../CommentLikes/BitbucketClient.cs | 6 +- .../Core/Admin/BitbucketClient.cs | 39 +-- .../Core/Dashboard/BitbucketClient.cs | 6 +- .../Core/Groups/BitbucketClient.cs | 3 +- .../Core/Inbox/BitbucketClient.cs | 6 +- .../Core/Markup/BitbucketClient.cs | 3 +- .../Core/Profile/BitbucketClient.cs | 3 +- .../Core/Projects/BitbucketClient.cs | 225 ++++++------------ .../Core/Repos/BitbucketClient.cs | 6 +- .../Core/Users/BitbucketClient.cs | 3 +- .../DefaultReviewers/BitbucketClient.cs | 3 +- src/Bitbucket.Net/Jira/BitbucketClient.cs | 3 +- .../PersonalAccessTokens/BitbucketClient.cs | 3 +- .../RefRestrictions/BitbucketClient.cs | 6 +- src/Bitbucket.Net/Ssh/BitbucketClient.cs | 15 +- 18 files changed, 113 insertions(+), 225 deletions(-) diff --git a/src/Bitbucket.Net/Audit/BitbucketClient.cs b/src/Bitbucket.Net/Audit/BitbucketClient.cs index 03de159..ee74f26 100644 --- a/src/Bitbucket.Net/Audit/BitbucketClient.cs +++ b/src/Bitbucket.Net/Audit/BitbucketClient.cs @@ -75,7 +75,7 @@ public async Task> GetProjectRepoAuditEventsAsync(string int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/Branches/BitbucketClient.cs b/src/Bitbucket.Net/Branches/BitbucketClient.cs index c4ad2ec..e7fdb11 100644 --- a/src/Bitbucket.Net/Branches/BitbucketClient.cs +++ b/src/Bitbucket.Net/Branches/BitbucketClient.cs @@ -44,8 +44,7 @@ public async Task> GetCommitBranchInfoAsync(string proje int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/Builds/BitbucketClient.cs b/src/Bitbucket.Net/Builds/BitbucketClient.cs index 7e0ad0e..591fabc 100644 --- a/src/Bitbucket.Net/Builds/BitbucketClient.cs +++ b/src/Bitbucket.Net/Builds/BitbucketClient.cs @@ -81,8 +81,7 @@ public async Task> GetBuildStatusForCommitAsync(string int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs index 2d0f22c..e86fee7 100644 --- a/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs +++ b/src/Bitbucket.Net/CommentLikes/BitbucketClient.cs @@ -44,8 +44,7 @@ public async Task> GetCommitCommentLikesAsync(string projectKe int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -118,8 +117,7 @@ public async Task> GetPullRequestCommentLikesAsync(string proj int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs index a96e670..58a26c7 100644 --- a/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Admin/BitbucketClient.cs @@ -42,8 +42,7 @@ public async Task> GetAdminGroupsAsync(string? int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -127,8 +126,7 @@ public async Task> GetAdminGroupMoreMembersAsync(string co int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -167,8 +165,7 @@ public async Task> GetAdminGroupMoreNonMembersAsync(string int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -206,8 +203,7 @@ public async Task> GetAdminUsersAsync(string? filter = nul int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -241,8 +237,7 @@ public async Task> GetAdminUsersAsync(string? filter = nul public async Task CreateAdminUserAsync(string name, string password, string displayName, string emailAddress, bool addToDefaultGroup = true, string notify = "false", CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["name"] = name, ["password"] = password, @@ -362,8 +357,7 @@ public async Task> GetAdminUserMoreMembersAsyn int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -399,8 +393,7 @@ public async Task> GetAdminUserMoreNonMembersA int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -603,8 +596,7 @@ public async Task> GetAdminGroupPermissionsAsync(st int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -632,8 +624,7 @@ public async Task> GetAdminGroupPermissionsAsync(st /// true if the update succeeded; otherwise, false. public async Task UpdateAdminGroupPermissionsAsync(Permissions permission, string name, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["permission"] = permission, ["name"] = name, @@ -678,8 +669,7 @@ public async Task> GetAdminGroupPermissionsNon int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -715,8 +705,7 @@ public async Task> GetAdminUserPermissionsAsync(stri int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -745,8 +734,7 @@ public async Task> GetAdminUserPermissionsAsync(stri /// true if the update succeeded; otherwise, false. public async Task UpdateAdminUserPermissionsAsync(Permissions permission, string name, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["permission"] = permission, ["name"] = name, @@ -793,8 +781,7 @@ public async Task> GetAdminUserPermissionsNoneAsync(string? fi int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs index 3b5a713..3bf9725 100644 --- a/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Dashboard/BitbucketClient.cs @@ -48,8 +48,7 @@ public async Task> GetDashboardPullRequestsAsync(PullRe int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["state"] = BitbucketHelpers.PullRequestStateToString(state), ["role"] = BitbucketHelpers.RoleToString(role), @@ -132,8 +131,7 @@ public async Task> GetDashboardPullRequestSug int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["changesSince"] = changesSinceSeconds, ["limit"] = limit, diff --git a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs index 54b62e2..a44e41c 100644 --- a/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Groups/BitbucketClient.cs @@ -31,8 +31,7 @@ public async Task> GetGroupNamesAsync(string? filter = null, int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["filter"] = filter, ["limit"] = limit, diff --git a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs index 8690108..762e7bc 100644 --- a/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Inbox/BitbucketClient.cs @@ -42,8 +42,7 @@ public async Task> GetInboxPullRequestsAsync( Roles role = Roles.Reviewer, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -78,8 +77,7 @@ public IAsyncEnumerable GetInboxPullRequestsStreamAsync( Roles role = Roles.Reviewer, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs index 659c637..9e76bb6 100644 --- a/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Markup/BitbucketClient.cs @@ -39,8 +39,7 @@ public async Task PreviewMarkupAsync(string text, bool? htmlEscape = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["urlMode"] = urlMode, ["hardWrap"] = BitbucketHelpers.BoolToString(hardWrap), diff --git a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs index b9ad22a..313445c 100644 --- a/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Profile/BitbucketClient.cs @@ -41,8 +41,7 @@ public async Task> GetRecentReposAsync(Permissions? perm int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs index 2d9f29e..40f0820 100644 --- a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs @@ -76,8 +76,7 @@ public async Task> GetProjectsAsync( Permissions? permission = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -115,8 +114,7 @@ public IAsyncEnumerable GetProjectsStreamAsync( Permissions? permission = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -214,8 +212,7 @@ public async Task> GetProjectUserPermissionsAsync(st int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -244,8 +241,7 @@ public async Task> GetProjectUserPermissionsAsync(st /// true if removal succeeded; otherwise, false. public async Task DeleteProjectUserPermissionsAsync(string projectKey, string userName, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["name"] = userName, }; @@ -268,8 +264,7 @@ public async Task DeleteProjectUserPermissionsAsync(string projectKey, str /// true if the update succeeded; otherwise, false. public async Task UpdateProjectUserPermissionsAsync(string projectKey, string userName, Permissions permission, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["name"] = userName, ["permission"] = BitbucketHelpers.PermissionToString(permission), @@ -299,8 +294,7 @@ public async Task> GetProjectUserPermissionsNoneAsync( int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -335,8 +329,7 @@ public async Task> GetProjectGroupPermissionsAsync( int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -364,8 +357,7 @@ public async Task> GetProjectGroupPermissionsAsync( /// true if the group permissions were removed; otherwise, false. public async Task DeleteProjectGroupPermissionsAsync(string projectKey, string groupName, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["name"] = groupName, }; @@ -388,8 +380,7 @@ public async Task DeleteProjectGroupPermissionsAsync(string projectKey, st /// true if the update succeeded; otherwise, false. public async Task UpdateProjectGroupPermissionsAsync(string projectKey, string groupName, Permissions permission, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["name"] = groupName, ["permission"] = BitbucketHelpers.PermissionToString(permission), @@ -419,8 +410,7 @@ public async Task> GetProjectGroupPermissionsNoneAsync int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -462,8 +452,7 @@ public async Task IsProjectDefaultPermissionAsync(string projectKey, Permi private async Task SetProjectDefaultPermissionAsync(string projectKey, Permissions permission, bool allow, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["allow"] = BitbucketHelpers.BoolToString(allow), }; @@ -515,8 +504,7 @@ public async Task> GetProjectRepositoriesAsync(string pr int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -543,8 +531,7 @@ public IAsyncEnumerable GetProjectRepositoriesStreamAsync(string pro int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -691,8 +678,7 @@ public async Task> GetProjectRepositoryForksAsync(st int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -742,8 +728,7 @@ public async Task> GetRelatedProjectRepositoriesAsyn int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -781,8 +766,7 @@ public async Task GetProjectRepositoryArchiveAsync(string projectKey, st string prefix, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["at"] = at, ["fileName"] = fileName, @@ -818,8 +802,7 @@ public async Task> GetProjectRepositoryGroupPermiss int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["filter"] = filter, ["limit"] = limit, @@ -849,8 +832,7 @@ public async Task> GetProjectRepositoryGroupPermiss /// true if the update succeeded; otherwise, false. public async Task UpdateProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["permission"] = BitbucketHelpers.PermissionToString(permission), ["name"] = name, @@ -900,8 +882,7 @@ public async Task> GetProjectRepositoryGroupPe int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -940,8 +921,7 @@ public async Task> GetProjectRepositoryUserPermissio int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["filter"] = filter, ["limit"] = limit, @@ -972,8 +952,7 @@ public async Task> GetProjectRepositoryUserPermissio /// true if the update succeeded; otherwise, false. public async Task UpdateProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["permission"] = BitbucketHelpers.PermissionToString(permission), ["name"] = name, @@ -1027,8 +1006,7 @@ public async Task> GetProjectRepositoryUserPermissionsNoneAsyn int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1071,8 +1049,7 @@ public async Task> GetBranchesAsync(string projectKey, strin BranchOrderBy? orderBy = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1107,8 +1084,7 @@ public IAsyncEnumerable GetBranchesStreamAsync(string projectKey, string BranchOrderBy? orderBy = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1195,8 +1171,7 @@ public async Task BrowseProjectRepositoryAsync(string projectKey, st bool noContent = false, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["at"] = at, ["type"] = BitbucketHelpers.BoolToString(type), @@ -1235,8 +1210,7 @@ public async Task BrowseProjectRepositoryPathAsync(string projec bool noContent = false, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["at"] = at, ["type"] = BitbucketHelpers.BoolToString(type), @@ -1390,8 +1364,7 @@ public async Task> GetChangesAsync(string projectKey, string int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1429,8 +1402,7 @@ public IAsyncEnumerable GetChangesStreamAsync(string projectKey, string int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1479,8 +1451,7 @@ public async Task> GetCommitsAsync(string projectKey, string int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1521,8 +1492,7 @@ public IAsyncEnumerable GetCommitsStreamAsync(string projectKey, string int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1557,8 +1527,7 @@ public IAsyncEnumerable GetCommitsStreamAsync(string projectKey, string /// The requested commit. public async Task GetCommitAsync(string projectKey, string repositorySlug, string commitId, string? path = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["path"] = path, }; @@ -1592,8 +1561,7 @@ public async Task> GetCommitChangesAsync(string projectKey, int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1634,8 +1602,7 @@ public IAsyncEnumerable GetCommitChangesStreamAsync(string projectKey, s int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1675,8 +1642,7 @@ public async Task> GetCommitCommentsAsync(string projectKey int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1709,8 +1675,7 @@ public async Task> GetCommitCommentsAsync(string projectKey public async Task CreateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, CommentInfo commentInfo, string? since = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["since"] = since, }; @@ -1779,8 +1744,7 @@ public async Task DeleteCommitCommentAsync(string projectKey, string repos int version = -1, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["version"] = version, }; @@ -1816,8 +1780,7 @@ public async Task GetCommitDiffAsync(string projectKey, string repo bool withComments = true, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), ["contextLines"] = contextLines, @@ -1859,8 +1822,7 @@ public async IAsyncEnumerable GetCommitDiffStreamAsync(string projectKey, bool withComments = true, [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), ["contextLines"] = contextLines, @@ -1941,8 +1903,7 @@ public async Task> GetRepositoryCompareChangesAsync(string p int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -1983,8 +1944,7 @@ public async Task GetRepositoryCompareDiffAsync(string projectKey, string whitespace = "ignore-all", CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["from"] = from, ["to"] = to, @@ -2023,8 +1983,7 @@ public async IAsyncEnumerable GetRepositoryCompareDiffStreamAsync(string p string whitespace = "ignore-all", [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["from"] = from, ["to"] = to, @@ -2071,8 +2030,7 @@ public async Task> GetRepositoryCompareCommitsAsync(string p int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2112,8 +2070,7 @@ public async Task GetRepositoryDiffAsync(string projectKey, string string whitespace = "ignore-all", CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["contextLines"] = contextLines, ["since"] = since, @@ -2150,8 +2107,7 @@ public async IAsyncEnumerable GetRepositoryDiffStreamAsync(string projectK string whitespace = "ignore-all", [EnumeratorCancellation] CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["contextLines"] = contextLines, ["since"] = since, @@ -2194,8 +2150,7 @@ public async Task> GetRepositoryFilesAsync(string projectKey int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2254,8 +2209,7 @@ public async Task> GetRepositoryParticipantsAsync(string p int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2304,8 +2258,7 @@ public async Task> GetPullRequestsAsync(string projectK bool withProperties = true, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2344,8 +2297,7 @@ public IAsyncEnumerable GetPullRequestsStreamAsync(string projectKe bool withProperties = true, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2461,8 +2413,7 @@ public async Task> GetPullRequestActivitiesAsyn int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2506,8 +2457,7 @@ public IAsyncEnumerable GetPullRequestActivitiesStreamAsync int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2538,8 +2488,7 @@ public IAsyncEnumerable GetPullRequestActivitiesStreamAsync /// true if the pull request was declined; otherwise, false. public async Task DeclinePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["version"] = version, }; @@ -2563,8 +2512,7 @@ public async Task DeclinePullRequestAsync(string projectKey, string reposi /// The merge state. public async Task GetPullRequestMergeStateAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["version"] = version, }; @@ -2618,8 +2566,7 @@ public async Task GetPullRequestMergeStateAsync(string pr /// The merged pull request. public async Task MergePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["version"] = version, }; @@ -2643,8 +2590,7 @@ public async Task MergePullRequestAsync(string projectKey, string r /// The reopened pull request. public async Task ReopenPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["version"] = version, }; @@ -2716,8 +2662,7 @@ public async Task> GetPullRequestChangesAsync(string project int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2764,8 +2709,7 @@ public IAsyncEnumerable GetPullRequestChangesStreamAsync(string projectK int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2820,8 +2764,7 @@ public async Task CreatePullRequestCommentAsync(string projectKey, s // Build the comment payload dynamically to avoid sending empty anchor objects // which Bitbucket Server 9.0 rejects with HTTP 500. // See: BUG-003 - add_pull_request_comment returns 500 error - var data = new Dictionary -(StringComparer.Ordinal) + var data = new Dictionary(StringComparer.Ordinal) { ["text"] = text, }; @@ -2894,8 +2837,7 @@ public async Task> GetPullRequestCommentsAsync(string pr int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -2948,8 +2890,7 @@ public IAsyncEnumerable GetPullRequestCommentsStreamAsync(string pro int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -3066,8 +3007,7 @@ public async Task> GetPullRequestCommitsAsync(string project int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -3096,8 +3036,7 @@ public IAsyncEnumerable GetPullRequestCommitsStreamAsync(string projectK int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -3240,8 +3179,7 @@ public async Task GetPullRequestDiffPathAsync(string projectKey, st private static Dictionary CreatePullRequestDiffQueryParams(int contextLines, DiffTypes diffType, string? sinceId, string? srcPath, string? untilId, string whitespace, bool withComments) { - return new Dictionary -(StringComparer.Ordinal) + return new Dictionary(StringComparer.Ordinal) { ["contextLines"] = contextLines, ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), @@ -3311,8 +3249,7 @@ public async Task> GetPullRequestParticipantsAsync(stri int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -3351,8 +3288,7 @@ public IAsyncEnumerable GetPullRequestParticipantsStreamAsync(strin int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -3502,8 +3438,7 @@ public async Task> GetPullRequestTasksAsync(string pr int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -3550,8 +3485,7 @@ public IAsyncEnumerable GetPullRequestTasksStreamAsync(string pro int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -3630,8 +3564,7 @@ public async Task> GetPullRequestBlockerCommentsAsyn int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -3673,8 +3606,7 @@ public IAsyncEnumerable GetPullRequestBlockerCommentsStreamAsync int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -3991,8 +3923,7 @@ public async Task RetrieveRawContentAsync(string projectKey, string repo bool htmlEscape = true, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["at"] = at, ["markup"] = BitbucketHelpers.BoolToString(markup), @@ -4062,8 +3993,7 @@ public async Task> GetProjectRepositoryHooksSettingsAsync(stri int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -4242,8 +4172,7 @@ public async Task> GetProjectRepositoryTagsAsync(string project int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -4283,8 +4212,7 @@ public IAsyncEnumerable GetProjectRepositoryTagsStreamAsync(string projectK int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -4370,8 +4298,7 @@ public async Task> GetProjectRepositoryWebHooksAsync(string int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -4440,8 +4367,7 @@ public async Task GetProjectRepositoryWebHookAsync(string projectKey, s bool statistics = false, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["statistics"] = BitbucketHelpers.BoolToString(statistics), }; @@ -4508,8 +4434,7 @@ public async Task GetProjectRepositoryWebHookLatestAsync(string projectK WebHookOutcomes? outcome = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["event"] = @event, ["outcome"] = BitbucketHelpers.WebHookOutcomeToString(outcome), diff --git a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs index 10ea955..e5e7e5e 100644 --- a/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Repos/BitbucketClient.cs @@ -40,8 +40,7 @@ public async Task> GetRepositoriesAsync( bool isPublic = false, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -85,8 +84,7 @@ public IAsyncEnumerable GetRepositoriesStreamAsync( bool isPublic = false, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs index 20dc770..b301b4d 100644 --- a/src/Bitbucket.Net/Core/Users/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Users/BitbucketClient.cs @@ -46,8 +46,7 @@ public async Task> GetUsersAsync(string? filter = null, string CancellationToken cancellationToken = default, params string[] permissionN) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs index 3eff7ee..5666ff9 100644 --- a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs +++ b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs @@ -181,8 +181,7 @@ public async Task> GetDefaultReviewersAsync(string projectKey, int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["sourceRepoId"] = sourceRepoId, ["targetRepoId"] = targetRepoId, diff --git a/src/Bitbucket.Net/Jira/BitbucketClient.cs b/src/Bitbucket.Net/Jira/BitbucketClient.cs index aba88af..21f7dec 100644 --- a/src/Bitbucket.Net/Jira/BitbucketClient.cs +++ b/src/Bitbucket.Net/Jira/BitbucketClient.cs @@ -41,8 +41,7 @@ public async Task> GetChangeSetsAsync(string issueKey, in int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs index d2424bd..0d94a33 100644 --- a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs +++ b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs @@ -41,8 +41,7 @@ public async Task> GetUserAccessTokensAsync(string user int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, diff --git a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs index 9cc7980..7633205 100644 --- a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs @@ -47,8 +47,7 @@ public async Task> GetProjectRefRestrictionsAsync(st int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), @@ -172,8 +171,7 @@ public async Task> GetRepositoryRefRestrictionsAsync int? avatarSize = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), diff --git a/src/Bitbucket.Net/Ssh/BitbucketClient.cs b/src/Bitbucket.Net/Ssh/BitbucketClient.cs index 331f74e..fa1605f 100644 --- a/src/Bitbucket.Net/Ssh/BitbucketClient.cs +++ b/src/Bitbucket.Net/Ssh/BitbucketClient.cs @@ -86,8 +86,7 @@ public async Task> GetProjectKeysAsync(int keyId, int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -124,8 +123,7 @@ public async Task> GetProjectKeysAsync(string projectKey int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -232,8 +230,7 @@ public async Task> GetRepoKeysAsync(int keyId, int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -273,8 +270,7 @@ public async Task> GetRepoKeysAsync(string projectKey int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, @@ -386,8 +382,7 @@ public async Task> GetUserKeysAsync(string? userSlug = null, int? start = null, CancellationToken cancellationToken = default) { - var queryParamValues = new Dictionary -(System.StringComparer.Ordinal) + var queryParamValues = new Dictionary(StringComparer.Ordinal) { ["limit"] = limit, ["start"] = start, From 5b77cf0f7ecbc1d27ab1387b268403e9cd17b9fd Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 00:36:40 +0000 Subject: [PATCH 37/50] chore: update CHANGELOG with new streaming methods for pull requests and repository changes --- CHANGELOG.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 360a197..2ca8663 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Removed commented-out `Avatar` property from `ProjectDefinition`. - Fixed duplicate `` XML doc tag on `GetRepositoriesStreamAsync`. +### Added + +- `GetPullRequestActivitiesStreamAsync` — stream PR activities as `IAsyncEnumerable` +- `GetPullRequestChangesStreamAsync` — stream PR changes as `IAsyncEnumerable` +- `GetPullRequestCommentsStreamAsync` — stream PR comments as `IAsyncEnumerable` +- `GetPullRequestParticipantsStreamAsync` — stream PR participants as `IAsyncEnumerable` +- `GetDashboardPullRequestsStreamAsync` — stream dashboard PRs as `IAsyncEnumerable` +- `GetInboxPullRequestsStreamAsync` — stream inbox PRs as `IAsyncEnumerable` +- `GetProjectRepositoryTagsStreamAsync` — stream repository tags as `IAsyncEnumerable` +- `GetPullRequestTasksStreamAsync` — stream PR tasks as `IAsyncEnumerable` (deprecated, pre-9.0) +- `GetPullRequestBlockerCommentsStreamAsync` — stream blocker comments as `IAsyncEnumerable` (9.0+) +- `GetChangesStreamAsync` — stream repository changes between refs as `IAsyncEnumerable` +- `GetCommitChangesStreamAsync` — stream commit changes as `IAsyncEnumerable` + ## [0.1.0-beta.1] - 2026-02-06 (pre-release) ### Notes From eea8af3e9d760d0ae09746e349c7247bf85af359 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:30:53 +0000 Subject: [PATCH 38/50] feat: add test fixtures for branches, commits, projects, and repositories --- .../Fixtures/Core/branches-page1.json | 23 ++++ .../Fixtures/Core/branches-page2.json | 15 +++ .../Fixtures/Core/commits-page1.json | 46 +++++++ .../Fixtures/Core/commits-page2.json | 29 +++++ .../Fixtures/Core/diff-empty.json | 8 ++ .../Fixtures/Core/diff-multiple.json | 123 ++++++++++++++++++ .../Fixtures/Core/empty-paged.json | 7 + .../Fixtures/Core/projects-page1.json | 39 ++++++ .../Fixtures/Core/projects-page2.json | 23 ++++ .../Fixtures/Core/repositories-page1.json | 47 +++++++ .../Fixtures/Core/repositories-page2.json | 27 ++++ 11 files changed, 387 insertions(+) create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/branches-page1.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/branches-page2.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/commits-page1.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/commits-page2.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/diff-empty.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/diff-multiple.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/empty-paged.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/projects-page1.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/projects-page2.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/repositories-page1.json create mode 100644 test/Bitbucket.Net.Tests/Fixtures/Core/repositories-page2.json diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/branches-page1.json b/test/Bitbucket.Net.Tests/Fixtures/Core/branches-page1.json new file mode 100644 index 0000000..3c159e0 --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/branches-page1.json @@ -0,0 +1,23 @@ +{ + "size": 2, + "limit": 2, + "isLastPage": false, + "nextPageStart": 2, + "start": 0, + "values": [ + { + "id": "refs/heads/main", + "displayId": "main", + "type": "BRANCH", + "latestCommit": "aaa111bbb222ccc333ddd444eee555fff6667778", + "isDefault": true + }, + { + "id": "refs/heads/develop", + "displayId": "develop", + "type": "BRANCH", + "latestCommit": "bbb222ccc333ddd444eee555fff666777888999aa", + "isDefault": false + } + ] +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/branches-page2.json b/test/Bitbucket.Net.Tests/Fixtures/Core/branches-page2.json new file mode 100644 index 0000000..2ef7b47 --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/branches-page2.json @@ -0,0 +1,15 @@ +{ + "size": 1, + "limit": 2, + "isLastPage": true, + "start": 2, + "values": [ + { + "id": "refs/heads/feature-x", + "displayId": "feature-x", + "type": "BRANCH", + "latestCommit": "ccc333ddd444eee555fff666777888999aaabbb00", + "isDefault": false + } + ] +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/commits-page1.json b/test/Bitbucket.Net.Tests/Fixtures/Core/commits-page1.json new file mode 100644 index 0000000..54f3ddf --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/commits-page1.json @@ -0,0 +1,46 @@ +{ + "size": 2, + "limit": 2, + "isLastPage": false, + "nextPageStart": 2, + "start": 0, + "values": [ + { + "id": "aaa111bbb222ccc333ddd444eee555fff6667778", + "displayId": "aaa111b", + "author": { + "name": "dev1", + "emailAddress": "dev1@example.com" + }, + "authorTimestamp": 1706918400000, + "committer": { + "name": "dev1", + "emailAddress": "dev1@example.com" + }, + "committerTimestamp": 1706918400000, + "message": "First commit", + "parents": [] + }, + { + "id": "bbb222ccc333ddd444eee555fff666777888999aa", + "displayId": "bbb222c", + "author": { + "name": "dev2", + "emailAddress": "dev2@example.com" + }, + "authorTimestamp": 1706922000000, + "committer": { + "name": "dev2", + "emailAddress": "dev2@example.com" + }, + "committerTimestamp": 1706922000000, + "message": "Second commit", + "parents": [ + { + "id": "aaa111bbb222ccc333ddd444eee555fff6667778", + "displayId": "aaa111b" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/commits-page2.json b/test/Bitbucket.Net.Tests/Fixtures/Core/commits-page2.json new file mode 100644 index 0000000..ac38f0f --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/commits-page2.json @@ -0,0 +1,29 @@ +{ + "size": 1, + "limit": 2, + "isLastPage": true, + "start": 2, + "values": [ + { + "id": "ccc333ddd444eee555fff666777888999aaabbb00", + "displayId": "ccc333d", + "author": { + "name": "dev3", + "emailAddress": "dev3@example.com" + }, + "authorTimestamp": 1706925600000, + "committer": { + "name": "dev3", + "emailAddress": "dev3@example.com" + }, + "committerTimestamp": 1706925600000, + "message": "Third commit", + "parents": [ + { + "id": "bbb222ccc333ddd444eee555fff666777888999aa", + "displayId": "bbb222c" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/diff-empty.json b/test/Bitbucket.Net.Tests/Fixtures/Core/diff-empty.json new file mode 100644 index 0000000..658f9d6 --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/diff-empty.json @@ -0,0 +1,8 @@ +{ + "diffs": [], + "truncated": false, + "contextLines": "10", + "fromHash": "abc123", + "toHash": "def456", + "whitespace": "IGNORE_ALL" +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/diff-multiple.json b/test/Bitbucket.Net.Tests/Fixtures/Core/diff-multiple.json new file mode 100644 index 0000000..baca701 --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/diff-multiple.json @@ -0,0 +1,123 @@ +{ + "diffs": [ + { + "source": { + "parent": "src", + "name": "file1.cs", + "toString": "src/file1.cs" + }, + "destination": { + "parent": "src", + "name": "file1.cs", + "toString": "src/file1.cs" + }, + "hunks": [ + { + "sourceLine": 1, + "sourceSpan": 3, + "destinationLine": 1, + "destinationSpan": 4, + "segments": [ + { + "type": "ADDED", + "lines": [ + { + "source": 0, + "destination": 1, + "line": "// New line in file1" + } + ] + } + ] + } + ], + "truncated": false + }, + { + "source": { + "parent": "src", + "name": "file2.cs", + "toString": "src/file2.cs" + }, + "destination": { + "parent": "src", + "name": "file2.cs", + "toString": "src/file2.cs" + }, + "hunks": [ + { + "sourceLine": 10, + "sourceSpan": 5, + "destinationLine": 10, + "destinationSpan": 6, + "segments": [ + { + "type": "REMOVED", + "lines": [ + { + "source": 10, + "destination": 0, + "line": "// Removed line in file2" + } + ] + }, + { + "type": "ADDED", + "lines": [ + { + "source": 0, + "destination": 10, + "line": "// Added line in file2" + }, + { + "source": 0, + "destination": 11, + "line": "// Another added line" + } + ] + } + ] + } + ], + "truncated": false + }, + { + "source": { + "parent": "tests", + "name": "test.cs", + "toString": "tests/test.cs" + }, + "destination": { + "parent": "tests", + "name": "test.cs", + "toString": "tests/test.cs" + }, + "hunks": [ + { + "sourceLine": 1, + "sourceSpan": 1, + "destinationLine": 1, + "destinationSpan": 2, + "segments": [ + { + "type": "CONTEXT", + "lines": [ + { + "source": 1, + "destination": 1, + "line": "using Xunit;" + } + ] + } + ] + } + ], + "truncated": false + } + ], + "truncated": false, + "contextLines": "10", + "fromHash": "abc123", + "toHash": "def456", + "whitespace": "IGNORE_ALL" +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/empty-paged.json b/test/Bitbucket.Net.Tests/Fixtures/Core/empty-paged.json new file mode 100644 index 0000000..379cb09 --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/empty-paged.json @@ -0,0 +1,7 @@ +{ + "size": 0, + "limit": 25, + "isLastPage": true, + "start": 0, + "values": [] +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/projects-page1.json b/test/Bitbucket.Net.Tests/Fixtures/Core/projects-page1.json new file mode 100644 index 0000000..1cd3c1a --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/projects-page1.json @@ -0,0 +1,39 @@ +{ + "size": 2, + "limit": 2, + "isLastPage": false, + "nextPageStart": 2, + "start": 0, + "values": [ + { + "key": "PROJ1", + "id": 1, + "name": "Project One", + "description": "First project", + "public": false, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://localhost/projects/PROJ1" + } + ] + } + }, + { + "key": "PROJ2", + "id": 2, + "name": "Project Two", + "description": "Second project", + "public": true, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://localhost/projects/PROJ2" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/projects-page2.json b/test/Bitbucket.Net.Tests/Fixtures/Core/projects-page2.json new file mode 100644 index 0000000..0ed1c78 --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/projects-page2.json @@ -0,0 +1,23 @@ +{ + "size": 1, + "limit": 2, + "isLastPage": true, + "start": 2, + "values": [ + { + "key": "PROJ3", + "id": 3, + "name": "Project Three", + "description": "Third project", + "public": false, + "type": "NORMAL", + "links": { + "self": [ + { + "href": "http://localhost/projects/PROJ3" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/repositories-page1.json b/test/Bitbucket.Net.Tests/Fixtures/Core/repositories-page1.json new file mode 100644 index 0000000..f0abf68 --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/repositories-page1.json @@ -0,0 +1,47 @@ +{ + "size": 2, + "limit": 2, + "isLastPage": false, + "nextPageStart": 2, + "start": 0, + "values": [ + { + "slug": "repo-alpha", + "id": 1, + "name": "Repo Alpha", + "scmId": "git", + "state": "AVAILABLE", + "forkable": true, + "public": false, + "project": { + "key": "TEST" + }, + "links": { + "self": [ + { + "href": "http://localhost/projects/TEST/repos/repo-alpha" + } + ] + } + }, + { + "slug": "repo-beta", + "id": 2, + "name": "Repo Beta", + "scmId": "git", + "state": "AVAILABLE", + "forkable": true, + "public": false, + "project": { + "key": "TEST" + }, + "links": { + "self": [ + { + "href": "http://localhost/projects/TEST/repos/repo-beta" + } + ] + } + } + ] +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/Fixtures/Core/repositories-page2.json b/test/Bitbucket.Net.Tests/Fixtures/Core/repositories-page2.json new file mode 100644 index 0000000..5fda2a8 --- /dev/null +++ b/test/Bitbucket.Net.Tests/Fixtures/Core/repositories-page2.json @@ -0,0 +1,27 @@ +{ + "size": 1, + "limit": 2, + "isLastPage": true, + "start": 2, + "values": [ + { + "slug": "repo-gamma", + "id": 3, + "name": "Repo Gamma", + "scmId": "git", + "state": "AVAILABLE", + "forkable": true, + "public": false, + "project": { + "key": "TEST" + }, + "links": { + "self": [ + { + "href": "http://localhost/projects/TEST/repos/repo-gamma" + } + ] + } + } + ] +} \ No newline at end of file From e6b24643e24be17c0d573c25ed6bf9e88aa7e667 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:34:23 +0000 Subject: [PATCH 39/50] feat: add methods for setting up paged and diff endpoints in WireMockServer --- .../Infrastructure/MockSetupExtensions.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs b/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs index e315557..06cd610 100644 --- a/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs +++ b/test/Bitbucket.Net.Tests/Infrastructure/MockSetupExtensions.cs @@ -13,6 +13,78 @@ public static class MockSetupExtensions private const string ApiBasePath = "/rest/api/1.0"; private const string FixturesBasePath = "Fixtures"; + public static WireMockServer SetupPagedEndpoint(this WireMockServer server, string path, string fixtureCategory, string page1File, string page2File) + { + server.Given(Request.Create() + .WithPath(path) + .WithParam("start", "0") + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(HttpStatusCode.OK) + .WithHeader("Content-Type", "application/json") + .WithBodyFromFile(GetFixturePath(fixtureCategory, page1File))); + + server.Given(Request.Create() + .WithPath(path) + .WithParam("start", "2") + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(HttpStatusCode.OK) + .WithHeader("Content-Type", "application/json") + .WithBodyFromFile(GetFixturePath(fixtureCategory, page2File))); + + return server; + } + + public static WireMockServer SetupPagedEndpointNoStartParam(this WireMockServer server, string path, string fixtureCategory, string page1File, string page2File) + { + server.Given(Request.Create() + .WithPath(path) + .WithParam("start", false) + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(HttpStatusCode.OK) + .WithHeader("Content-Type", "application/json") + .WithBodyFromFile(GetFixturePath(fixtureCategory, page1File))); + + server.Given(Request.Create() + .WithPath(path) + .WithParam("start", "2") + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(HttpStatusCode.OK) + .WithHeader("Content-Type", "application/json") + .WithBodyFromFile(GetFixturePath(fixtureCategory, page2File))); + + return server; + } + + public static WireMockServer SetupEmptyPagedEndpoint(this WireMockServer server, string path) + { + server.Given(Request.Create() + .WithPath(path) + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(HttpStatusCode.OK) + .WithHeader("Content-Type", "application/json") + .WithBodyFromFile(GetFixturePath("Core", "empty-paged.json"))); + + return server; + } + + public static WireMockServer SetupDiffEndpoint(this WireMockServer server, string path, string fixtureFile) + { + server.Given(Request.Create() + .WithPath(path) + .UsingGet()) + .RespondWith(Response.Create() + .WithStatusCode(HttpStatusCode.OK) + .WithHeader("Content-Type", "application/json") + .WithBodyFromFile(GetFixturePath("Core", fixtureFile))); + + return server; + } + public static WireMockServer SetupGetProjects(this WireMockServer server, int? start = null) { var request = Request.Create() From ea1c54effa7c93db8a0dbb6394698383f141267f Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:34:52 +0000 Subject: [PATCH 40/50] feat: add methods to create BitbucketClient with HttpClient and FlurlClient --- .../Infrastructure/BitbucketMockFixture.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs b/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs index f1bf273..b520bdb 100644 --- a/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs +++ b/test/Bitbucket.Net.Tests/Infrastructure/BitbucketMockFixture.cs @@ -1,3 +1,4 @@ +using Flurl.Http; using WireMock.Server; using Xunit; @@ -25,6 +26,18 @@ public BitbucketClient CreateClient() return new BitbucketClient(BaseUrl, TestConstants.TestUsername, TestConstants.TestPassword); } + public BitbucketClient CreateClientWithHttpClient() + { + var httpClient = new HttpClient { BaseAddress = new Uri(BaseUrl) }; + return new BitbucketClient(httpClient, BaseUrl, () => "test-token"); + } + + public BitbucketClient CreateClientWithFlurlClient() + { + var flurlClient = new FlurlClient(BaseUrl); + return new BitbucketClient(flurlClient, () => "test-token"); + } + public void Reset() { Server.Reset(); From 4316bc5ac6cabadf08b2065c95255a778a516bba Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:35:40 +0000 Subject: [PATCH 41/50] feat: add comprehensive mock tests for cancellation, DI constructors, diff streaming, and various streaming methods --- .../MockTests/CancellationMockTests.cs | 241 +++++++++++ .../MockTests/DiConstructorMockTests.cs | 232 +++++++++++ .../MockTests/DiffStreamingMockTests.cs | 209 ++++++++++ .../MockTests/StreamingMockTests.cs | 384 ++++++++++++++++++ 4 files changed, 1066 insertions(+) create mode 100644 test/Bitbucket.Net.Tests/MockTests/CancellationMockTests.cs create mode 100644 test/Bitbucket.Net.Tests/MockTests/DiConstructorMockTests.cs create mode 100644 test/Bitbucket.Net.Tests/MockTests/DiffStreamingMockTests.cs create mode 100644 test/Bitbucket.Net.Tests/MockTests/StreamingMockTests.cs diff --git a/test/Bitbucket.Net.Tests/MockTests/CancellationMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/CancellationMockTests.cs new file mode 100644 index 0000000..599b2de --- /dev/null +++ b/test/Bitbucket.Net.Tests/MockTests/CancellationMockTests.cs @@ -0,0 +1,241 @@ +using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Tests.Infrastructure; +using Flurl.Http; +using Xunit; + +namespace Bitbucket.Net.Tests.MockTests; + +public class CancellationMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private const string ApiBasePath = "/rest/api/1.0"; + private readonly BitbucketMockFixture _fixture = fixture; + + #region Buffered Methods — Pre-cancelled Token + + [Fact] + public async Task GetProjectsAsync_PreCancelledToken_ThrowsOperationCanceled() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsAnyAsync( + () => client.GetProjectsAsync(cancellationToken: cts.Token)); + } + + [Fact] + public async Task GetProjectAsync_PreCancelledToken_ThrowsOperationCanceled() + { + _fixture.Reset(); + _fixture.Server.SetupGetProject(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await AssertCancellationPropagatedAsync( + () => client.GetProjectAsync(TestConstants.TestProjectKey, cts.Token)); + } + + [Fact] + public async Task CreateProjectAsync_PreCancelledToken_ThrowsOperationCanceled() + { + _fixture.Reset(); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + var definition = new ProjectDefinition { Key = TestConstants.TestProjectKey, Name = TestConstants.TestProjectName }; + + await AssertCancellationPropagatedAsync( + () => client.CreateProjectAsync(definition, cts.Token)); + } + + [Fact] + public async Task DeleteProjectAsync_PreCancelledToken_ThrowsOperationCanceled() + { + _fixture.Reset(); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await AssertCancellationPropagatedAsync( + () => client.DeleteProjectAsync(TestConstants.TestProjectKey, cts.Token)); + } + + [Fact] + public async Task GetPullRequestsAsync_PreCancelledToken_ThrowsOperationCanceled() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequests(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsAnyAsync( + () => client.GetPullRequestsAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, cancellationToken: cts.Token)); + } + + #endregion + + #region Streaming Methods — Pre-cancelled Token + + [Fact] + public async Task GetProjectsStreamAsync_PreCancelledToken_ThrowsOnMoveNext() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsAnyAsync(async () => + { + await foreach (var _ in client.GetProjectsStreamAsync(cancellationToken: cts.Token)) + { + } + }); + } + + [Fact] + public async Task GetPullRequestsStreamAsync_PreCancelledToken_ThrowsOnMoveNext() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequests(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsAnyAsync(async () => + { + await foreach (var _ in client.GetPullRequestsStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, cancellationToken: cts.Token)) + { + } + }); + } + + [Fact] + public async Task GetBranchesStreamAsync_PreCancelledToken_ThrowsOnMoveNext() + { + _fixture.Reset(); + _fixture.Server.SetupGetBranches(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsAnyAsync(async () => + { + await foreach (var _ in client.GetBranchesStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, cancellationToken: cts.Token)) + { + } + }); + } + + #endregion + + #region Streaming Methods — Cancel During Multi-page + + [Fact] + public async Task GetProjectsStreamAsync_CancelAfterFirstPage_StopsEnumeration() + { + _fixture.Reset(); + _fixture.Server.SetupPagedEndpoint( + $"{ApiBasePath}/projects", "Core", "projects-page1.json", "projects-page2.json"); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + var results = new List(); + + await Assert.ThrowsAnyAsync(async () => + { + await foreach (var project in client.GetProjectsStreamAsync(start: 0, cancellationToken: cts.Token)) + { + results.Add(project); + if (results.Count >= 2) + cts.Cancel(); + } + }); + + Assert.Equal(2, results.Count); + } + + #endregion + + #region Diff Streaming — Pre-cancelled Token + + [Fact] + public async Task GetPullRequestDiffStreamAsync_PreCancelledToken_ThrowsOperationCanceled() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestDiff(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await AssertCancellationPropagatedAsync(async () => + { + await foreach (var _ in client.GetPullRequestDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId, cancellationToken: cts.Token)) + { + } + }); + } + + [Fact] + public async Task GetCommitDiffStreamAsync_PreCancelledToken_ThrowsOperationCanceled() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommitDiff(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestCommitId); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await AssertCancellationPropagatedAsync(async () => + { + await foreach (var _ in client.GetCommitDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestCommitId, cancellationToken: cts.Token)) + { + } + }); + } + + #endregion + + #region Pre-cancelled Token — No HTTP Call Observed + + [Fact] + public async Task GetProjectsAsync_PreCancelledToken_NoHttpCallMade() + { + _fixture.Reset(); + var client = _fixture.CreateClient(); + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + await Assert.ThrowsAnyAsync( + () => client.GetProjectsAsync(cancellationToken: cts.Token)); + + Assert.Empty(_fixture.Server.LogEntries); + } + + #endregion + + /// + /// Asserts that a cancelled token propagates correctly, whether the runtime throws + /// directly or Flurl wraps it in a + /// . + /// + private static async Task AssertCancellationPropagatedAsync(Func action) + { + try + { + await action(); + Assert.Fail("Expected cancellation to be propagated, but the operation completed normally."); + } + catch (OperationCanceledException) + { + // Direct cancellation — expected (paged methods check token first) + } + catch (FlurlHttpException ex) when (ex.InnerException is OperationCanceledException) + { + // Flurl-wrapped cancellation — also expected (non-paged methods hit Flurl first) + } + } +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/DiConstructorMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DiConstructorMockTests.cs new file mode 100644 index 0000000..41eddcd --- /dev/null +++ b/test/Bitbucket.Net.Tests/MockTests/DiConstructorMockTests.cs @@ -0,0 +1,232 @@ +using Bitbucket.Net.Common.Exceptions; +using Bitbucket.Net.Tests.Infrastructure; +using System.Net; +using Xunit; + +namespace Bitbucket.Net.Tests.MockTests; + +public class DiConstructorMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private const string ApiBasePath = "/rest/api/1.0"; + private readonly BitbucketMockFixture _fixture = fixture; + + #region HttpClient Constructor + + [Fact] + public async Task HttpClientConstructor_GetProjects_Succeeds() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClientWithHttpClient(); + + var projects = await client.GetProjectsAsync(); + + Assert.NotNull(projects); + var projectList = projects.ToList(); + Assert.Single(projectList); + Assert.Equal(TestConstants.TestProjectKey, projectList[0].Key); + } + + [Fact] + public async Task HttpClientConstructor_GetPullRequests_Succeeds() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequests(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClientWithHttpClient(); + + var pullRequests = await client.GetPullRequestsAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + + Assert.NotNull(pullRequests); + Assert.Single(pullRequests); + } + + [Fact] + public async Task HttpClientConstructor_StreamingEndpoint_Succeeds() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClientWithHttpClient(); + + var results = new List(); + await foreach (var project in client.GetProjectsStreamAsync()) + { + results.Add(project); + } + + Assert.Single(results); + } + + [Fact] + public async Task HttpClientConstructor_ErrorHandling_ThrowsTypedException() + { + _fixture.Reset(); + var projectKey = "NOPE"; + _fixture.Server.SetupNotFound($"{ApiBasePath}/projects/{projectKey}"); + var client = _fixture.CreateClientWithHttpClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); + } + + [Fact] + public async Task HttpClientConstructor_Unauthorized_ThrowsAuthenticationException() + { + _fixture.Reset(); + _fixture.Server.SetupUnauthorized($"{ApiBasePath}/projects/{TestConstants.TestProjectKey}"); + var client = _fixture.CreateClientWithHttpClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(TestConstants.TestProjectKey)); + + Assert.Equal(HttpStatusCode.Unauthorized, exception.StatusCode); + } + + [Fact] + public async Task HttpClientConstructor_ServerError_ThrowsServerException() + { + _fixture.Reset(); + _fixture.Server.SetupInternalServerError($"{ApiBasePath}/projects/{TestConstants.TestProjectKey}"); + var client = _fixture.CreateClientWithHttpClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(TestConstants.TestProjectKey)); + + Assert.Equal(HttpStatusCode.InternalServerError, exception.StatusCode); + } + + #endregion + + #region FlurlClient Constructor + + [Fact] + public async Task FlurlClientConstructor_GetProjects_Succeeds() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClientWithFlurlClient(); + + var projects = await client.GetProjectsAsync(); + + Assert.NotNull(projects); + var projectList = projects.ToList(); + Assert.Single(projectList); + Assert.Equal(TestConstants.TestProjectKey, projectList[0].Key); + } + + [Fact] + public async Task FlurlClientConstructor_GetPullRequests_Succeeds() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequests(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClientWithFlurlClient(); + + var pullRequests = await client.GetPullRequestsAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + + Assert.NotNull(pullRequests); + Assert.Single(pullRequests); + } + + [Fact] + public async Task FlurlClientConstructor_StreamingEndpoint_Succeeds() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClientWithFlurlClient(); + + var results = new List(); + await foreach (var project in client.GetProjectsStreamAsync()) + { + results.Add(project); + } + + Assert.Single(results); + } + + [Fact] + public async Task FlurlClientConstructor_ErrorHandling_ThrowsTypedException() + { + _fixture.Reset(); + var projectKey = "NOPE"; + _fixture.Server.SetupNotFound($"{ApiBasePath}/projects/{projectKey}"); + var client = _fixture.CreateClientWithFlurlClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(projectKey)); + + Assert.Equal(HttpStatusCode.NotFound, exception.StatusCode); + } + + [Fact] + public async Task FlurlClientConstructor_Unauthorized_ThrowsAuthenticationException() + { + _fixture.Reset(); + _fixture.Server.SetupUnauthorized($"{ApiBasePath}/projects/{TestConstants.TestProjectKey}"); + var client = _fixture.CreateClientWithFlurlClient(); + + var exception = await Assert.ThrowsAsync( + () => client.GetProjectAsync(TestConstants.TestProjectKey)); + + Assert.Equal(HttpStatusCode.Unauthorized, exception.StatusCode); + } + + #endregion + + #region Both Constructors — Consistent Behavior + + [Fact] + public async Task BothConstructors_SameEndpoint_ReturnEquivalentResults() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var httpClientBasedClient = _fixture.CreateClientWithHttpClient(); + var flurlBasedClient = _fixture.CreateClientWithFlurlClient(); + + var httpResults = (await httpClientBasedClient.GetProjectsAsync()).ToList(); + + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var flurlResults = (await flurlBasedClient.GetProjectsAsync()).ToList(); + + Assert.Equal(httpResults.Count, flurlResults.Count); + Assert.Equal(httpResults[0].Key, flurlResults[0].Key); + Assert.Equal(httpResults[0].Name, flurlResults[0].Name); + } + + #endregion + + #region Token Authentication Verification + + [Fact] + public async Task HttpClientConstructor_SendsAuthorizationHeader() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClientWithHttpClient(); + + await client.GetProjectsAsync(); + + var logEntry = Assert.Single(_fixture.Server.LogEntries); + var authHeader = logEntry.RequestMessage.Headers?["Authorization"]; + Assert.NotNull(authHeader); + Assert.Contains("Bearer test-token", authHeader.ToString()); + } + + [Fact] + public async Task FlurlClientConstructor_SendsAuthorizationHeader() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClientWithFlurlClient(); + + await client.GetProjectsAsync(); + + var logEntry = Assert.Single(_fixture.Server.LogEntries); + var authHeader = logEntry.RequestMessage.Headers?["Authorization"]; + Assert.NotNull(authHeader); + Assert.Contains("Bearer test-token", authHeader.ToString()); + } + + #endregion +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/DiffStreamingMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/DiffStreamingMockTests.cs new file mode 100644 index 0000000..88c198c --- /dev/null +++ b/test/Bitbucket.Net.Tests/MockTests/DiffStreamingMockTests.cs @@ -0,0 +1,209 @@ +using Bitbucket.Net.Tests.Infrastructure; +using Xunit; + +namespace Bitbucket.Net.Tests.MockTests; + +public class DiffStreamingMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private const string ApiBasePath = "/rest/api/1.0"; + private readonly BitbucketMockFixture _fixture = fixture; + + #region GetCommitDiffStreamAsync + + [Fact] + public async Task GetCommitDiffStreamAsync_SingleDiff_YieldsDiffEntry() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommitDiff(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestCommitId); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetCommitDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestCommitId)); + + Assert.Single(results); + Assert.NotNull(results[0].Source); + Assert.NotNull(results[0].Destination); + Assert.NotNull(results[0].Hunks); + Assert.NotEmpty(results[0].Hunks!); + } + + [Fact] + public async Task GetCommitDiffStreamAsync_MultipleDiffs_YieldsAllEntries() + { + _fixture.Reset(); + _fixture.Server.SetupDiffEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/commits/{TestConstants.TestCommitId}/diff", + "diff-multiple.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetCommitDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestCommitId)); + + Assert.Equal(3, results.Count); + } + + [Fact] + public async Task GetCommitDiffStreamAsync_EmptyDiffs_YieldsZeroItems() + { + _fixture.Reset(); + _fixture.Server.SetupDiffEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/commits/{TestConstants.TestCommitId}/diff", + "diff-empty.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetCommitDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestCommitId)); + + Assert.Empty(results); + } + + #endregion + + #region GetRepositoryDiffStreamAsync + + [Fact] + public async Task GetRepositoryDiffStreamAsync_SingleDiff_YieldsDiffEntry() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositoryDiff(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetRepositoryDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, until: "HEAD")); + + Assert.Single(results); + Assert.NotNull(results[0].Hunks); + } + + [Fact] + public async Task GetRepositoryDiffStreamAsync_MultipleDiffs_YieldsAllEntries() + { + _fixture.Reset(); + _fixture.Server.SetupDiffEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/diff", + "diff-multiple.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetRepositoryDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, until: "HEAD")); + + Assert.Equal(3, results.Count); + } + + [Fact] + public async Task GetRepositoryDiffStreamAsync_EmptyDiffs_YieldsZeroItems() + { + _fixture.Reset(); + _fixture.Server.SetupDiffEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/diff", + "diff-empty.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetRepositoryDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, until: "HEAD")); + + Assert.Empty(results); + } + + #endregion + + #region GetRepositoryCompareDiffStreamAsync + + [Fact] + public async Task GetRepositoryCompareDiffStreamAsync_SingleDiff_YieldsDiffEntry() + { + _fixture.Reset(); + _fixture.Server.SetupGetCompareDiff(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetRepositoryCompareDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, from: "main", to: "feature")); + + Assert.Single(results); + } + + [Fact] + public async Task GetRepositoryCompareDiffStreamAsync_EmptyDiffs_YieldsZeroItems() + { + _fixture.Reset(); + _fixture.Server.SetupDiffEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/compare/diff", + "diff-empty.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetRepositoryCompareDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, from: "main", to: "feature")); + + Assert.Empty(results); + } + + #endregion + + #region GetPullRequestDiffStreamAsync + + [Fact] + public async Task GetPullRequestDiffStreamAsync_SingleDiff_YieldsDiffEntry() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestDiff(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); + + Assert.Single(results); + Assert.NotNull(results[0].Source); + Assert.NotNull(results[0].Destination); + } + + [Fact] + public async Task GetPullRequestDiffStreamAsync_MultipleDiffs_YieldsAllEntries() + { + _fixture.Reset(); + _fixture.Server.SetupDiffEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/pull-requests/{TestConstants.TestPullRequestId}/diff", + "diff-multiple.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); + + Assert.Equal(3, results.Count); + } + + [Fact] + public async Task GetPullRequestDiffStreamAsync_EmptyDiffs_YieldsZeroItems() + { + _fixture.Reset(); + _fixture.Server.SetupDiffEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/pull-requests/{TestConstants.TestPullRequestId}/diff", + "diff-empty.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); + + Assert.Empty(results); + } + + #endregion + + #region Diff Content Validation + + [Fact] + public async Task GetPullRequestDiffStreamAsync_DiffContainsExpectedSegmentsAndHunks() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestDiff(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestDiffStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); + + var diff = Assert.Single(results); + var hunk = Assert.Single(diff.Hunks!); + var segment = Assert.Single(hunk.Segments!); + Assert.Equal("ADDED", segment.Type); + Assert.NotEmpty(segment.Lines!); + } + + #endregion + + private static async Task> CollectAsync(IAsyncEnumerable source) + { + var list = new List(); + await foreach (var item in source) + { + list.Add(item); + } + return list; + } +} \ No newline at end of file diff --git a/test/Bitbucket.Net.Tests/MockTests/StreamingMockTests.cs b/test/Bitbucket.Net.Tests/MockTests/StreamingMockTests.cs new file mode 100644 index 0000000..705e991 --- /dev/null +++ b/test/Bitbucket.Net.Tests/MockTests/StreamingMockTests.cs @@ -0,0 +1,384 @@ +using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Tests.Infrastructure; +using Xunit; + +namespace Bitbucket.Net.Tests.MockTests; + +public class StreamingMockTests(BitbucketMockFixture fixture) : IClassFixture +{ + private const string ApiBasePath = "/rest/api/1.0"; + private readonly BitbucketMockFixture _fixture = fixture; + + #region GetProjectsStreamAsync + + [Fact] + public async Task GetProjectsStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjects(); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetProjectsStreamAsync()); + + Assert.Single(results); + Assert.Equal(TestConstants.TestProjectKey, results[0].Key); + } + + [Fact] + public async Task GetProjectsStreamAsync_MultiplePages_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupPagedEndpoint( + $"{ApiBasePath}/projects", "Core", "projects-page1.json", "projects-page2.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetProjectsStreamAsync(start: 0)); + + Assert.Equal(3, results.Count); + Assert.Equal("PROJ1", results[0].Key); + Assert.Equal("PROJ2", results[1].Key); + Assert.Equal("PROJ3", results[2].Key); + } + + [Fact] + public async Task GetProjectsStreamAsync_EmptyResult_YieldsZeroItems() + { + _fixture.Reset(); + _fixture.Server.SetupEmptyPagedEndpoint($"{ApiBasePath}/projects"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetProjectsStreamAsync()); + + Assert.Empty(results); + } + + #endregion + + #region GetProjectRepositoriesStreamAsync + + [Fact] + public async Task GetProjectRepositoriesStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetRepositories(TestConstants.TestProjectKey); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetProjectRepositoriesStreamAsync(TestConstants.TestProjectKey)); + + Assert.Single(results); + Assert.Equal(TestConstants.TestRepositorySlug, results[0].Slug); + } + + [Fact] + public async Task GetProjectRepositoriesStreamAsync_MultiplePages_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupPagedEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos", + "Core", "repositories-page1.json", "repositories-page2.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetProjectRepositoriesStreamAsync(TestConstants.TestProjectKey, start: 0)); + + Assert.Equal(3, results.Count); + Assert.Equal("repo-alpha", results[0].Slug); + Assert.Equal("repo-beta", results[1].Slug); + Assert.Equal("repo-gamma", results[2].Slug); + } + + #endregion + + #region GetRepositoriesStreamAsync + + [Fact] + public async Task GetRepositoriesStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupCustomResponse($"{ApiBasePath}/repos", System.Net.HttpStatusCode.OK, "Core", "repositories-list.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetRepositoriesStreamAsync()); + + Assert.Single(results); + } + + [Fact] + public async Task GetRepositoriesStreamAsync_MultiplePages_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupPagedEndpoint( + $"{ApiBasePath}/repos", "Core", "repositories-page1.json", "repositories-page2.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetRepositoriesStreamAsync(start: 0)); + + Assert.Equal(3, results.Count); + } + + #endregion + + #region GetBranchesStreamAsync + + [Fact] + public async Task GetBranchesStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetBranches(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetBranchesStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug)); + + Assert.Equal(2, results.Count); + Assert.Equal("master", results[0].DisplayId); + } + + [Fact] + public async Task GetBranchesStreamAsync_MultiplePages_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupPagedEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/branches", + "Core", "branches-page1.json", "branches-page2.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetBranchesStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, start: 0)); + + Assert.Equal(3, results.Count); + Assert.Equal("main", results[0].DisplayId); + Assert.Equal("develop", results[1].DisplayId); + Assert.Equal("feature-x", results[2].DisplayId); + } + + #endregion + + #region GetCommitsStreamAsync + + [Fact] + public async Task GetCommitsStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommits(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetCommitsStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, until: "HEAD")); + + Assert.Equal(2, results.Count); + } + + [Fact] + public async Task GetCommitsStreamAsync_MultiplePages_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupPagedEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/commits", + "Core", "commits-page1.json", "commits-page2.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetCommitsStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, until: "HEAD", start: 0)); + + Assert.Equal(3, results.Count); + } + + #endregion + + #region GetPullRequestsStreamAsync + + [Fact] + public async Task GetPullRequestsStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequests(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestsStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug)); + + Assert.Single(results); + Assert.Equal(TestConstants.TestPullRequestTitle, results[0].Title); + } + + [Fact] + public async Task GetPullRequestsStreamAsync_EmptyResult_YieldsZeroItems() + { + _fixture.Reset(); + _fixture.Server.SetupEmptyPagedEndpoint( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/pull-requests"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestsStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug)); + + Assert.Empty(results); + } + + #endregion + + #region GetPullRequestCommitsStreamAsync + + [Fact] + public async Task GetPullRequestCommitsStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestCommits(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestCommitsStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); + + Assert.Equal(2, results.Count); + } + + #endregion + + #region GetRawFileContentLinesStreamAsync + + [Fact] + public async Task GetRawFileContentLinesStreamAsync_ReturnsContentLines() + { + _fixture.Reset(); + _fixture.Server.SetupGetRawFileContentStream(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetRawFileContentLinesStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, "README.md")); + + Assert.NotEmpty(results); + Assert.Contains(results, l => l.Contains("README")); + } + + #endregion + + #region Phase 5 Streaming Methods + + [Fact] + public async Task GetPullRequestActivitiesStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestActivities(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestActivitiesStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); + + Assert.Equal(2, results.Count); + } + + [Fact] + public async Task GetPullRequestChangesStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupCustomResponse( + $"{ApiBasePath}/projects/{TestConstants.TestProjectKey}/repos/{TestConstants.TestRepositorySlug}/pull-requests/{TestConstants.TestPullRequestId}/changes", + System.Net.HttpStatusCode.OK, "PullRequests", "changes.json"); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestChangesStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); + + Assert.Equal(2, results.Count); + } + + [Fact] + public async Task GetPullRequestParticipantsStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestParticipants(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestParticipantsStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); + + Assert.Single(results); + } + + [Fact] + public async Task GetPullRequestTasksStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestTasks(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + +#pragma warning disable CS0618 // Obsolete + var results = await CollectAsync(client.GetPullRequestTasksStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); +#pragma warning restore CS0618 + + Assert.Single(results); + } + + [Fact] + public async Task GetPullRequestBlockerCommentsStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetPullRequestBlockerComments(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetPullRequestBlockerCommentsStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestPullRequestId)); + + Assert.Single(results); + } + + [Fact] + public async Task GetChangesStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetChanges(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetChangesStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, until: "HEAD")); + + Assert.Single(results); + } + + [Fact] + public async Task GetCommitChangesStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetCommitChanges(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestCommitId); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetCommitChangesStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, TestConstants.TestCommitId)); + + Assert.Single(results); + } + + [Fact] + public async Task GetProjectRepositoryTagsStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetProjectRepositoryTags(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetProjectRepositoryTagsStreamAsync(TestConstants.TestProjectKey, TestConstants.TestRepositorySlug, filterText: "", orderBy: BranchOrderBy.Alphabetical)); + + Assert.Equal(2, results.Count); + } + + [Fact] + public async Task GetDashboardPullRequestsStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetDashboardPullRequests(); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetDashboardPullRequestsStreamAsync()); + + Assert.Single(results); + } + + [Fact] + public async Task GetInboxPullRequestsStreamAsync_SinglePage_YieldsAllItems() + { + _fixture.Reset(); + _fixture.Server.SetupGetInboxPullRequests(); + var client = _fixture.CreateClient(); + + var results = await CollectAsync(client.GetInboxPullRequestsStreamAsync()); + + Assert.Single(results); + } + + #endregion + + private static async Task> CollectAsync(IAsyncEnumerable source) + { + var list = new List(); + await foreach (var item in source) + { + list.Add(item); + } + return list; + } +} \ No newline at end of file From 705bd021b314002fdd7dd4854a2446a37bed4142 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 01:38:38 +0000 Subject: [PATCH 42/50] chore: update CHANGELOG with testing improvements --- CHANGELOG.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2ca8663..ab0253c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `GetChangesStreamAsync` — stream repository changes between refs as `IAsyncEnumerable` - `GetCommitChangesStreamAsync` — stream commit changes as `IAsyncEnumerable` +### Testing + +- Added streaming endpoint mock tests covering all 20 streaming methods (single-page, multi-page, empty result scenarios) +- Added diff streaming tests for commit, repository, compare, and PR diffs (single, multiple, empty) +- Added MCP extension method tests for `StreamDiffsWithLimitsAsync` and `TakeDiffsWithLimitsAsync` +- Added cancellation token propagation tests (pre-cancelled tokens for buffered, streaming, and diff methods; mid-stream cancellation) +- Added DI constructor integration tests for `HttpClient` and `IFlurlClient` injection paths (CRUD, error handling, streaming, auth header verification) +- Introduced paginated fixture data (`projects-page1.json`, `projects-page2.json`, etc.) and `SetupPagedEndpoint` helper for multi-page mock tests +- Total test count increased from 633 to 696 (+63 new tests) + ## [0.1.0-beta.1] - 2026-02-06 (pre-release) ### Notes From c0ac8d21231e14ae2878ce00128270d904e64f7d Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 02:05:29 +0000 Subject: [PATCH 43/50] feat: enhance documentation with detailed XML comments --- .../Models/Core/Projects/Branch.cs | 20 +++++++++ .../Models/Core/Projects/BranchBase.cs | 10 +++++ .../Models/Core/Projects/Change.cs | 42 +++++++++++++++++++ .../Models/Core/Projects/Comment.cs | 42 +++++++++++++++++++ .../Models/Core/Projects/CommentAnchor.cs | 30 +++++++++++++ .../Models/Core/Projects/CommentRef.cs | 42 +++++++++++++++++++ .../Models/Core/Projects/Commit.cs | 34 +++++++++++++++ .../Models/Core/Projects/CommitParent.cs | 10 +++++ .../Models/Core/Projects/Diff.cs | 14 +++++++ .../Models/Core/Projects/DiffHunk.cs | 26 ++++++++++++ .../Models/Core/Projects/LineRef.cs | 18 ++++++++ .../Models/Core/Projects/Link.cs | 6 +++ .../Models/Core/Projects/Links.cs | 6 +++ .../Models/Core/Projects/Participant.cs | 18 ++++++++ .../Models/Core/Projects/Project.cs | 18 ++++++++ .../Models/Core/Projects/ProjectDefinition.cs | 10 +++++ .../Models/Core/Projects/ProjectRef.cs | 6 +++ .../Models/Core/Projects/PullRequest.cs | 30 +++++++++++++ .../Core/Projects/PullRequestActivity.cs | 34 +++++++++++++++ .../Models/Core/Projects/PullRequestInfo.cs | 38 +++++++++++++++++ .../Models/Core/Projects/Repository.cs | 30 +++++++++++++ .../Models/Core/Projects/RepositoryRef.cs | 14 +++++++ .../Models/Core/Projects/Reviewer.cs | 6 +++ .../Models/Core/Projects/Segment.cs | 14 +++++++ src/Bitbucket.Net/Models/Core/Projects/Tag.cs | 26 ++++++++++++ .../Models/Core/Projects/WithId.cs | 6 +++ .../Models/Core/Tasks/BitbucketTask.cs | 10 +++++ .../Models/Core/Tasks/TaskAnchor.cs | 18 ++++++++ .../Models/Core/Tasks/TaskRef.cs | 26 ++++++++++++ .../Models/Core/Users/Identity.cs | 6 +++ src/Bitbucket.Net/Models/Core/Users/Named.cs | 6 +++ src/Bitbucket.Net/Models/Core/Users/User.cs | 26 ++++++++++++ 32 files changed, 642 insertions(+) diff --git a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs index 965be2c..419388f 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs @@ -3,6 +3,9 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Full Bitbucket branch. Extends with commit info, default status, and parsed metadata. +/// public class Branch : BranchBase { private BranchMetaData? _branchMetadata; @@ -12,10 +15,24 @@ public class Branch : BranchBase DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, }; + /// + /// Gets or sets the SHA of the latest commit on this branch. + /// public string? LatestCommit { get; set; } + + /// + /// Gets or sets the changeset identifier of the latest change on this branch. + /// public string? LatestChangeset { get; set; } + + /// + /// Gets or sets a value indicating whether this is the repository's default branch. + /// public bool IsDefault { get; set; } + /// + /// Gets parsed branch metadata (ahead/behind counts, build status, outgoing pull requests) from the raw JSON. + /// public BranchMetaData? BranchMetadata { get @@ -65,6 +82,9 @@ public BranchMetaData? BranchMetadata } } + /// + /// Gets or sets the raw JSON metadata array returned by Bitbucket Server for this branch. + /// [JsonPropertyName("metadata")] public JsonElement? Metadata { get; set; } diff --git a/src/Bitbucket.Net/Models/Core/Projects/BranchBase.cs b/src/Bitbucket.Net/Models/Core/Projects/BranchBase.cs index 640da12..55577e2 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BranchBase.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BranchBase.cs @@ -1,7 +1,17 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Base branch reference. Extends with a display identifier and ref type. +/// public class BranchBase : WithId { + /// + /// Gets or sets the short display name of the branch (e.g. "main"). + /// public string? DisplayId { get; set; } + + /// + /// Gets or sets the ref type (e.g. "BRANCH" or "TAG"). + /// public string? Type { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Change.cs b/src/Bitbucket.Net/Models/Core/Projects/Change.cs index ffe9417..a95b41b 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Change.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Change.cs @@ -1,15 +1,57 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Represents a file change in a Bitbucket commit or pull request. +/// public class Change { + /// + /// Gets or sets the content hash of the file after the change. + /// public string? ContentId { get; set; } + + /// + /// Gets or sets the content hash of the file before the change. + /// public string? FromContentId { get; set; } + + /// + /// Gets or sets the file path after the change. + /// public Path? Path { get; set; } + + /// + /// Gets or sets a value indicating whether the file is executable after the change. + /// public bool Executable { get; set; } + + /// + /// Gets or sets the percentage of the file that is unchanged. + /// public int PercentUnchanged { get; set; } + + /// + /// Gets or sets the change type (e.g. "ADD", "MODIFY", "DELETE", "MOVE", "COPY"). + /// public string? Type { get; set; } + + /// + /// Gets or sets the node type (e.g. "FILE" or "DIRECTORY"). + /// public string? NodeType { get; set; } + + /// + /// Gets or sets the original file path before a move or copy. + /// public Path? SrcPath { get; set; } + + /// + /// Gets or sets a value indicating whether the file was executable before the change. + /// public bool SrcExecutable { get; set; } + + /// + /// Gets or sets the hypermedia links for this change. + /// public Links? Links { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs index 4d5eb84..b2dd738 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs @@ -5,10 +5,24 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// A comment on a Bitbucket pull request, file, or commit. +/// public class Comment { + /// + /// Gets or sets the server-assigned comment identifier. + /// public int Id { get; set; } + + /// + /// Gets or sets the version number for optimistic locking on updates. + /// public int Version { get; set; } + + /// + /// Gets or sets the comment body text. + /// public string? Text { get; set; } /// @@ -33,13 +47,41 @@ public class Comment /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? ResolvedDate { get; set; } + + /// + /// Gets or sets the date and time when the comment was created. + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } + + /// + /// Gets or sets the date and time when the comment was last updated. + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? UpdatedDate { get; set; } + + /// + /// Gets or sets the user who authored the comment. + /// public User? Author { get; set; } + + /// + /// Gets or sets the nested reply comments. + /// public List? Comments { get; set; } + + /// + /// Gets or sets the tasks associated with this comment. + /// public List? Tasks { get; set; } + + /// + /// Gets or sets the participants in the comment thread. + /// public List? Participants { get; set; } + + /// + /// Gets or sets the hypermedia links for this comment. + /// public Links? Links { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentAnchor.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentAnchor.cs index 1d62bd6..05e6f6c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentAnchor.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentAnchor.cs @@ -3,15 +3,45 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Anchor location for an inline comment on a specific file and line in a pull request diff. +/// public class CommentAnchor { + /// + /// Gets or sets the line number the comment is anchored to. + /// public int? Line { get; set; } + + /// + /// Gets or sets the line type (e.g. ADDED, REMOVED, CONTEXT). + /// [JsonConverter(typeof(LineTypesConverter))] public LineTypes LineType { get; set; } + + /// + /// Gets or sets the file type (e.g. FROM for source, TO for destination). + /// [JsonConverter(typeof(FileTypesConverter))] public FileTypes FileType { get; set; } + + /// + /// Gets or sets the commit hash of the source side of the diff. + /// public string? FromHash { get; set; } + + /// + /// Gets or sets the commit hash of the destination side of the diff. + /// public string? ToHash { get; set; } + + /// + /// Gets or sets the file path the comment is anchored to. + /// public string? Path { get; set; } + + /// + /// Gets or sets the original file path before a rename or move. + /// public string? SrcPath { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs index d9a8908..ecd4c13 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs @@ -5,18 +5,60 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Lightweight comment reference used in task anchors and nested comment structures. +/// public class CommentRef { + /// + /// Gets or sets the additional properties bag. + /// public Properties? Properties { get; set; } + + /// + /// Gets or sets the server-assigned comment identifier. + /// public int Id { get; set; } + + /// + /// Gets or sets the version number for optimistic locking on updates. + /// public int Version { get; set; } + + /// + /// Gets or sets the comment body text. + /// public string? Text { get; set; } + + /// + /// Gets or sets the user who authored the comment. + /// public User? Author { get; set; } + + /// + /// Gets or sets the date and time when the comment was created. + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } + + /// + /// Gets or sets the date and time when the comment was last updated. + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? UpdatedDate { get; set; } + + /// + /// Gets or sets the nested reply comment references. + /// public List? Comments { get; set; } + + /// + /// Gets or sets the tasks associated with this comment. + /// public List? Tasks { get; set; } + + /// + /// Gets or sets the operations the current user is permitted to perform on this comment. + /// public Permittedoperations? PermittedOperations { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Commit.cs b/src/Bitbucket.Net/Models/Core/Projects/Commit.cs index be31aeb..fb6bc7d 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Commit.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Commit.cs @@ -3,16 +3,50 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Full Git commit. Extends with author, committer, message, and parent references. +/// public class Commit : CommitParent { + /// + /// Gets or sets the commit author. + /// public Author? Author { get; set; } + + /// + /// Gets or sets the author timestamp (Unix epoch milliseconds from the Bitbucket API). + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset AuthorTimestamp { get; set; } + + /// + /// Gets or sets the committer (may differ from author in cherry-picks or patches). + /// public Author? Committer { get; set; } + + /// + /// Gets or sets the committer timestamp (Unix epoch milliseconds from the Bitbucket API). + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset CommitterTimestamp { get; set; } + + /// + /// Gets or sets the commit message. + /// public string? Message { get; set; } + + /// + /// Gets or sets the parent commits. + /// public List? Parents { get; set; } + + /// + /// Gets or sets the number of unique authors in the commit range. + /// public int AuthorCount { get; set; } + + /// + /// Gets or sets the total number of commits in the range. + /// public int TotalCount { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommitParent.cs b/src/Bitbucket.Net/Models/Core/Projects/CommitParent.cs index 463bf7c..7f23edd 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommitParent.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommitParent.cs @@ -1,7 +1,17 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Lightweight commit reference containing the full and abbreviated SHA. +/// public class CommitParent { + /// + /// Gets or sets the full commit SHA hash. + /// public string? Id { get; set; } + + /// + /// Gets or sets the abbreviated commit SHA shown in the Bitbucket UI. + /// public string? DisplayId { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Diff.cs b/src/Bitbucket.Net/Models/Core/Projects/Diff.cs index 2bd1f8c..bb78453 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Diff.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Diff.cs @@ -1,8 +1,22 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// A file-level diff. Extends with source/destination paths and hunks. +/// public class Diff : DiffInfo { + /// + /// Gets or sets the source (before) file path. + /// public Path? Source { get; set; } + + /// + /// Gets or sets the destination (after) file path. + /// public Path? Destination { get; set; } + + /// + /// Gets or sets the list of diff hunks containing the actual line changes. + /// public List? Hunks { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs b/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs index 7b45a17..17142a9 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/DiffHunk.cs @@ -1,11 +1,37 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// A hunk within a file diff, describing a contiguous block of changes. +/// public class DiffHunk { + /// + /// Gets or sets the starting line number in the source (before) file. + /// public int SourceLine { get; set; } + + /// + /// Gets or sets the number of lines from the source file included in this hunk. + /// public int SourceSpan { get; set; } + + /// + /// Gets or sets the starting line number in the destination (after) file. + /// public int DestinationLine { get; set; } + + /// + /// Gets or sets the number of lines from the destination file included in this hunk. + /// public int DestinationSpan { get; set; } + + /// + /// Gets or sets the segments (groups of added, removed, or context lines) in this hunk. + /// public List? Segments { get; set; } + + /// + /// Gets or sets a value indicating whether this hunk was truncated by the server. + /// public bool Truncated { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/LineRef.cs b/src/Bitbucket.Net/Models/Core/Projects/LineRef.cs index 5d83201..ba111e8 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/LineRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/LineRef.cs @@ -1,9 +1,27 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// A single line in a diff segment with source and destination line numbers. +/// public class LineRef { + /// + /// Gets or sets the line number in the destination (after) file. + /// public int Destination { get; set; } + + /// + /// Gets or sets the line number in the source (before) file. + /// public int Source { get; set; } + + /// + /// Gets or sets the text content of the line. + /// public string? Line { get; set; } + + /// + /// Gets or sets a value indicating whether this line was truncated by the server. + /// public bool Truncated { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Link.cs b/src/Bitbucket.Net/Models/Core/Projects/Link.cs index 51d7bef..898d940 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Link.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Link.cs @@ -1,7 +1,13 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// A single hyperlink in the Bitbucket REST API response. +/// public class Link { + /// + /// Gets or sets the URL of the link. + /// public string? Href { get; set; } public override string ToString() => Href ?? string.Empty; diff --git a/src/Bitbucket.Net/Models/Core/Projects/Links.cs b/src/Bitbucket.Net/Models/Core/Projects/Links.cs index be82168..7d8811c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Links.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Links.cs @@ -1,6 +1,12 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Hypermedia links for a Bitbucket resource. +/// public class Links { + /// + /// Gets or sets the self-referencing links. + /// public List? Self { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs index 0e63c5b..ffc0f00 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs @@ -4,12 +4,30 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// A participant in a Bitbucket pull request (author, reviewer, or watcher). +/// public class Participant { + /// + /// Gets or sets the participant's user details. + /// public User? User { get; set; } + + /// + /// Gets or sets the participant's role (e.g. AUTHOR, REVIEWER, PARTICIPANT). + /// [JsonConverter(typeof(RolesConverter))] public Roles Role { get; set; } + + /// + /// Gets or sets a value indicating whether the participant has approved the pull request. + /// public bool Approved { get; set; } + + /// + /// Gets or sets the participant's review status (e.g. APPROVED, UNAPPROVED, NEEDS_WORK). + /// [JsonConverter(typeof(ParticipantStatusConverter))] public ParticipantStatus Status { get; set; } diff --git a/src/Bitbucket.Net/Models/Core/Projects/Project.cs b/src/Bitbucket.Net/Models/Core/Projects/Project.cs index 3a1b36a..8f35fc2 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Project.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Project.cs @@ -1,10 +1,28 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Full Bitbucket project. Extends with server-assigned identity and metadata. +/// public class Project : ProjectDefinition { + /// + /// Gets or sets the server-assigned project identifier. + /// public int Id { get; set; } + + /// + /// Gets or sets a value indicating whether the project is publicly accessible. + /// public bool Public { get; set; } + + /// + /// Gets or sets the project type (e.g. "NORMAL" or "PERSONAL"). + /// public string? Type { get; set; } + + /// + /// Gets or sets the hypermedia links for this project. + /// public Links? Links { get; set; } public override string ToString() => Name ?? string.Empty; diff --git a/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs b/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs index b882e9a..c45f711 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ProjectDefinition.cs @@ -1,7 +1,17 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Extends with a human-readable name and description. +/// public class ProjectDefinition : ProjectRef { + /// + /// Gets or sets the project display name. + /// public string? Name { get; set; } + + /// + /// Gets or sets the project description. + /// public string? Description { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/ProjectRef.cs b/src/Bitbucket.Net/Models/Core/Projects/ProjectRef.cs index 5757caa..2e8fdcc 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/ProjectRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/ProjectRef.cs @@ -1,6 +1,12 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Lightweight reference to a Bitbucket project, identified by its key. +/// public class ProjectRef { + /// + /// Gets or sets the unique project key (e.g. "PRJ"). + /// public string? Key { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs index ef6260e..33f9933 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs @@ -3,16 +3,46 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Full Bitbucket pull request. Extends with server-assigned identity, timestamps, and participants. +/// public class PullRequest : PullRequestInfo { + /// + /// Gets or sets the server-assigned pull request identifier. + /// public int Id { get; set; } + + /// + /// Gets or sets the version number for optimistic locking on updates. + /// public int Version { get; set; } + + /// + /// Gets or sets the date and time when the pull request was created. + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } + + /// + /// Gets or sets the date and time when the pull request was last updated. + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? UpdatedDate { get; set; } + + /// + /// Gets or sets the pull request author. + /// public Participant? Author { get; set; } + + /// + /// Gets or sets the list of participants (author, reviewers, and watchers). + /// public List? Participants { get; set; } + + /// + /// Gets or sets the hypermedia links for this pull request. + /// public Links? Links { get; set; } public override string ToString() => $"{Author?.User?.DisplayName ?? "Unknown"}: {Title ?? "(untitled)"}"; diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs index 7f5879d..f60301b 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs @@ -4,15 +4,49 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// An activity event on a Bitbucket pull request (e.g. comment, approval, merge). +/// public class PullRequestActivity { + /// + /// Gets or sets the server-assigned activity identifier. + /// public int Id { get; set; } + + /// + /// Gets or sets the date and time when the activity occurred. + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } + + /// + /// Gets or sets the user who performed the activity. + /// public User? User { get; set; } + + /// + /// Gets or sets the activity action type (e.g. "COMMENTED", "APPROVED", "MERGED", "OPENED"). + /// public string? Action { get; set; } + + /// + /// Gets or sets the comment-specific action (e.g. "ADDED", "UPDATED", "DELETED") when the activity involves a comment. + /// public string? CommentAction { get; set; } + + /// + /// Gets or sets the comment associated with this activity, if any. + /// public Comment? Comment { get; set; } + + /// + /// Gets or sets the anchor location for an inline comment, if applicable. + /// public CommentAnchor? CommentAnchor { get; set; } + + /// + /// Gets or sets the commit associated with this activity, if any. + /// public Commit? Commit { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs index fcf8084..71cf44c 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestInfo.cs @@ -3,16 +3,54 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Core pull request data used when creating or updating a pull request. +/// public class PullRequestInfo { + /// + /// Gets or sets the pull request title. + /// public string? Title { get; set; } + + /// + /// Gets or sets the pull request description (supports Markdown). + /// public string? Description { get; set; } + + /// + /// Gets or sets the pull request state (e.g. OPEN, MERGED, DECLINED). + /// [JsonConverter(typeof(PullRequestStatesConverter))] public PullRequestStates State { get; set; } + + /// + /// Gets or sets a value indicating whether the pull request is open. + /// public bool Open { get; set; } + + /// + /// Gets or sets a value indicating whether the pull request is closed. + /// public bool Closed { get; set; } + + /// + /// Gets or sets the source branch reference. + /// public FromToRef? FromRef { get; set; } + + /// + /// Gets or sets the target branch reference. + /// public FromToRef? ToRef { get; set; } + + /// + /// Gets or sets a value indicating whether the pull request is locked from further changes. + /// public bool Locked { get; set; } + + /// + /// Gets or sets the list of assigned reviewers. + /// public List? Reviewers { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Repository.cs b/src/Bitbucket.Net/Models/Core/Projects/Repository.cs index 0a46f93..f38b6fe 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Repository.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Repository.cs @@ -1,13 +1,43 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Full Bitbucket repository. Extends with server-assigned identity and metadata. +/// public class Repository : RepositoryRef { + /// + /// Gets or sets the server-assigned repository identifier. + /// public int Id { get; set; } + + /// + /// Gets or sets the SCM type identifier (e.g. "git"). + /// public string? ScmId { get; set; } + + /// + /// Gets or sets the repository state (e.g. "AVAILABLE"). + /// public string? State { get; set; } + + /// + /// Gets or sets a human-readable status message for the repository. + /// public string? StatusMessage { get; set; } + + /// + /// Gets or sets a value indicating whether the repository can be forked. + /// public bool Forkable { get; set; } + + /// + /// Gets or sets a value indicating whether the repository is publicly accessible. + /// public bool Public { get; set; } + + /// + /// Gets or sets the clone URLs and other hypermedia links for this repository. + /// public CloneLinks? Links { get; set; } public override string ToString() => Name ?? string.Empty; diff --git a/src/Bitbucket.Net/Models/Core/Projects/RepositoryRef.cs b/src/Bitbucket.Net/Models/Core/Projects/RepositoryRef.cs index 86f7374..5f50cfb 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/RepositoryRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/RepositoryRef.cs @@ -1,8 +1,22 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Lightweight reference to a Bitbucket repository, identified by slug and parent project. +/// public class RepositoryRef { + /// + /// Gets or sets the URL-friendly repository identifier. + /// public string? Slug { get; set; } + + /// + /// Gets or sets the repository display name. + /// public string? Name { get; set; } + + /// + /// Gets or sets the parent project reference. + /// public ProjectRef? Project { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Reviewer.cs b/src/Bitbucket.Net/Models/Core/Projects/Reviewer.cs index 8f1c3a6..6095f34 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Reviewer.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Reviewer.cs @@ -1,6 +1,12 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// A pull request reviewer. Extends with the last-reviewed commit reference. +/// public class Reviewer : Participant { + /// + /// Gets or sets the SHA of the last commit the reviewer has reviewed. + /// public string? LastReviewedCommit { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Segment.cs b/src/Bitbucket.Net/Models/Core/Projects/Segment.cs index 3b72f7a..4f0d314 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Segment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Segment.cs @@ -1,8 +1,22 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// A segment within a diff hunk, grouping consecutive lines of the same type. +/// public class Segment { + /// + /// Gets or sets the segment type (e.g. "ADDED", "REMOVED", or "CONTEXT"). + /// public string? Type { get; set; } + + /// + /// Gets or sets the lines in this segment. + /// public List? Lines { get; set; } + + /// + /// Gets or sets a value indicating whether this segment was truncated by the server. + /// public bool Truncated { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Tag.cs b/src/Bitbucket.Net/Models/Core/Projects/Tag.cs index f390624..be724f5 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Tag.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Tag.cs @@ -1,11 +1,37 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// A Git tag in a Bitbucket repository. +/// public class Tag { + /// + /// Gets or sets the full tag ref path (e.g. "refs/tags/v1.0"). + /// public string? Id { get; set; } + + /// + /// Gets or sets the short tag name shown in the Bitbucket UI. + /// public string? DisplayId { get; set; } + + /// + /// Gets or sets the ref type (typically "TAG"). + /// public string? Type { get; set; } + + /// + /// Gets or sets the SHA of the latest commit this tag points to. + /// public string? LatestCommit { get; set; } + + /// + /// Gets or sets the changeset identifier of the latest change. + /// public string? LatestChangeset { get; set; } + + /// + /// Gets or sets the tag object hash (for annotated tags). + /// public string? Hash { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/WithId.cs b/src/Bitbucket.Net/Models/Core/Projects/WithId.cs index 2511472..f2f7c5e 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/WithId.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/WithId.cs @@ -1,6 +1,12 @@ namespace Bitbucket.Net.Models.Core.Projects; +/// +/// Base class for Bitbucket entities identified by a string identifier. +/// public class WithId { + /// + /// Gets or sets the unique identifier (typically a Git ref path such as "refs/heads/main"). + /// public string? Id { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTask.cs b/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTask.cs index 1da05e9..25449d6 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTask.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/BitbucketTask.cs @@ -1,7 +1,17 @@ namespace Bitbucket.Net.Models.Core.Tasks; +/// +/// A Bitbucket pull request task. Extends with an anchor and state. +/// public class BitbucketTask : TaskRef { + /// + /// Gets or sets the comment anchor this task is attached to. + /// public TaskAnchor? Anchor { get; set; } + + /// + /// Gets or sets the task state (e.g. "OPEN" or "RESOLVED"). + /// public string? State { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs index cf32816..ae3b006 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs @@ -4,11 +4,29 @@ namespace Bitbucket.Net.Models.Core.Tasks; +/// +/// A task anchor that represents the comment a task is attached to. Extends with versioning and nested content. +/// public class TaskAnchor : TaskRef { + /// + /// Gets or sets the version number for optimistic locking on updates. + /// public int Version { get; set; } + + /// + /// Gets or sets the date and time when the anchor was last updated. + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? UpdatedDate { get; set; } + + /// + /// Gets or sets the nested comment references on this anchor. + /// public List? Comments { get; set; } + + /// + /// Gets or sets the tasks associated with this anchor. + /// public List? Tasks { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs index 3864198..9306753 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs @@ -5,13 +5,39 @@ namespace Bitbucket.Net.Models.Core.Tasks; +/// +/// Abstract base class for Bitbucket tasks with identity, text, author, and creation date. +/// public abstract class TaskRef { + /// + /// Gets or sets the additional properties bag. + /// public Properties? Properties { get; set; } + + /// + /// Gets or sets the server-assigned task identifier. + /// public int Id { get; set; } + + /// + /// Gets or sets the task description text. + /// public string? Text { get; set; } + + /// + /// Gets or sets the user who created the task. + /// public User? Author { get; set; } + + /// + /// Gets or sets the date and time when the task was created. + /// [JsonConverter(typeof(UnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } + + /// + /// Gets or sets the operations the current user is permitted to perform on this task. + /// public Permittedoperations? PermittedOperations { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Users/Identity.cs b/src/Bitbucket.Net/Models/Core/Users/Identity.cs index f08ee5e..a54e30a 100644 --- a/src/Bitbucket.Net/Models/Core/Users/Identity.cs +++ b/src/Bitbucket.Net/Models/Core/Users/Identity.cs @@ -1,6 +1,12 @@ namespace Bitbucket.Net.Models.Core.Users; +/// +/// Extends with an email address. +/// public class Identity : Named { + /// + /// Gets or sets the email address associated with the identity. + /// public string? EmailAddress { get; set; } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Users/Named.cs b/src/Bitbucket.Net/Models/Core/Users/Named.cs index fb5b069..83cb021 100644 --- a/src/Bitbucket.Net/Models/Core/Users/Named.cs +++ b/src/Bitbucket.Net/Models/Core/Users/Named.cs @@ -1,7 +1,13 @@ namespace Bitbucket.Net.Models.Core.Users; +/// +/// Base class for Bitbucket entities that have a name. +/// public class Named { + /// + /// Gets or sets the entity name (typically the username for users). + /// public string? Name { get; set; } public override string ToString() => Name ?? string.Empty; diff --git a/src/Bitbucket.Net/Models/Core/Users/User.cs b/src/Bitbucket.Net/Models/Core/Users/User.cs index f146118..4405c1a 100644 --- a/src/Bitbucket.Net/Models/Core/Users/User.cs +++ b/src/Bitbucket.Net/Models/Core/Users/User.cs @@ -1,12 +1,38 @@ namespace Bitbucket.Net.Models.Core.Users; +/// +/// Full Bitbucket user. Extends with server-assigned identity, display name, and account state. +/// public class User : Identity { + /// + /// Gets or sets the server-assigned user identifier. + /// public int Id { get; set; } + + /// + /// Gets or sets the user's display name. + /// public string? DisplayName { get; set; } + + /// + /// Gets or sets a value indicating whether the user account is active. + /// public bool Active { get; set; } + + /// + /// Gets or sets the URL-friendly user identifier. + /// public string? Slug { get; set; } + + /// + /// Gets or sets the user type (e.g. "NORMAL" or "SERVICE"). + /// public string? Type { get; set; } + + /// + /// Gets or sets the URL of the user's avatar image. + /// public string? AvatarUrl { get; set; } public override string ToString() => DisplayName ?? string.Empty; From dc562093a1eea876f8ecdec7d9ec8e0674ebcae5 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 12:03:23 +0000 Subject: [PATCH 44/50] refactor: Split the massive Projects client into dedicated clients for each area --- .../Core/Projects/BitbucketClient.Branches.cs | 332 ++ .../Core/Projects/BitbucketClient.Commits.cs | 547 ++ .../Core/Projects/BitbucketClient.Compare.cs | 314 ++ .../Core/Projects/BitbucketClient.Projects.cs | 483 ++ .../BitbucketClient.PullRequestComments.cs | 269 + .../BitbucketClient.PullRequestDetails.cs | 436 ++ .../Projects/BitbucketClient.PullRequests.cs | 552 ++ .../Projects/BitbucketClient.Repositories.cs | 547 ++ .../BitbucketClient.RepositorySettings.cs | 595 +++ .../Core/Projects/BitbucketClient.Tasks.cs | 507 ++ .../Core/Projects/BitbucketClient.cs | 4491 ----------------- 11 files changed, 4582 insertions(+), 4491 deletions(-) create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.Branches.cs create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.Commits.cs create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.Compare.cs create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.Projects.cs create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequestComments.cs create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequestDetails.cs create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequests.cs create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.Repositories.cs create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.RepositorySettings.cs create mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.Tasks.cs delete mode 100644 src/Bitbucket.Net/Core/Projects/BitbucketClient.cs diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.Branches.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Branches.cs new file mode 100644 index 0000000..592ac82 --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Branches.cs @@ -0,0 +1,332 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Projects; +using Flurl.Http; +using System.Buffers; +using System.Runtime.CompilerServices; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Retrieves branches for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional base branch or tag filter. + /// Whether to include additional details. + /// Optional branch name filter. + /// Optional branch ordering. + /// Cancellation token. + /// A collection of branches. + public async Task> GetBranchesAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + string? baseBranchOrTag = null, + bool? details = null, + string? filterText = null, + BranchOrderBy? orderBy = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["base"] = baseBranchOrTag, + ["details"] = details.HasValue ? BitbucketHelpers.BoolToString(details.Value) : null, + ["filterText"] = filterText, + ["orderBy"] = orderBy.HasValue ? BitbucketHelpers.BranchOrderByToString(orderBy.Value) : null, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams all branches for a repository as an IAsyncEnumerable. + /// + public IAsyncEnumerable GetBranchesStreamAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + string? baseBranchOrTag = null, + bool? details = null, + string? filterText = null, + BranchOrderBy? orderBy = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["base"] = baseBranchOrTag, + ["details"] = details.HasValue ? BitbucketHelpers.BoolToString(details.Value) : null, + ["filterText"] = filterText, + ["orderBy"] = orderBy.HasValue ? BitbucketHelpers.BranchOrderByToString(orderBy.Value) : null, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Creates a branch in a repository. + /// + /// The project key. + /// The repository slug. + /// The branch information. + /// Cancellation token. + /// The created branch. + public async Task CreateBranchAsync(string projectKey, string repositorySlug, BranchInfo branchInfo, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .SendAsync(HttpMethod.Post, CreateJsonContent(branchInfo), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves the default branch for a repository. + /// + /// The project key. + /// The repository slug. + /// Cancellation token. + /// The default branch. + public async Task GetDefaultBranchAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches/default") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Sets the default branch for a repository. + /// + /// The project key. + /// The repository slug. + /// The target branch reference. + /// Cancellation token. + /// true if the default branch was updated; otherwise, false. + public async Task SetDefaultBranchAsync(string projectKey, string repositorySlug, BranchRef branchRef, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") + .SendAsync(HttpMethod.Put, CreateJsonContent(branchRef), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Browses repository content at a specific ref. + /// + /// The project key. + /// The repository slug. + /// The ref (branch, tag, or commit). + /// Whether to include type information. + /// Whether to include blame metadata. + /// If true and blame is requested, omit file content. + /// Cancellation token. + /// The browsed item metadata. + public async Task BrowseProjectRepositoryAsync(string projectKey, string repositorySlug, string at, bool type = false, + bool blame = false, + bool noContent = false, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["at"] = at, + ["type"] = BitbucketHelpers.BoolToString(type), + }; + if (blame) + { + queryParamValues.Add("blame", value: null); + } + if (blame && noContent) + { + queryParamValues.Add("noContent", value: null); + } + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/browse") + .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Browses a specific path within a repository at a given ref. + /// + /// The project key. + /// The repository slug. + /// The path to browse. + /// The ref (branch, tag, or commit). + /// Whether to include type information. + /// Whether to include blame metadata. + /// If true and blame is requested, omit file content. + /// Cancellation token. + /// The browsed path item metadata. + public async Task BrowseProjectRepositoryPathAsync(string projectKey, string repositorySlug, string path, string at, bool type = false, + bool blame = false, + bool noContent = false, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["at"] = at, + ["type"] = BitbucketHelpers.BoolToString(type), + }; + if (blame) + { + queryParamValues.Add("blame", value: null); + } + if (blame && noContent) + { + queryParamValues.Add("noContent", value: null); + } + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") + .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Gets the raw content of a file as a stream. This is optimal for large files as it doesn't buffer the entire content in memory. + /// + /// The project key. + /// The repository slug. + /// The file path within the repository. + /// Optional ref (branch, tag, or commit) to get the file content at. Defaults to default branch. + /// Cancellation token. + /// A stream containing the raw file content. Caller is responsible for disposing. + public async Task GetRawFileContentStreamAsync(string projectKey, string repositorySlug, string path, + string? at = null, + CancellationToken cancellationToken = default) + { + var request = GetProjectsReposUrl(projectKey, repositorySlug, $"/raw/{path}"); + + if (!string.IsNullOrEmpty(at)) + { + request = request.SetQueryParam("at", at); + } + + var response = await request + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + return await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Streams the raw content of a file line by line. This is optimal for large text files. + /// + /// The project key. + /// The repository slug. + /// The file path within the repository. + /// Optional ref (branch, tag, or commit) to get the file content at. Defaults to default branch. + /// Cancellation token. + /// An async enumerable of lines from the file. + public async IAsyncEnumerable GetRawFileContentLinesStreamAsync(string projectKey, string repositorySlug, string path, + string? at = null, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var stream = await GetRawFileContentStreamAsync(projectKey, repositorySlug, path, at, cancellationToken).ConfigureAwait(false); + + await using (stream.ConfigureAwait(false)) + { + using var reader = new StreamReader(stream); + while (!reader.EndOfStream) + { + cancellationToken.ThrowIfCancellationRequested(); + var line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); + if (line is not null) + { + yield return line; + } + } + } + } + + /// + /// Updates a file at the specified path in the repository. + /// Uses ArrayPool<byte> for zero-copy buffer management to minimize heap allocations. + /// + public async Task UpdateProjectRepositoryPathAsync(string projectKey, string repositorySlug, string path, + string fileName, + string branch, + string? message = null, + string? sourceCommitId = null, + string? sourceBranch = null, + CancellationToken cancellationToken = default) + { + if (!File.Exists(fileName)) + { + throw new ArgumentException($"File doesn't exist: {fileName}", nameof(fileName)); + } + + var fileInfo = new FileInfo(fileName); + int fileSize = checked((int)fileInfo.Length); + + // Use ArrayPool to rent a buffer instead of allocating new array + byte[] buffer = ArrayPool.Shared.Rent(fileSize); + try + { + int bytesRead; + var stm = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); + await using (stm.ConfigureAwait(false)) + { + bytesRead = await stm.ReadAsync(buffer.AsMemory(0, fileSize), cancellationToken).ConfigureAwait(false); + } + + // Create MemoryStream over the exact bytes read (not the rented buffer size) + using var memoryStream = new MemoryStream(buffer, 0, bytesRead, writable: false); + + var data = new DynamicMultipartFormDataContent + { + { new StreamContent(memoryStream), "content" }, + { new StringContent(branch), "branch" }, + { message, message == null ? null : new StringContent(message), "message" }, + { sourceCommitId, sourceCommitId == null ? null : new StringContent(sourceCommitId), "sourceCommitId" }, + { sourceBranch, sourceBranch == null ? null : new StringContent(sourceBranch), "sourceBranch" }, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") + .PutAsync(data.ToMultipartFormDataContent(), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + finally + { + // Always return the buffer to the pool + ArrayPool.Shared.Return(buffer); + } + } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.Commits.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Commits.cs new file mode 100644 index 0000000..ac8a753 --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Commits.cs @@ -0,0 +1,547 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Projects; +using Flurl.Http; +using System.Runtime.CompilerServices; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Retrieves changes for a repository between two refs. + /// + /// The project key. + /// The repository slug. + /// The target ref. + /// Optional starting ref. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of changes. + public async Task> GetChangesAsync(string projectKey, string repositorySlug, string until, string? since = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["since"] = since, + ["until"] = until, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams changes for a repository between refs, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The target ref. + /// Optional starting ref. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of changes. + public IAsyncEnumerable GetChangesStreamAsync(string projectKey, string repositorySlug, string until, string? since = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["since"] = since, + ["until"] = until, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Retrieves commits for a repository. + /// + /// The project key. + /// The repository slug. + /// The ref to retrieve commits until. + /// Whether to follow renames. + /// Whether to ignore missing commits. + /// Merge commit inclusion policy. + /// Optional path filter. + /// Optional starting ref. + /// Whether to include commit counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of commits. + public async Task> GetCommitsAsync(string projectKey, string repositorySlug, + string until, + bool followRenames = false, + bool ignoreMissing = false, + MergeCommits merges = MergeCommits.Exclude, + string? path = null, + string? since = null, + bool withCounts = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["followRenames"] = BitbucketHelpers.BoolToString(followRenames), + ["ignoreMissing"] = BitbucketHelpers.BoolToString(ignoreMissing), + ["merges"] = BitbucketHelpers.MergeCommitsToString(merges), + ["path"] = path, + ["since"] = since, + ["until"] = until, + ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams all commits for a repository as an IAsyncEnumerable. + /// + public IAsyncEnumerable GetCommitsStreamAsync(string projectKey, string repositorySlug, + string until, + bool followRenames = false, + bool ignoreMissing = false, + MergeCommits merges = MergeCommits.Exclude, + string? path = null, + string? since = null, + bool withCounts = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["followRenames"] = BitbucketHelpers.BoolToString(followRenames), + ["ignoreMissing"] = BitbucketHelpers.BoolToString(ignoreMissing), + ["merges"] = BitbucketHelpers.MergeCommitsToString(merges), + ["path"] = path, + ["since"] = since, + ["until"] = until, + ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Retrieves a commit by ID. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Optional path filter. + /// Cancellation token. + /// The requested commit. + public async Task GetCommitAsync(string projectKey, string repositorySlug, string commitId, string? path = null, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["path"] = path, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves the list of file changes for a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Optional starting commit ID. + /// Whether to include comment counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of changes. + public async Task> GetCommitChangesAsync(string projectKey, string repositorySlug, string commitId, + string? since = null, + bool withComments = true, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["since"] = since, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams changes for a specific commit, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Optional starting commit ID. + /// Whether to include comment counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of changes. + public IAsyncEnumerable GetCommitChangesStreamAsync(string projectKey, string repositorySlug, string commitId, + string? since = null, + bool withComments = true, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["since"] = since, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Retrieves comments for a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The file path within the commit. + /// Optional starting comment ID. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of comments. + public async Task> GetCommitCommentsAsync(string projectKey, string repositorySlug, string commitId, + string path, + string? since = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["path"] = path, + ["since"] = since, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Creates a comment on a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The comment payload. + /// Optional starting comment ID for context. + /// Cancellation token. + /// The created comment reference. + public async Task CreateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, + CommentInfo commentInfo, string? since = null, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["since"] = since, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") + .SetQueryParams(queryParamValues) + .SendAsync(HttpMethod.Post, CreateJsonContent(commentInfo), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a specific commit comment by ID. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The comment ID. + /// Optional avatar size. + /// Cancellation token. + /// The requested comment reference. + public async Task GetCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates the text of a commit comment. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The comment ID. + /// The updated comment text. + /// Cancellation token. + /// The updated comment reference. + public async Task UpdateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, + CommentText commentText, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") + .SendAsync(HttpMethod.Put, CreateJsonContent(commentText), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a commit comment. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// The comment ID. + /// Optional comment version for concurrency. + /// Cancellation token. + /// true if deleted; otherwise, false. + public async Task DeleteCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, + int version = -1, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["version"] = version, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") + .SetQueryParams(queryParamValues) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a diff for a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Whether to auto-detect source path. + /// Context lines to include. + /// Optional since commit. + /// Optional source path filter. + /// Whitespace handling strategy. + /// Whether to include comments. + /// Cancellation token. + /// The diff result. + public async Task GetCommitDiffAsync(string projectKey, string repositorySlug, string commitId, + bool autoSrcPath = false, + int contextLines = -1, + string? since = null, + string? srcPath = null, + string whitespace = "ignore-all", + bool withComments = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), + ["contextLines"] = contextLines, + ["since"] = since, + ["srcPath"] = srcPath, + ["whitespace"] = whitespace, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Streams the diff for a specific commit, yielding individual diff entries as they are parsed. + /// This is more memory-efficient for large diffs. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Auto source path. + /// Number of context lines. + /// Since commit. + /// Source path filter. + /// Whitespace handling. + /// Include comments. + /// Cancellation token. + /// An async enumerable of diffs. + public async IAsyncEnumerable GetCommitDiffStreamAsync(string projectKey, string repositorySlug, string commitId, + bool autoSrcPath = false, + int contextLines = -1, + string? since = null, + string? srcPath = null, + string whitespace = "ignore-all", + bool withComments = true, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), + ["contextLines"] = contextLines, + ["since"] = since, + ["srcPath"] = srcPath, + ["whitespace"] = whitespace, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + + await using (responseStream.ConfigureAwait(false)) + { + await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) + { + yield return diff; + } + } + } + + /// + /// Starts watching a commit for notifications. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Cancellation token. + /// true if watch was created; otherwise, false. + public async Task CreateCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Stops watching a commit. + /// + /// The project key. + /// The repository slug. + /// The commit ID. + /// Cancellation token. + /// true if the watch was removed; otherwise, false. + public async Task DeleteCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.Compare.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Compare.cs new file mode 100644 index 0000000..fcdd17d --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Compare.cs @@ -0,0 +1,314 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Projects; +using Flurl.Http; +using System.Runtime.CompilerServices; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Compares two refs and returns the list of changes. + /// + /// The project key. + /// The repository slug. + /// The source ref. + /// The target ref. + /// Optional source repository key for cross-repo compare. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of changes between the refs. + public async Task> GetRepositoryCompareChangesAsync(string projectKey, string repositorySlug, string from, string to, + string? fromRepo = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["from"] = from, + ["to"] = to, + ["fromRepo"] = fromRepo, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Compares two refs and returns a diff. + /// + /// The project key. + /// The repository slug. + /// The source ref. + /// The target ref. + /// Optional source repository key for cross-repo compare. + /// Optional source path filter. + /// Number of context lines. + /// Whitespace handling strategy. + /// Cancellation token. + /// The diff between the refs. + public async Task GetRepositoryCompareDiffAsync(string projectKey, string repositorySlug, string from, string to, + string? fromRepo = null, + string? srcPath = null, + int contextLines = -1, + string whitespace = "ignore-all", + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["from"] = from, + ["to"] = to, + ["fromRepo"] = fromRepo, + ["srcPath"] = srcPath, + ["contextLines"] = contextLines, + ["whitespace"] = whitespace, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Streams the compare diff between two refs, yielding individual diff entries as they are parsed. + /// This is more memory-efficient for large diffs. + /// + /// The project key. + /// The repository slug. + /// The source ref (branch, tag, or commit). + /// The target ref (branch, tag, or commit). + /// Optional source repository if comparing across forks. + /// Source path filter. + /// Number of context lines. + /// Whitespace handling. + /// Cancellation token. + /// An async enumerable of diffs. + public async IAsyncEnumerable GetRepositoryCompareDiffStreamAsync(string projectKey, string repositorySlug, string from, string to, + string? fromRepo = null, + string? srcPath = null, + int contextLines = -1, + string whitespace = "ignore-all", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["from"] = from, + ["to"] = to, + ["fromRepo"] = fromRepo, + ["srcPath"] = srcPath, + ["contextLines"] = contextLines, + ["whitespace"] = whitespace, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + + await using (responseStream.ConfigureAwait(false)) + { + await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) + { + yield return diff; + } + } + } + + /// + /// Compares two refs and returns the commits between them. + /// + /// The project key. + /// The repository slug. + /// The source ref. + /// The target ref. + /// Optional source repository key for cross-repo compare. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of commits between the refs. + public async Task> GetRepositoryCompareCommitsAsync(string projectKey, string repositorySlug, string from, string to, + string? fromRepo = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["from"] = from, + ["to"] = to, + ["fromRepo"] = fromRepo, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/commits") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Retrieves a repository diff between two commits. + /// + /// The project key. + /// The repository slug. + /// The target commit ID. + /// Number of context lines. + /// Optional starting commit ID. + /// Optional source path filter. + /// Whitespace handling strategy. + /// Cancellation token. + /// The diff result. + public async Task GetRepositoryDiffAsync(string projectKey, string repositorySlug, string until, + int contextLines = -1, + string? since = null, + string? srcPath = null, + string whitespace = "ignore-all", + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["contextLines"] = contextLines, + ["since"] = since, + ["srcPath"] = srcPath, + ["until"] = until, + ["whitespace"] = whitespace, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Streams the repository diff, yielding individual diff entries as they are parsed. + /// This is more memory-efficient for large diffs. + /// + /// The project key. + /// The repository slug. + /// The commit ID to diff until. + /// Number of context lines. + /// The commit ID to diff since. + /// Source path filter. + /// Whitespace handling. + /// Cancellation token. + /// An async enumerable of diffs. + public async IAsyncEnumerable GetRepositoryDiffStreamAsync(string projectKey, string repositorySlug, string until, + int contextLines = -1, + string? since = null, + string? srcPath = null, + string whitespace = "ignore-all", + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["contextLines"] = contextLines, + ["since"] = since, + ["srcPath"] = srcPath, + ["until"] = until, + ["whitespace"] = whitespace, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + + await using (responseStream.ConfigureAwait(false)) + { + await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) + { + yield return diff; + } + } + } + + /// + /// Retrieves file paths in a repository at the specified ref. + /// + /// The project key. + /// The repository slug. + /// Optional ref (branch, tag, commit). + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of file paths. + public async Task> GetRepositoryFilesAsync(string projectKey, string repositorySlug, string? at = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["at"] = at, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/files") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Retrieves last-modified metadata for a repository at a ref. + /// + /// The project key. + /// The repository slug. + /// The ref (branch, tag, or commit). + /// Cancellation token. + /// Last modified information. + public async Task GetProjectRepositoryLastModifiedAsync(string projectKey, string repositorySlug, string at, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/last-modified") + .SetQueryParam("at", at) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.Projects.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Projects.cs new file mode 100644 index 0000000..58f43da --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Projects.cs @@ -0,0 +1,483 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Admin; +using Bitbucket.Net.Models.Core.Projects; +using Flurl.Http; +using System.Text.Json; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Gets the base projects URL. + /// + /// An targeting the projects endpoint. + private IFlurlRequest GetProjectsUrl() => GetBaseUrl() + .AppendPathSegment("/projects"); + + /// + /// Gets the projects URL for the specified path. + /// + /// The path to append. + /// An pointing to the projects path. + private IFlurlRequest GetProjectsUrl(string path) => GetProjectsUrl() + .AppendPathSegment(path); + + /// + /// Gets the URL for a specific project. + /// + /// The project key. + /// An pointing to the project. + private IFlurlRequest GetProjectUrl(string projectKey) => GetProjectsUrl() + .AppendPathSegment($"/{projectKey}"); + + /// + /// Gets the URL for a repository within a project. + /// + /// The project key. + /// The repository slug. + /// An pointing to the repository. + private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug) => GetProjectsUrl($"/{projectKey}/repos/{repositorySlug}"); + + /// + /// Gets the URL for a specific path within a project repository. + /// + /// The project key. + /// The repository slug. + /// The additional path to append. + /// An pointing to the repository path. + private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug, string path) => GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment(path); + + /// + /// Retrieves projects accessible to the current user. + /// + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional project name filter. + /// Optional permission filter. + /// Token to cancel the operation. + /// A collection of projects. + public async Task> GetProjectsAsync( + int? maxPages = null, + int? limit = null, + int? start = null, + string? name = null, + Permissions? permission = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["name"] = name, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsUrl() + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams projects accessible to the current user as they are retrieved, improving memory efficiency for large result sets. + /// + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional project name filter. + /// Optional permission filter. + /// Token to cancel the operation. + /// An async enumerable of projects. + public IAsyncEnumerable GetProjectsStreamAsync( + int? maxPages = null, + int? limit = null, + int? start = null, + string? name = null, + Permissions? permission = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["name"] = name, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsUrl() + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Creates a project. + /// + /// The project definition. + /// Token to cancel the operation. + /// The created project. + public async Task CreateProjectAsync(ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl() + .SendAsync(HttpMethod.Post, CreateJsonContent(projectDefinition), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a project. + /// + /// The project key. + /// Token to cancel the operation. + /// true if deletion succeeded; otherwise, false. + public async Task DeleteProjectAsync(string projectKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl($"/{projectKey}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a project. + /// + /// The project key. + /// The updated project definition. + /// Token to cancel the operation. + /// The updated project. + public async Task UpdateProjectAsync(string projectKey, ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl($"/{projectKey}") + .SendAsync(HttpMethod.Put, CreateJsonContent(projectDefinition), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a project by key. + /// + /// The project key. + /// Token to cancel the operation. + /// The requested project. + public async Task GetProjectAsync(string projectKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl($"/{projectKey}") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves user permissions for a project. + /// + /// The project key. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// A collection of user permissions. + public async Task> GetProjectUserPermissionsAsync(string projectKey, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/users") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Removes a user's permissions from a project. + /// + /// The project key. + /// The user name. + /// Token to cancel the operation. + /// true if removal succeeded; otherwise, false. + public async Task DeleteProjectUserPermissionsAsync(string projectKey, string userName, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["name"] = userName, + }; + + var response = await GetProjectsUrl($"/{projectKey}/permissions/users") + .SetQueryParams(queryParamValues) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a user's permissions for a project. + /// + /// The project key. + /// The user name. + /// The permission to grant. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. + public async Task UpdateProjectUserPermissionsAsync(string projectKey, string userName, Permissions permission, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["name"] = userName, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; + + var response = await GetProjectsUrl($"/{projectKey}/permissions/users") + .SetQueryParams(queryParamValues) + .PutAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves users with no permissions on a project. + /// + /// The project key. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of licensed users without project permissions. + public async Task> GetProjectUserPermissionsNoneAsync(string projectKey, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/users/none") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Retrieves group permissions for a project. + /// + /// The project key. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of group permissions. + public async Task> GetProjectGroupPermissionsAsync(string projectKey, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Removes a group's permissions from a project. + /// + /// The project key. + /// The group name. + /// Token to cancel the operation. + /// true if the group permissions were removed; otherwise, false. + public async Task DeleteProjectGroupPermissionsAsync(string projectKey, string groupName, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["name"] = groupName, + }; + + var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") + .SetQueryParams(queryParamValues) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a group's permissions for a project. + /// + /// The project key. + /// The group name. + /// The permission to grant. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. + public async Task UpdateProjectGroupPermissionsAsync(string projectKey, string groupName, Permissions permission, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["name"] = groupName, + ["permission"] = BitbucketHelpers.PermissionToString(permission), + }; + + var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") + .SetQueryParams(queryParamValues) + .PutAsync(new StringContent(""), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves groups that currently have no permissions on a project. + /// + /// The project key. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of licensed users representing groups without permissions. + public async Task> GetProjectGroupPermissionsNoneAsync(string projectKey, string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/groups/none") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Checks whether a permission is granted to all users for a project. + /// + /// The project key. + /// The permission to check. + /// Token to cancel the operation. + /// true if the permission is granted to all; otherwise, false. + public async Task IsProjectDefaultPermissionAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) + { + var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, s => + { + using var doc = JsonDocument.Parse(s); + return doc.RootElement.GetProperty("permitted").GetBoolean(); + }, cancellationToken) + .ConfigureAwait(false); + } + + private async Task SetProjectDefaultPermissionAsync(string projectKey, Permissions permission, bool allow, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["allow"] = BitbucketHelpers.BoolToString(allow), + }; + + var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") + .SetQueryParams(queryParamValues) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Grants a permission to all users for a project. + /// + /// The project key. + /// The permission to grant. + /// Token to cancel the operation. + /// true if the permission was granted; otherwise, false. + public async Task GrantProjectPermissionToAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) + { + return await SetProjectDefaultPermissionAsync(projectKey, permission, allow: true, cancellationToken).ConfigureAwait(false); + } + + /// + /// Revokes a permission from all users for a project. + /// + /// The project key. + /// The permission to revoke. + /// Token to cancel the operation. + /// true if the permission was revoked; otherwise, false. + public async Task RevokeProjectPermissionFromAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) + { + return await SetProjectDefaultPermissionAsync(projectKey, permission, allow: false, cancellationToken).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequestComments.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequestComments.cs new file mode 100644 index 0000000..77a4dc9 --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequestComments.cs @@ -0,0 +1,269 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Projects; +using Flurl.Http; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + + /// + /// Creates a comment on a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The comment text. + /// Optional parent comment ID to create a reply. + /// Optional diff type. + /// Optional from commit hash for anchoring. + /// Optional file path for anchoring. + /// Optional source path for move/rename anchors. + /// Optional to commit hash for anchoring. + /// Optional line number for anchoring. + /// Optional file type for anchoring. + /// Optional line type for anchoring. + /// Cancellation token. + /// The created comment reference. + public async Task CreatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, + string text, + string? parentId = null, + DiffTypes? diffType = null, + string? fromHash = null, + string? path = null, + string? srcPath = null, + string? toHash = null, + int? line = null, + FileTypes? fileType = null, + LineTypes? lineType = null, + CancellationToken cancellationToken = default) + { + // Build the comment payload dynamically to avoid sending empty anchor objects + // which Bitbucket Server 9.0 rejects with HTTP 500. + // See: BUG-003 - add_pull_request_comment returns 500 error + var data = new Dictionary(StringComparer.Ordinal) + { + ["text"] = text, + }; + + if (!string.IsNullOrEmpty(parentId)) + { + data["parent"] = new { id = parentId }; + } + + // Only include anchor if at least one anchor-related field is specified + // Empty anchor objects cause HTTP 500 on Bitbucket Server 9.0 + var hasAnchorData = diffType.HasValue + || !string.IsNullOrEmpty(fromHash) + || !string.IsNullOrEmpty(path) + || !string.IsNullOrEmpty(srcPath) + || !string.IsNullOrEmpty(toHash) + || line.HasValue + || fileType.HasValue + || lineType.HasValue; + + if (hasAnchorData) + { + data["anchor"] = new + { + diffType = BitbucketHelpers.DiffTypeToString(diffType), + fromHash, + path, + srcPath, + toHash, + line, + fileType = BitbucketHelpers.FileTypeToString(fileType), + lineType = BitbucketHelpers.LineTypeToString(lineType), + }; + } + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/comments") + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves comments for a pull request path. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The file path to filter comments. + /// Anchor state filter. + /// Diff type filter. + /// Optional from commit hash. + /// Optional to commit hash. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of pull request comments. + public async Task> GetPullRequestCommentsAsync(string projectKey, string repositorySlug, long pullRequestId, + string path, + AnchorStates anchorState = AnchorStates.Active, + DiffTypes diffType = DiffTypes.Effective, + string? fromHash = null, + string? toHash = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + ["path"] = path, + ["anchorState"] = BitbucketHelpers.AnchorStateToString(anchorState), + ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), + ["fromHash"] = fromHash, + ["toHash"] = toHash, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/comments") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams comments for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The file path to filter comments for. + /// The anchor state filter. + /// The diff type filter. + /// Optional from commit hash. + /// Optional to commit hash. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// An async enumerable of comment references. + public IAsyncEnumerable GetPullRequestCommentsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + string path, + AnchorStates anchorState = AnchorStates.Active, + DiffTypes diffType = DiffTypes.Effective, + string? fromHash = null, + string? toHash = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + ["path"] = path, + ["anchorState"] = BitbucketHelpers.AnchorStateToString(anchorState), + ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), + ["fromHash"] = fromHash, + ["toHash"] = toHash, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/comments") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Retrieves a single pull request comment by ID. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The comment ID. + /// Optional avatar size. + /// Cancellation token. + /// The requested comment reference. + public async Task GetPullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a pull request comment. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The comment ID. + /// The comment version for optimistic concurrency. + /// The updated comment text. + /// Cancellation token. + /// The updated comment reference. + public async Task UpdatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, + int version, string text, CancellationToken cancellationToken = default) + { + var data = new + { + version, + text, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") + .SetQueryParam("version", version) + .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a pull request comment. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The comment ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// true if the comment was deleted; otherwise, false. + public async Task DeletePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, + int version = -1, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") + .SetQueryParam("version", version) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequestDetails.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequestDetails.cs new file mode 100644 index 0000000..1f17f63 --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequestDetails.cs @@ -0,0 +1,436 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Projects; +using Flurl.Http; +using System.Runtime.CompilerServices; +using System.Text.Json; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Retrieves activities for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional starting activity ID. + /// Optional activity type filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of pull request activities. + public async Task> GetPullRequestActivitiesAsync(string projectKey, string repositorySlug, long pullRequestId, + long? fromId = null, + PullRequestFromTypes? fromType = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["fromId"] = fromId, + ["fromType"] = BitbucketHelpers.PullRequestFromTypeToString(fromType), + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/activities") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams activities for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional starting activity ID. + /// Optional from type filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// An async enumerable of pull request activities. + public IAsyncEnumerable GetPullRequestActivitiesStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + long? fromId = null, + PullRequestFromTypes? fromType = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["fromId"] = fromId, + ["fromType"] = BitbucketHelpers.PullRequestFromTypeToString(fromType), + ["avatarSize"] = avatarSize, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/activities") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + + /// + /// Retrieves changes for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Scope of changes to include. + /// Optional since commit ID. + /// Optional until commit ID. + /// Whether to include comment counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of changes. + public async Task> GetPullRequestChangesAsync(string projectKey, string repositorySlug, long pullRequestId, + ChangeScopes changeScope = ChangeScopes.All, + string? sinceId = null, + string? untilId = null, + bool withComments = true, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["changeScope"] = BitbucketHelpers.ChangeScopeToString(changeScope), + ["sinceId"] = sinceId, + ["untilId"] = untilId, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams changes for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The change scope filter. + /// Optional since commit ID. + /// Optional until commit ID. + /// Whether to include comment counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of changes. + public IAsyncEnumerable GetPullRequestChangesStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + ChangeScopes changeScope = ChangeScopes.All, + string? sinceId = null, + string? untilId = null, + bool withComments = true, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["changeScope"] = BitbucketHelpers.ChangeScopeToString(changeScope), + ["sinceId"] = sinceId, + ["untilId"] = untilId, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/changes") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + + /// + /// Retrieves commits associated with a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Whether to include change counts. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of commits. + public async Task> GetPullRequestCommitsAsync(string projectKey, string repositorySlug, long pullRequestId, + bool withCounts = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams all commits for a pull request as an IAsyncEnumerable. + /// + public IAsyncEnumerable GetPullRequestCommitsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + bool withCounts = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Retrieves the diff for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Number of context lines to include. + /// Diff type. + /// Optional since commit ID. + /// Optional source path filter. + /// Optional until commit ID. + /// Whitespace handling option. + /// Whether to include comments. + /// Cancellation token. + /// Differences for the pull request. + public async Task GetPullRequestDiffAsync(string projectKey, string repositorySlug, long pullRequestId, + int contextLines = -1, + DiffTypes diffType = DiffTypes.Effective, + string? sinceId = null, + string? srcPath = null, + string? untilId = null, + string whitespace = "ignore-all", + bool withComments = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Streams diff entries for a pull request as an . + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Number of context lines to include. + /// Diff type. + /// Optional since commit ID. + /// Optional source path filter. + /// Optional until commit ID. + /// Whitespace handling option. + /// Whether to include comments. + /// Cancellation token. + /// A stream of diff entries. + public async IAsyncEnumerable GetPullRequestDiffStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + int contextLines = -1, + DiffTypes diffType = DiffTypes.Effective, + string? sinceId = null, + string? srcPath = null, + string? untilId = null, + string whitespace = "ignore-all", + bool withComments = true, + [EnumeratorCancellation] CancellationToken cancellationToken = default) + { + var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + + try + { + await foreach (var diff in DeserializePullRequestDiffsAsync(responseStream, cancellationToken).ConfigureAwait(false)) + { + yield return diff; + } + } + finally + { + await responseStream.DisposeAsync().ConfigureAwait(false); + } + } + + /// + /// Retrieves the diff for a specific path within a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The file path to filter by. + /// Number of context lines to include. + /// Diff type. + /// Optional since commit ID. + /// Optional source path filter. + /// Optional until commit ID. + /// Whitespace handling option. + /// Whether to include comments. + /// Cancellation token. + /// Differences for the specified path. + public async Task GetPullRequestDiffPathAsync(string projectKey, string repositorySlug, long pullRequestId, + string path, + int contextLines = -1, + DiffTypes diffType = DiffTypes.Effective, + string? sinceId = null, + string? srcPath = null, + string? untilId = null, + string whitespace = "ignore-all", + bool withComments = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/diff/{path}") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + private static Dictionary CreatePullRequestDiffQueryParams(int contextLines, DiffTypes diffType, string? sinceId, + string? srcPath, string? untilId, string whitespace, bool withComments) + { + return new Dictionary(StringComparer.Ordinal) + { + ["contextLines"] = contextLines, + ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), + ["sinceId"] = sinceId, + ["srcPath"] = srcPath, + ["untilId"] = untilId, + ["whitespace"] = whitespace, + ["withComments"] = BitbucketHelpers.BoolToString(withComments), + }; + } + + private static async IAsyncEnumerable DeserializePullRequestDiffsAsync(Stream responseStream, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) + { + yield return diff; + } + } + + /// + /// Deserializes diff entries from a JSON stream containing a "diffs" array. + /// Used by all diff streaming methods (commit, repository, compare, pull request). + /// Uses zero-copy deserialization directly from JsonElement to avoid intermediate string allocations. + /// + private static async IAsyncEnumerable DeserializeDiffsFromStreamAsync(Stream responseStream, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + using var doc = await JsonDocument.ParseAsync(responseStream, cancellationToken: cancellationToken).ConfigureAwait(false); + + if (!doc.RootElement.TryGetProperty("diffs", out var diffsArray) || diffsArray.ValueKind != JsonValueKind.Array) + { + yield break; + } + + foreach (var diffElement in diffsArray.EnumerateArray()) + { + cancellationToken.ThrowIfCancellationRequested(); + + // Zero-copy: Deserialize directly from JsonElement instead of GetRawText() string allocation + var diff = diffElement.Deserialize(s_jsonOptions); + if (diff is not null) + { + yield return diff; + } + } + } + + // Note: MoveToDiffArrayAsync is no longer needed with System.Text.Json approach + +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequests.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequests.cs new file mode 100644 index 0000000..c964b2d --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.PullRequests.cs @@ -0,0 +1,552 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Models.Core.Users; +using Flurl.Http; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Retrieves participants related to pull requests in a repository. + /// + /// The project key. + /// The repository slug. + /// Direction of pull requests to consider. + /// Optional filter string. + /// Optional role filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of identities. + public async Task> GetRepositoryParticipantsAsync(string projectKey, string repositorySlug, + PullRequestDirections direction = PullRequestDirections.Incoming, + string? filter = null, + Roles? role = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), + ["filter"] = filter, + ["role"] = BitbucketHelpers.RoleToString(role), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/participants") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Retrieves pull requests for a repository with optional filtering. + /// + /// The project key. + /// The repository slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Pull request direction filter. + /// Optional branch filter. + /// Pull request state. + /// Ordering option. + /// Whether to include attributes. + /// Whether to include properties. + /// Cancellation token. + /// A collection of pull requests. + public async Task> GetPullRequestsAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + PullRequestDirections direction = PullRequestDirections.Incoming, + string? branchId = null, + PullRequestStates state = PullRequestStates.Open, + PullRequestOrders order = PullRequestOrders.Newest, + bool withAttributes = true, + bool withProperties = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), + ["at"] = branchId, + ["state"] = BitbucketHelpers.PullRequestStateToString(state), + ["order"] = BitbucketHelpers.PullRequestOrderToString(order), + ["withAttributes"] = BitbucketHelpers.BoolToString(withAttributes), + ["withProperties"] = BitbucketHelpers.BoolToString(withProperties), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams all pull requests for a repository as an IAsyncEnumerable. + /// + public IAsyncEnumerable GetPullRequestsStreamAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + PullRequestDirections direction = PullRequestDirections.Incoming, + string? branchId = null, + PullRequestStates state = PullRequestStates.Open, + PullRequestOrders order = PullRequestOrders.Newest, + bool withAttributes = true, + bool withProperties = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), + ["at"] = branchId, + ["state"] = BitbucketHelpers.PullRequestStateToString(state), + ["order"] = BitbucketHelpers.PullRequestOrderToString(order), + ["withAttributes"] = BitbucketHelpers.BoolToString(withAttributes), + ["withProperties"] = BitbucketHelpers.BoolToString(withProperties), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Creates a pull request in a repository. + /// + /// The project key. + /// The repository slug. + /// The pull request payload. + /// Cancellation token. + /// The created pull request. + public async Task CreatePullRequestAsync(string projectKey, string repositorySlug, PullRequestInfo pullRequestInfo, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") + .SendAsync(HttpMethod.Post, CreateJsonContent(pullRequestInfo), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a pull request by ID. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The requested pull request. + public async Task GetPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The update payload. + /// Cancellation token. + /// The updated pull request. + public async Task UpdatePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, PullRequestUpdate pullRequestUpdate, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") + .SendAsync(HttpMethod.Put, CreateJsonContent(pullRequestUpdate), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Version info for optimistic concurrency. + /// Cancellation token. + /// true if the pull request was deleted; otherwise, false. + public async Task DeletePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, VersionInfo versionInfo, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") + .SendAsync(HttpMethod.Delete, CreateJsonContent(versionInfo), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + /// + /// Declines a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// true if the pull request was declined; otherwise, false. + public async Task DeclinePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["version"] = version, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/decline") + .SetQueryParams(queryParamValues) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves the merge state for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// The merge state. + public async Task GetPullRequestMergeStateAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["version"] = version, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Gets the merge base (common ancestor) commit for a pull request. + /// This is the best common ancestor between the latest commits of the source and target branches. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The merge base commit, or null if not found (HTTP 204 - no common ancestor exists). + /// + /// This endpoint is useful for creating line-specific comments on pull requests. + /// The returned commit ID can be used as the fromHash parameter when creating anchored comments, + /// while the toHash can be obtained from on the pull request's FromRef. + /// + public async Task GetPullRequestMergeBaseAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge-base") + .AllowHttpStatus(204) + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + // HTTP 204 indicates no common ancestor exists (e.g., unrelated histories) + if (response.StatusCode == 204) + { + return null; + } + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Merges a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// The merged pull request. + public async Task MergePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["version"] = version, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") + .SetQueryParams(queryParamValues) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Reopens a declined pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional version for optimistic concurrency. + /// Cancellation token. + /// The reopened pull request. + public async Task ReopenPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["version"] = version, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/reopen") + .SetQueryParams(queryParamValues) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Approves a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The reviewer entry reflecting the approval. + public async Task ApprovePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Removes an approval from a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The reviewer entry after removal. + public async Task DeletePullRequestApprovalAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves participants for a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Cancellation token. + /// A collection of participants. + public async Task> GetPullRequestParticipantsAsync(string projectKey, string repositorySlug, long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams participants for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// An async enumerable of participants. + public IAsyncEnumerable GetPullRequestParticipantsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Assigns a role to a user in a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The user to assign. + /// The role to assign. + /// Cancellation token. + /// The created participant entry. + public async Task AssignUserRoleToPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, + Named named, + Roles role, + CancellationToken cancellationToken = default) + { + var data = new + { + user = named, + role = BitbucketHelpers.RoleToString(role), + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a participant from a pull request by username. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The username to remove. + /// Cancellation token. + /// true if removal succeeded; otherwise, false. + public async Task DeletePullRequestParticipantAsync(string projectKey, string repositorySlug, long pullRequestId, string userName, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") + .SetQueryParam("username", userName) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a participant's approval status on a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The user slug to update. + /// The user identity. + /// Whether the participant approves the PR. + /// The participant status. + /// Cancellation token. + /// The updated participant entry. + public async Task UpdatePullRequestParticipantStatus(string projectKey, string repositorySlug, long pullRequestId, + string userSlug, + Named named, + bool approved, + ParticipantStatus participantStatus, + CancellationToken cancellationToken = default) + { + var data = new + { + user = named, + approved = BitbucketHelpers.BoolToString(approved), + status = BitbucketHelpers.ParticipantStatusToString(participantStatus), + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants/{userSlug}") + .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Removes a participant from a pull request by user slug. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The user slug to remove. + /// Cancellation token. + /// true if removal succeeded; otherwise, false. + public async Task UnassignUserFromPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, string userSlug, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/participants/{userSlug}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.Repositories.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Repositories.cs new file mode 100644 index 0000000..5fbb358 --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Repositories.cs @@ -0,0 +1,547 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Admin; +using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Models.Core.Users; +using Flurl.Http; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Retrieves repositories for a project. + /// + /// The project key. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of repositories. + public async Task> GetProjectRepositoriesAsync(string projectKey, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsUrl($"/{projectKey}/repos") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams all repositories for a project as an . + /// + public IAsyncEnumerable GetProjectRepositoriesStreamAsync(string projectKey, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsUrl($"/{projectKey}/repos") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Creates a repository within a project. + /// + /// The project key. + /// The repository name. + /// Optional SCM identifier (default is git). + /// Token to cancel the operation. + /// The created repository. + public async Task CreateProjectRepositoryAsync(string projectKey, string repositoryName, string scmId = "git", CancellationToken cancellationToken = default) + { + var data = new + { + name = repositoryName, + scmId, + }; + + var response = await GetProjectsUrl($"/{projectKey}/repos") + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a repository within a project. + /// + /// The project key. + /// The repository slug. + /// Token to cancel the operation. + /// The requested repository. + public async Task GetProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Creates a fork of a repository, optionally targeting another project or slug. + /// + /// The source project key. + /// The source repository slug. + /// Optional target project key for the fork. + /// Optional target repository slug. + /// Optional display name for the fork. + /// Token to cancel the operation. + /// The created repository fork. + public async Task CreateProjectRepositoryForkAsync(string projectKey, string repositorySlug, string? targetProjectKey = null, string? targetSlug = null, string? targetName = null, CancellationToken cancellationToken = default) + { + var data = new + { + slug = targetSlug ?? repositorySlug, + name = targetName, + project = targetProjectKey == null ? null : new ProjectRef { Key = targetProjectKey }, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Schedules a repository for deletion. + /// + /// The project key. + /// The repository slug. + /// Token to cancel the operation. + /// true if the repository was scheduled for deletion; otherwise, false. + public async Task ScheduleProjectRepositoryForDeletionAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates repository metadata such as name, forkability, project, or visibility. + /// + /// The project key. + /// The repository slug. + /// Optional new repository name. + /// Optional forkable flag. + /// Optional target project key. + /// Optional public visibility flag. + /// Token to cancel the operation. + /// The updated repository. + public async Task UpdateProjectRepositoryAsync(string projectKey, string repositorySlug, + string? targetName = null, + bool? isForkable = null, + string? targetProjectKey = null, + bool? isPublic = null, + CancellationToken cancellationToken = default) + { + var data = new + { + name = targetName, + forkable = isForkable, + project = targetProjectKey == null ? null : new ProjectRef { Key = targetProjectKey }, + @public = isPublic, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves forks of a repository. + /// + /// The project key. + /// The repository slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of repository forks. + public async Task> GetProjectRepositoryForksAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/forks") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Recreates a repository in place. + /// + /// The project key. + /// The repository slug. + /// Token to cancel the operation. + /// The recreated repository. + public async Task RecreateProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/recreate") + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves repositories related to the specified repository (e.g., forks). + /// + /// The project key. + /// The repository slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of related repository forks. + public async Task> GetRelatedProjectRepositoriesAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/related") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Retrieves an archive (zip/tar) of a repository at a specific ref. + /// + /// The project key. + /// The repository slug. + /// The ref (commit/branch/tag) to archive. + /// The archive file name. + /// The archive format. + /// Optional path filter. + /// Optional archive prefix. + /// Token to cancel the operation. + /// Archive bytes. + public async Task GetProjectRepositoryArchiveAsync(string projectKey, string repositorySlug, + string at, + string fileName, + ArchiveFormats archiveFormat, + string path, + string prefix, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["at"] = at, + ["fileName"] = fileName, + ["format"] = BitbucketHelpers.ArchiveFormatToString(archiveFormat), + ["path"] = path, + ["prefix"] = prefix, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/archive") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + return await ReadResponseBytesAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves group permissions for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of group permissions. + public async Task> GetProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, + string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["filter"] = filter, + ["limit"] = limit, + ["start"] = start, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Updates a group's permissions for a repository. + /// + /// The project key. + /// The repository slug. + /// The permission to grant. + /// The group name. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. + public async Task UpdateProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["permission"] = BitbucketHelpers.PermissionToString(permission), + ["name"] = name, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") + .SetQueryParams(queryParamValues) + .SendAsync(HttpMethod.Put, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Removes a group's permissions from a repository. + /// + /// The project key. + /// The repository slug. + /// The group name. + /// Token to cancel the operation. + /// true if the permissions were removed; otherwise, false. + public async Task DeleteProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, string name, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") + .SetQueryParam("name", name) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves groups or users without permissions on a repository. + /// + /// The project key. + /// The repository slug. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of removable group or user entries. + public async Task> GetProjectRepositoryGroupPermissionsNoneAsync(string projectKey, string repositorySlug, + string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups/none") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Retrieves user permissions for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// A collection of user permissions. + public async Task> GetProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, + string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["filter"] = filter, + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Updates a user's permissions for a repository. + /// + /// The project key. + /// The repository slug. + /// The permission to grant. + /// The user name. + /// Token to cancel the operation. + /// true if the update succeeded; otherwise, false. + public async Task UpdateProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["permission"] = BitbucketHelpers.PermissionToString(permission), + ["name"] = name, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") + .SetQueryParams(queryParamValues) + .SendAsync(HttpMethod.Put, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Removes a user's permissions from a repository. + /// + /// The project key. + /// The repository slug. + /// The user name. + /// Optional avatar size. + /// Token to cancel the operation. + /// true if the permissions were removed; otherwise, false. + public async Task DeleteProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, string name, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") + .SetQueryParam("name", name) + .SetQueryParam("avatarSize", avatarSize) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves users who have no permissions on a repository. + /// + /// The project key. + /// The repository slug. + /// Optional filter string. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// A collection of users without repository permissions. + public async Task> GetProjectRepositoryUserPermissionsNoneAsync(string projectKey, string repositorySlug, + string? filter = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filter"] = filter, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users/none") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.RepositorySettings.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.RepositorySettings.cs new file mode 100644 index 0000000..aff4026 --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.RepositorySettings.cs @@ -0,0 +1,595 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Admin; +using Bitbucket.Net.Models.Core.Projects; +using Flurl.Http; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Retrieves raw content from a file path in a repository. + /// + /// The project key. + /// The repository slug. + /// The file path to fetch. + /// Optional ref (branch, tag, commit). + /// Whether to render markup. + /// Whether to hard wrap the output. + /// Whether to HTML-escape the output. + /// Cancellation token. + /// A stream containing the raw content. + public async Task RetrieveRawContentAsync(string projectKey, string repositorySlug, string path, + string? at = null, + bool markup = false, + bool hardWrap = true, + bool htmlEscape = true, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["at"] = at, + ["markup"] = BitbucketHelpers.BoolToString(markup), + ["hardWrap"] = BitbucketHelpers.BoolToString(hardWrap), + ["htmlEscape"] = BitbucketHelpers.BoolToString(htmlEscape), + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/raw/{path}") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); + return await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves pull request settings for a repository. + /// + /// The project key. + /// The repository slug. + /// Cancellation token. + /// The pull request settings. + public async Task GetProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates pull request settings for a repository. + /// + /// The project key. + /// The repository slug. + /// The settings payload. + /// Cancellation token. + /// The updated pull request settings. + public async Task UpdateProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, + PullRequestSettings pullRequestSettings, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") + .SendAsync(HttpMethod.Post, CreateJsonContent(pullRequestSettings), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves repository hooks. + /// + /// The project key. + /// The repository slug. + /// Optional hook type filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of hooks. + public async Task> GetProjectRepositoryHooksSettingsAsync(string projectKey, string repositorySlug, + HookTypes? hookType = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["type"] = hookType, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/hooks") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Retrieves a specific hook's settings. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Cancellation token. + /// The hook configuration. + public async Task GetProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a repository hook. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Cancellation token. + /// true if deletion succeeded; otherwise, false. + public async Task DeleteProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Enables a repository hook, optionally providing settings. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Optional hook settings. + /// Cancellation token. + /// The enabled hook. + public async Task EnableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, object? hookSettings = null, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") + .SendAsync(HttpMethod.Put, CreateJsonContent(hookSettings), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Disables a repository hook. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Cancellation token. + /// The disabled hook. + public async Task DisableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves all settings for a repository hook. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// Cancellation token. + /// A dictionary of hook settings. + public async Task> GetProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates all settings for a repository hook. + /// + /// The project key. + /// The repository slug. + /// The hook key. + /// The settings payload. + /// Cancellation token. + /// The updated settings. + public async Task> UpdateProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, + Dictionary allSettings, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") + .SendAsync(HttpMethod.Put, CreateJsonContent(allSettings), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves merge strategies for pull requests within a project SCM. + /// + /// The project key. + /// The SCM identifier. + /// Cancellation token. + /// The pull request settings. + public async Task GetProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, CancellationToken cancellationToken = default) + { + var response = await GetProjectUrl(projectKey) + .AppendPathSegment($"/settings/pull-requests/{scmId}") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates merge strategies for pull requests within a project SCM. + /// + /// The project key. + /// The SCM identifier. + /// The merge strategies payload. + /// Cancellation token. + /// The updated merge strategies. + public async Task UpdateProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, MergeStrategies mergeStrategies, CancellationToken cancellationToken = default) + { + var response = await GetProjectUrl(projectKey) + .AppendPathSegment($"/settings/pull-requests/{scmId}") + .SendAsync(HttpMethod.Post, CreateJsonContent(mergeStrategies), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves tags from a repository. + /// + /// The project key. + /// The repository slug. + /// Filter text for tag names. + /// Ordering option. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of tags. + public async Task> GetProjectRepositoryTagsAsync(string projectKey, string repositorySlug, + string filterText, + BranchOrderBy orderBy, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filterText"] = filterText, + ["orderBy"] = BitbucketHelpers.BranchOrderByToString(orderBy), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams tags for a repository, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// Filter text for tag names. + /// Ordering option. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of tags. + public IAsyncEnumerable GetProjectRepositoryTagsStreamAsync(string projectKey, string repositorySlug, + string filterText, + BranchOrderBy orderBy, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["filterText"] = filterText, + ["orderBy"] = BitbucketHelpers.BranchOrderByToString(orderBy), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Creates a tag in a repository. + /// + /// The project key. + /// The repository slug. + /// The tag name. + /// The starting commit or ref. + /// The tag message. + /// Cancellation token. + /// The created tag. + public async Task CreateProjectRepositoryTagAsync(string projectKey, string repositorySlug, + string name, + string startPoint, + string message, + CancellationToken cancellationToken = default) + { + var data = new + { + name, + startPoint, + message, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a tag by name. + /// + /// The project key. + /// The repository slug. + /// The tag name. + /// Cancellation token. + /// The requested tag. + public async Task GetProjectRepositoryTagAsync(string projectKey, string repositorySlug, string tagName, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/tags/{tagName}") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves webhooks for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional event filter. + /// Whether to include statistics. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Cancellation token. + /// A collection of webhooks. + public async Task> GetProjectRepositoryWebHooksAsync(string projectKey, string repositorySlug, + string? @event = null, + bool statistics = false, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["event"] = @event, + ["statistics"] = BitbucketHelpers.BoolToString(statistics), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Creates a webhook for a repository. + /// + /// The project key. + /// The repository slug. + /// The webhook payload. + /// Cancellation token. + /// The created webhook. + public async Task CreateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, WebHook webHook, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") + .SendAsync(HttpMethod.Post, CreateJsonContent(webHook), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Tests a webhook delivery for a repository. + /// + /// The project key. + /// The repository slug. + /// The URL to test. + /// Cancellation token. + /// The webhook test response. + public async Task TestProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, string url, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks/test") + .SetQueryParam("url", url) + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a webhook by ID. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Whether to include statistics. + /// Cancellation token. + /// The webhook. + public async Task GetProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, + string webHookId, + bool statistics = false, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["statistics"] = BitbucketHelpers.BoolToString(statistics), + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a webhook. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// The webhook payload. + /// Cancellation token. + /// The updated webhook. + public async Task UpdateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, + string webHookId, WebHook webHook, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") + .SendAsync(HttpMethod.Put, CreateJsonContent(webHook), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a webhook. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Cancellation token. + /// true if deletion succeeded; otherwise, false. + public async Task DeleteProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, + string webHookId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + //public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, + /// + /// Retrieves the latest webhook invocation summary as a string. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Optional event filter. + /// Optional outcome filter. + /// Cancellation token. + /// The latest invocation payload. + public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, + string webHookId, + string? @event = null, + WebHookOutcomes? outcome = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["event"] = @event, + ["outcome"] = BitbucketHelpers.WebHookOutcomeToString(outcome), + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/latest") + .SetQueryParams(queryParamValues) + //.GetJsonAsync() + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves webhook statistics. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Optional event filter. + /// Cancellation token. + /// Webhook statistics. + public async Task GetProjectRepositoryWebHookStatisticsAsync(string projectKey, string repositorySlug, + string webHookId, + string? @event = null, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics") + .SetQueryParam("event", @event) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a statistics summary for a webhook. + /// + /// The project key. + /// The repository slug. + /// The webhook ID. + /// Cancellation token. + /// A dictionary of webhook statistics counts. + public async Task> GetProjectRepositoryWebHookStatisticsSummaryAsync(string projectKey, string repositorySlug, + string webHookId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics/summary") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.Tasks.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Tasks.cs new file mode 100644 index 0000000..f1bcb72 --- /dev/null +++ b/src/Bitbucket.Net/Core/Projects/BitbucketClient.Tasks.cs @@ -0,0 +1,507 @@ +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Exceptions; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Core.Projects; +using Bitbucket.Net.Models.Core.Tasks; +using Flurl.Http; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Gets tasks for a pull request using the legacy tasks endpoint. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Maximum number of pages to retrieve. + /// Maximum number of results per page. + /// Pagination start index. + /// Avatar size for user avatars. + /// Cancellation token. + /// Collection of tasks. + /// + /// + /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and returns 404 Not Found on servers version 9.0+. + /// + /// + /// For Bitbucket Server 9.0+, use instead. + /// For cross-version compatibility, use . + /// + /// + [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsAsync for 9.0+ or GetPullRequestTasksWithFallbackAsync for cross-version compatibility.")] + public async Task> GetPullRequestTasksAsync(string projectKey, string repositorySlug, long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/tasks") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams tasks for a pull request, yielding items as they are retrieved. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size. + /// Token to cancel the operation. + /// An async enumerable of tasks. + /// + /// + /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and returns 404 Not Found on servers version 9.0+. + /// + /// + /// For Bitbucket Server 9.0+, use instead. + /// + /// + [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsStreamAsync for 9.0+ compatibility.")] + public IAsyncEnumerable GetPullRequestTasksStreamAsync(string projectKey, string repositorySlug, long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/tasks") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Gets the task count for a pull request using the legacy tasks endpoint. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// The task count. + /// + /// + /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and may return 404 Not Found on servers version 9.0+. + /// + /// + /// For Bitbucket Server 9.0+, use and count the results. + /// + /// + [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsAsync and count the results for 9.0+ compatibility.")] + public async Task GetPullRequestTaskCountAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/tasks/count") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + #region Blocker Comments (Bitbucket Server 9.0+) + + /// + /// Gets blocker comments (tasks) for a pull request. + /// This endpoint is available in Bitbucket Server 9.0+ and replaces the legacy tasks endpoint. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional filter: , , or null for all. + /// Maximum number of pages to retrieve. + /// Maximum number of results per page. + /// Pagination start index. + /// Cancellation token. + /// Collection of blocker comments. + /// + /// + /// In Bitbucket Server 9.0+, tasks have been replaced by blocker comments. + /// A blocker comment is a comment with severity: 'BLOCKER' that must be resolved before the pull request can be merged. + /// + /// + /// For servers prior to 9.0, use instead. + /// + /// + public async Task> GetPullRequestBlockerCommentsAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + BlockerCommentState? state = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["state"] = BitbucketHelpers.BlockerCommentStateToString(state), + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/blocker-comments") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Streams blocker comments for a pull request, yielding items as they are retrieved. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Optional blocker comment state filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Token to cancel the operation. + /// An async enumerable of blocker comments. + public IAsyncEnumerable GetPullRequestBlockerCommentsStreamAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + BlockerCommentState? state = null, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["state"] = BitbucketHelpers.BlockerCommentStateToString(state), + }; + + return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/blocker-comments") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken); + } + + /// + /// Gets a single blocker comment by ID. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// Cancellation token. + /// The blocker comment. + public async Task GetPullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Creates a blocker comment (task) on a pull request. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment text. + /// Optional anchor for file/line-specific blockers. + /// Cancellation token. + /// The created blocker comment. + public async Task CreatePullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + string text, + CommentAnchor? anchor = null, + CancellationToken cancellationToken = default) + { + var data = new + { + text, + severity = "BLOCKER", + anchor, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments") + .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a blocker comment's text. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// The updated blocker comment text. + /// The version of the blocker comment (for optimistic locking). + /// Cancellation token. + /// The updated blocker comment. + public async Task UpdatePullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + string text, + int version, + CancellationToken cancellationToken = default) + { + var data = new + { + text, + version, + }; + + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") + .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a blocker comment. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// The version of the blocker comment (for optimistic locking). + /// Cancellation token. + /// True if the blocker comment was deleted successfully. + public async Task DeletePullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + int version, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") + .SetQueryParam("version", version) + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Resolves a blocker comment (marks the task as complete). + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// The version of the blocker comment (for optimistic locking). + /// Cancellation token. + /// The resolved blocker comment. + public async Task ResolvePullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + int version, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}/resolve") + .SetQueryParam("version", version) + .PutAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Reopens a resolved blocker comment. + /// This endpoint is available in Bitbucket Server 9.0+. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// The blocker comment ID. + /// The version of the blocker comment (for optimistic locking). + /// Cancellation token. + /// The reopened blocker comment. + public async Task ReopenPullRequestBlockerCommentAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + long blockerCommentId, + int version, + CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}/reopen") + .SetQueryParam("version", version) + .PutAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Gets pull request tasks with automatic fallback for cross-version compatibility. + /// + /// + /// + /// This method provides backward compatibility across Bitbucket Server versions: + /// + /// + /// Bitbucket Server 9.0+: Uses the new /blocker-comments endpoint. + /// Bitbucket Server < 9.0: Falls back to the legacy /tasks endpoint. + /// + /// + /// The method first tries the new blocker-comments endpoint. If it returns 404 (Not Found), + /// it automatically falls back to the legacy tasks endpoint. + /// + /// + /// For new code targeting Bitbucket Server 9.0+, prefer using + /// directly for better type safety. + /// + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Maximum number of pages to retrieve. + /// Maximum number of results per page. + /// Pagination start index. + /// Cancellation token. + /// + /// A collection of blocker comments () on Bitbucket 9.0+, + /// or legacy tasks () on older versions. + /// + public async Task> GetPullRequestTasksWithFallbackAsync( + string projectKey, + string repositorySlug, + long pullRequestId, + int? maxPages = null, + int? limit = null, + int? start = null, + CancellationToken cancellationToken = default) + { + try + { + // Try new blocker-comments endpoint first (Bitbucket 9.0+) + var blockerComments = await GetPullRequestBlockerCommentsAsync( + projectKey, repositorySlug, pullRequestId, + maxPages: maxPages, limit: limit, start: start, + cancellationToken: cancellationToken).ConfigureAwait(false); + + return blockerComments.Cast(); + } + catch (BitbucketNotFoundException) + { + // Fall back to legacy tasks endpoint (Bitbucket < 9.0) +#pragma warning disable CS0618 // Type or member is obsolete - intentional fallback + var tasks = await GetPullRequestTasksAsync( + projectKey, repositorySlug, pullRequestId, + maxPages: maxPages, limit: limit, start: start, + cancellationToken: cancellationToken).ConfigureAwait(false); +#pragma warning restore CS0618 + + return tasks.Cast(); + } + } + + #endregion + + /// + /// Subscribes the current user to watch a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// true if the watch was added; otherwise, false. + public async Task WatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/watch") + .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Unsubscribes the current user from watching a pull request. + /// + /// The project key. + /// The repository slug. + /// The pull request ID. + /// Cancellation token. + /// true if the watch was removed; otherwise, false. + public async Task UnwatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) + { + var response = await GetProjectsReposUrl(projectKey, repositorySlug) + .AppendPathSegment($"/pull-requests/{pullRequestId}/watch") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + +} \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs b/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs deleted file mode 100644 index 40f0820..0000000 --- a/src/Bitbucket.Net/Core/Projects/BitbucketClient.cs +++ /dev/null @@ -1,4491 +0,0 @@ -using Bitbucket.Net.Common; -using Bitbucket.Net.Common.Exceptions; -using Bitbucket.Net.Common.Models; -using Bitbucket.Net.Models.Core.Admin; -using Bitbucket.Net.Models.Core.Projects; -using Bitbucket.Net.Models.Core.Tasks; -using Bitbucket.Net.Models.Core.Users; -using Flurl.Http; -using System.Buffers; -using System.Runtime.CompilerServices; -using System.Text.Json; - -namespace Bitbucket.Net; - -/// -/// Provides project and repository management Bitbucket API operations. -/// -public partial class BitbucketClient -{ - /// - /// Gets the base projects URL. - /// - /// An targeting the projects endpoint. - private IFlurlRequest GetProjectsUrl() => GetBaseUrl() - .AppendPathSegment("/projects"); - - /// - /// Gets the projects URL for the specified path. - /// - /// The path to append. - /// An pointing to the projects path. - private IFlurlRequest GetProjectsUrl(string path) => GetProjectsUrl() - .AppendPathSegment(path); - - /// - /// Gets the URL for a specific project. - /// - /// The project key. - /// An pointing to the project. - private IFlurlRequest GetProjectUrl(string projectKey) => GetProjectsUrl() - .AppendPathSegment($"/{projectKey}"); - - /// - /// Gets the URL for a repository within a project. - /// - /// The project key. - /// The repository slug. - /// An pointing to the repository. - private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug) => GetProjectsUrl($"/{projectKey}/repos/{repositorySlug}"); - - /// - /// Gets the URL for a specific path within a project repository. - /// - /// The project key. - /// The repository slug. - /// The additional path to append. - /// An pointing to the repository path. - private IFlurlRequest GetProjectsReposUrl(string projectKey, string repositorySlug, string path) => GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment(path); - - /// - /// Retrieves projects accessible to the current user. - /// - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional project name filter. - /// Optional permission filter. - /// Token to cancel the operation. - /// A collection of projects. - public async Task> GetProjectsAsync( - int? maxPages = null, - int? limit = null, - int? start = null, - string? name = null, - Permissions? permission = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["name"] = name, - ["permission"] = BitbucketHelpers.PermissionToString(permission), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsUrl() - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams projects accessible to the current user as they are retrieved, improving memory efficiency for large result sets. - /// - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional project name filter. - /// Optional permission filter. - /// Token to cancel the operation. - /// An async enumerable of projects. - public IAsyncEnumerable GetProjectsStreamAsync( - int? maxPages = null, - int? limit = null, - int? start = null, - string? name = null, - Permissions? permission = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["name"] = name, - ["permission"] = BitbucketHelpers.PermissionToString(permission), - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsUrl() - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Creates a project. - /// - /// The project definition. - /// Token to cancel the operation. - /// The created project. - public async Task CreateProjectAsync(ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) - { - var response = await GetProjectsUrl() - .SendAsync(HttpMethod.Post, CreateJsonContent(projectDefinition), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a project. - /// - /// The project key. - /// Token to cancel the operation. - /// true if deletion succeeded; otherwise, false. - public async Task DeleteProjectAsync(string projectKey, CancellationToken cancellationToken = default) - { - var response = await GetProjectsUrl($"/{projectKey}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a project. - /// - /// The project key. - /// The updated project definition. - /// Token to cancel the operation. - /// The updated project. - public async Task UpdateProjectAsync(string projectKey, ProjectDefinition projectDefinition, CancellationToken cancellationToken = default) - { - var response = await GetProjectsUrl($"/{projectKey}") - .SendAsync(HttpMethod.Put, CreateJsonContent(projectDefinition), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a project by key. - /// - /// The project key. - /// Token to cancel the operation. - /// The requested project. - public async Task GetProjectAsync(string projectKey, CancellationToken cancellationToken = default) - { - var response = await GetProjectsUrl($"/{projectKey}") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves user permissions for a project. - /// - /// The project key. - /// Optional filter string. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size. - /// Token to cancel the operation. - /// A collection of user permissions. - public async Task> GetProjectUserPermissionsAsync(string projectKey, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsUrl($"/{projectKey}/permissions/users") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Removes a user's permissions from a project. - /// - /// The project key. - /// The user name. - /// Token to cancel the operation. - /// true if removal succeeded; otherwise, false. - public async Task DeleteProjectUserPermissionsAsync(string projectKey, string userName, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["name"] = userName, - }; - - var response = await GetProjectsUrl($"/{projectKey}/permissions/users") - .SetQueryParams(queryParamValues) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a user's permissions for a project. - /// - /// The project key. - /// The user name. - /// The permission to grant. - /// Token to cancel the operation. - /// true if the update succeeded; otherwise, false. - public async Task UpdateProjectUserPermissionsAsync(string projectKey, string userName, Permissions permission, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["name"] = userName, - ["permission"] = BitbucketHelpers.PermissionToString(permission), - }; - - var response = await GetProjectsUrl($"/{projectKey}/permissions/users") - .SetQueryParams(queryParamValues) - .PutAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves users with no permissions on a project. - /// - /// The project key. - /// Optional filter string. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// A collection of licensed users without project permissions. - public async Task> GetProjectUserPermissionsNoneAsync(string projectKey, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsUrl($"/{projectKey}/permissions/users/none") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Retrieves group permissions for a project. - /// - /// The project key. - /// Optional filter string. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// A collection of group permissions. - public async Task> GetProjectGroupPermissionsAsync(string projectKey, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Removes a group's permissions from a project. - /// - /// The project key. - /// The group name. - /// Token to cancel the operation. - /// true if the group permissions were removed; otherwise, false. - public async Task DeleteProjectGroupPermissionsAsync(string projectKey, string groupName, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["name"] = groupName, - }; - - var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") - .SetQueryParams(queryParamValues) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a group's permissions for a project. - /// - /// The project key. - /// The group name. - /// The permission to grant. - /// Token to cancel the operation. - /// true if the update succeeded; otherwise, false. - public async Task UpdateProjectGroupPermissionsAsync(string projectKey, string groupName, Permissions permission, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["name"] = groupName, - ["permission"] = BitbucketHelpers.PermissionToString(permission), - }; - - var response = await GetProjectsUrl($"/{projectKey}/permissions/groups") - .SetQueryParams(queryParamValues) - .PutAsync(new StringContent(""), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves groups that currently have no permissions on a project. - /// - /// The project key. - /// Optional filter string. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// A collection of licensed users representing groups without permissions. - public async Task> GetProjectGroupPermissionsNoneAsync(string projectKey, string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsUrl($"/{projectKey}/permissions/groups/none") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Checks whether a permission is granted to all users for a project. - /// - /// The project key. - /// The permission to check. - /// Token to cancel the operation. - /// true if the permission is granted to all; otherwise, false. - public async Task IsProjectDefaultPermissionAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) - { - var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, s => - { - using var doc = JsonDocument.Parse(s); - return doc.RootElement.GetProperty("permitted").GetBoolean(); - }, cancellationToken) - .ConfigureAwait(false); - } - - private async Task SetProjectDefaultPermissionAsync(string projectKey, Permissions permission, bool allow, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["allow"] = BitbucketHelpers.BoolToString(allow), - }; - - var response = await GetProjectsUrl($"/{projectKey}/permissions/{BitbucketHelpers.PermissionToString(permission)}/all") - .SetQueryParams(queryParamValues) - .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Grants a permission to all users for a project. - /// - /// The project key. - /// The permission to grant. - /// Token to cancel the operation. - /// true if the permission was granted; otherwise, false. - public async Task GrantProjectPermissionToAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) - { - return await SetProjectDefaultPermissionAsync(projectKey, permission, allow: true, cancellationToken).ConfigureAwait(false); - } - - /// - /// Revokes a permission from all users for a project. - /// - /// The project key. - /// The permission to revoke. - /// Token to cancel the operation. - /// true if the permission was revoked; otherwise, false. - public async Task RevokeProjectPermissionFromAllAsync(string projectKey, Permissions permission, CancellationToken cancellationToken = default) - { - return await SetProjectDefaultPermissionAsync(projectKey, permission, allow: false, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves repositories for a project. - /// - /// The project key. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// A collection of repositories. - public async Task> GetProjectRepositoriesAsync(string projectKey, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsUrl($"/{projectKey}/repos") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams all repositories for a project as an . - /// - public IAsyncEnumerable GetProjectRepositoriesStreamAsync(string projectKey, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsUrl($"/{projectKey}/repos") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Creates a repository within a project. - /// - /// The project key. - /// The repository name. - /// Optional SCM identifier (default is git). - /// Token to cancel the operation. - /// The created repository. - public async Task CreateProjectRepositoryAsync(string projectKey, string repositoryName, string scmId = "git", CancellationToken cancellationToken = default) - { - var data = new - { - name = repositoryName, - scmId, - }; - - var response = await GetProjectsUrl($"/{projectKey}/repos") - .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a repository within a project. - /// - /// The project key. - /// The repository slug. - /// Token to cancel the operation. - /// The requested repository. - public async Task GetProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Creates a fork of a repository, optionally targeting another project or slug. - /// - /// The source project key. - /// The source repository slug. - /// Optional target project key for the fork. - /// Optional target repository slug. - /// Optional display name for the fork. - /// Token to cancel the operation. - /// The created repository fork. - public async Task CreateProjectRepositoryForkAsync(string projectKey, string repositorySlug, string? targetProjectKey = null, string? targetSlug = null, string? targetName = null, CancellationToken cancellationToken = default) - { - var data = new - { - slug = targetSlug ?? repositorySlug, - name = targetName, - project = targetProjectKey == null ? null : new ProjectRef { Key = targetProjectKey }, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Schedules a repository for deletion. - /// - /// The project key. - /// The repository slug. - /// Token to cancel the operation. - /// true if the repository was scheduled for deletion; otherwise, false. - public async Task ScheduleProjectRepositoryForDeletionAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates repository metadata such as name, forkability, project, or visibility. - /// - /// The project key. - /// The repository slug. - /// Optional new repository name. - /// Optional forkable flag. - /// Optional target project key. - /// Optional public visibility flag. - /// Token to cancel the operation. - /// The updated repository. - public async Task UpdateProjectRepositoryAsync(string projectKey, string repositorySlug, - string? targetName = null, - bool? isForkable = null, - string? targetProjectKey = null, - bool? isPublic = null, - CancellationToken cancellationToken = default) - { - var data = new - { - name = targetName, - forkable = isForkable, - project = targetProjectKey == null ? null : new ProjectRef { Key = targetProjectKey }, - @public = isPublic, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves forks of a repository. - /// - /// The project key. - /// The repository slug. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// A collection of repository forks. - public async Task> GetProjectRepositoryForksAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/forks") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Recreates a repository in place. - /// - /// The project key. - /// The repository slug. - /// Token to cancel the operation. - /// The recreated repository. - public async Task RecreateProjectRepositoryAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/recreate") - .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves repositories related to the specified repository (e.g., forks). - /// - /// The project key. - /// The repository slug. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// A collection of related repository forks. - public async Task> GetRelatedProjectRepositoriesAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/related") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Retrieves an archive (zip/tar) of a repository at a specific ref. - /// - /// The project key. - /// The repository slug. - /// The ref (commit/branch/tag) to archive. - /// The archive file name. - /// The archive format. - /// Optional path filter. - /// Optional archive prefix. - /// Token to cancel the operation. - /// Archive bytes. - public async Task GetProjectRepositoryArchiveAsync(string projectKey, string repositorySlug, - string at, - string fileName, - ArchiveFormats archiveFormat, - string path, - string prefix, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["at"] = at, - ["fileName"] = fileName, - ["format"] = BitbucketHelpers.ArchiveFormatToString(archiveFormat), - ["path"] = path, - ["prefix"] = prefix, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/archive") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); - return await ReadResponseBytesAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves group permissions for a repository. - /// - /// The project key. - /// The repository slug. - /// Optional filter string. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// A collection of group permissions. - public async Task> GetProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, - string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["filter"] = filter, - ["limit"] = limit, - ["start"] = start, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Updates a group's permissions for a repository. - /// - /// The project key. - /// The repository slug. - /// The permission to grant. - /// The group name. - /// Token to cancel the operation. - /// true if the update succeeded; otherwise, false. - public async Task UpdateProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["permission"] = BitbucketHelpers.PermissionToString(permission), - ["name"] = name, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") - .SetQueryParams(queryParamValues) - .SendAsync(HttpMethod.Put, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Removes a group's permissions from a repository. - /// - /// The project key. - /// The repository slug. - /// The group name. - /// Token to cancel the operation. - /// true if the permissions were removed; otherwise, false. - public async Task DeleteProjectRepositoryGroupPermissionsAsync(string projectKey, string repositorySlug, string name, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups") - .SetQueryParam("name", name) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves groups or users without permissions on a repository. - /// - /// The project key. - /// The repository slug. - /// Optional filter string. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// A collection of removable group or user entries. - public async Task> GetProjectRepositoryGroupPermissionsNoneAsync(string projectKey, string repositorySlug, - string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/groups/none") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Retrieves user permissions for a repository. - /// - /// The project key. - /// The repository slug. - /// Optional filter string. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size. - /// Token to cancel the operation. - /// A collection of user permissions. - public async Task> GetProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, - string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["filter"] = filter, - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Updates a user's permissions for a repository. - /// - /// The project key. - /// The repository slug. - /// The permission to grant. - /// The user name. - /// Token to cancel the operation. - /// true if the update succeeded; otherwise, false. - public async Task UpdateProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, Permissions permission, string name, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["permission"] = BitbucketHelpers.PermissionToString(permission), - ["name"] = name, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") - .SetQueryParams(queryParamValues) - .SendAsync(HttpMethod.Put, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Removes a user's permissions from a repository. - /// - /// The project key. - /// The repository slug. - /// The user name. - /// Optional avatar size. - /// Token to cancel the operation. - /// true if the permissions were removed; otherwise, false. - public async Task DeleteProjectRepositoryUserPermissionsAsync(string projectKey, string repositorySlug, string name, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users") - .SetQueryParam("name", name) - .SetQueryParam("avatarSize", avatarSize) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves users who have no permissions on a repository. - /// - /// The project key. - /// The repository slug. - /// Optional filter string. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// A collection of users without repository permissions. - public async Task> GetProjectRepositoryUserPermissionsNoneAsync(string projectKey, string repositorySlug, - string? filter = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["filter"] = filter, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/permissions/users/none") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Retrieves branches for a repository. - /// - /// The project key. - /// The repository slug. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional base branch or tag filter. - /// Whether to include additional details. - /// Optional branch name filter. - /// Optional branch ordering. - /// Cancellation token. - /// A collection of branches. - public async Task> GetBranchesAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - string? baseBranchOrTag = null, - bool? details = null, - string? filterText = null, - BranchOrderBy? orderBy = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["base"] = baseBranchOrTag, - ["details"] = details.HasValue ? BitbucketHelpers.BoolToString(details.Value) : null, - ["filterText"] = filterText, - ["orderBy"] = orderBy.HasValue ? BitbucketHelpers.BranchOrderByToString(orderBy.Value) : null, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams all branches for a repository as an IAsyncEnumerable. - /// - public IAsyncEnumerable GetBranchesStreamAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - string? baseBranchOrTag = null, - bool? details = null, - string? filterText = null, - BranchOrderBy? orderBy = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["base"] = baseBranchOrTag, - ["details"] = details.HasValue ? BitbucketHelpers.BoolToString(details.Value) : null, - ["filterText"] = filterText, - ["orderBy"] = orderBy.HasValue ? BitbucketHelpers.BranchOrderByToString(orderBy.Value) : null, - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Creates a branch in a repository. - /// - /// The project key. - /// The repository slug. - /// The branch information. - /// Cancellation token. - /// The created branch. - public async Task CreateBranchAsync(string projectKey, string repositorySlug, BranchInfo branchInfo, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .SendAsync(HttpMethod.Post, CreateJsonContent(branchInfo), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves the default branch for a repository. - /// - /// The project key. - /// The repository slug. - /// Cancellation token. - /// The default branch. - public async Task GetDefaultBranchAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches/default") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Sets the default branch for a repository. - /// - /// The project key. - /// The repository slug. - /// The target branch reference. - /// Cancellation token. - /// true if the default branch was updated; otherwise, false. - public async Task SetDefaultBranchAsync(string projectKey, string repositorySlug, BranchRef branchRef, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/branches") - .SendAsync(HttpMethod.Put, CreateJsonContent(branchRef), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Browses repository content at a specific ref. - /// - /// The project key. - /// The repository slug. - /// The ref (branch, tag, or commit). - /// Whether to include type information. - /// Whether to include blame metadata. - /// If true and blame is requested, omit file content. - /// Cancellation token. - /// The browsed item metadata. - public async Task BrowseProjectRepositoryAsync(string projectKey, string repositorySlug, string at, bool type = false, - bool blame = false, - bool noContent = false, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["at"] = at, - ["type"] = BitbucketHelpers.BoolToString(type), - }; - if (blame) - { - queryParamValues.Add("blame", value: null); - } - if (blame && noContent) - { - queryParamValues.Add("noContent", value: null); - } - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/browse") - .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Browses a specific path within a repository at a given ref. - /// - /// The project key. - /// The repository slug. - /// The path to browse. - /// The ref (branch, tag, or commit). - /// Whether to include type information. - /// Whether to include blame metadata. - /// If true and blame is requested, omit file content. - /// Cancellation token. - /// The browsed path item metadata. - public async Task BrowseProjectRepositoryPathAsync(string projectKey, string repositorySlug, string path, string at, bool type = false, - bool blame = false, - bool noContent = false, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["at"] = at, - ["type"] = BitbucketHelpers.BoolToString(type), - }; - if (blame) - { - queryParamValues.Add("blame", value: null); - } - if (blame && noContent) - { - queryParamValues.Add("noContent", value: null); - } - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") - .SetQueryParams(queryParamValues, Flurl.NullValueHandling.NameOnly) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Gets the raw content of a file as a stream. This is optimal for large files as it doesn't buffer the entire content in memory. - /// - /// The project key. - /// The repository slug. - /// The file path within the repository. - /// Optional ref (branch, tag, or commit) to get the file content at. Defaults to default branch. - /// Cancellation token. - /// A stream containing the raw file content. Caller is responsible for disposing. - public async Task GetRawFileContentStreamAsync(string projectKey, string repositorySlug, string path, - string? at = null, - CancellationToken cancellationToken = default) - { - var request = GetProjectsReposUrl(projectKey, repositorySlug, $"/raw/{path}"); - - if (!string.IsNullOrEmpty(at)) - { - request = request.SetQueryParam("at", at); - } - - var response = await request - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); - return await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Streams the raw content of a file line by line. This is optimal for large text files. - /// - /// The project key. - /// The repository slug. - /// The file path within the repository. - /// Optional ref (branch, tag, or commit) to get the file content at. Defaults to default branch. - /// Cancellation token. - /// An async enumerable of lines from the file. - public async IAsyncEnumerable GetRawFileContentLinesStreamAsync(string projectKey, string repositorySlug, string path, - string? at = null, - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var stream = await GetRawFileContentStreamAsync(projectKey, repositorySlug, path, at, cancellationToken).ConfigureAwait(false); - - await using (stream.ConfigureAwait(false)) - { - using var reader = new StreamReader(stream); - while (!reader.EndOfStream) - { - cancellationToken.ThrowIfCancellationRequested(); - var line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false); - if (line is not null) - { - yield return line; - } - } - } - } - - /// - /// Updates a file at the specified path in the repository. - /// Uses ArrayPool<byte> for zero-copy buffer management to minimize heap allocations. - /// - public async Task UpdateProjectRepositoryPathAsync(string projectKey, string repositorySlug, string path, - string fileName, - string branch, - string? message = null, - string? sourceCommitId = null, - string? sourceBranch = null, - CancellationToken cancellationToken = default) - { - if (!File.Exists(fileName)) - { - throw new ArgumentException($"File doesn't exist: {fileName}", nameof(fileName)); - } - - var fileInfo = new FileInfo(fileName); - int fileSize = checked((int)fileInfo.Length); - - // Use ArrayPool to rent a buffer instead of allocating new array - byte[] buffer = ArrayPool.Shared.Rent(fileSize); - try - { - int bytesRead; - var stm = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true); - await using (stm.ConfigureAwait(false)) - { - bytesRead = await stm.ReadAsync(buffer.AsMemory(0, fileSize), cancellationToken).ConfigureAwait(false); - } - - // Create MemoryStream over the exact bytes read (not the rented buffer size) - using var memoryStream = new MemoryStream(buffer, 0, bytesRead, writable: false); - - var data = new DynamicMultipartFormDataContent - { - { new StreamContent(memoryStream), "content" }, - { new StringContent(branch), "branch" }, - { message, message == null ? null : new StringContent(message), "message" }, - { sourceCommitId, sourceCommitId == null ? null : new StringContent(sourceCommitId), "sourceCommitId" }, - { sourceBranch, sourceBranch == null ? null : new StringContent(sourceBranch), "sourceBranch" }, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/browse/{path}") - .PutAsync(data.ToMultipartFormDataContent(), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - finally - { - // Always return the buffer to the pool - ArrayPool.Shared.Return(buffer); - } - } - - /// - /// Retrieves changes for a repository between two refs. - /// - /// The project key. - /// The repository slug. - /// The target ref. - /// Optional starting ref. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of changes. - public async Task> GetChangesAsync(string projectKey, string repositorySlug, string until, string? since = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["since"] = since, - ["until"] = until, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams changes for a repository between refs, yielding items as they are retrieved. - /// - /// The project key. - /// The repository slug. - /// The target ref. - /// Optional starting ref. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// An async enumerable of changes. - public IAsyncEnumerable GetChangesStreamAsync(string projectKey, string repositorySlug, string until, string? since = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["since"] = since, - ["until"] = until, - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/changes") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Retrieves commits for a repository. - /// - /// The project key. - /// The repository slug. - /// The ref to retrieve commits until. - /// Whether to follow renames. - /// Whether to ignore missing commits. - /// Merge commit inclusion policy. - /// Optional path filter. - /// Optional starting ref. - /// Whether to include commit counts. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of commits. - public async Task> GetCommitsAsync(string projectKey, string repositorySlug, - string until, - bool followRenames = false, - bool ignoreMissing = false, - MergeCommits merges = MergeCommits.Exclude, - string? path = null, - string? since = null, - bool withCounts = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["followRenames"] = BitbucketHelpers.BoolToString(followRenames), - ["ignoreMissing"] = BitbucketHelpers.BoolToString(ignoreMissing), - ["merges"] = BitbucketHelpers.MergeCommitsToString(merges), - ["path"] = path, - ["since"] = since, - ["until"] = until, - ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams all commits for a repository as an IAsyncEnumerable. - /// - public IAsyncEnumerable GetCommitsStreamAsync(string projectKey, string repositorySlug, - string until, - bool followRenames = false, - bool ignoreMissing = false, - MergeCommits merges = MergeCommits.Exclude, - string? path = null, - string? since = null, - bool withCounts = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["followRenames"] = BitbucketHelpers.BoolToString(followRenames), - ["ignoreMissing"] = BitbucketHelpers.BoolToString(ignoreMissing), - ["merges"] = BitbucketHelpers.MergeCommitsToString(merges), - ["path"] = path, - ["since"] = since, - ["until"] = until, - ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/commits") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Retrieves a commit by ID. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// Optional path filter. - /// Cancellation token. - /// The requested commit. - public async Task GetCommitAsync(string projectKey, string repositorySlug, string commitId, string? path = null, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["path"] = path, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves the list of file changes for a commit. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// Optional starting commit ID. - /// Whether to include comment counts. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of changes. - public async Task> GetCommitChangesAsync(string projectKey, string repositorySlug, string commitId, - string? since = null, - bool withComments = true, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["since"] = since, - ["withComments"] = BitbucketHelpers.BoolToString(withComments), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/changes") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams changes for a specific commit, yielding items as they are retrieved. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// Optional starting commit ID. - /// Whether to include comment counts. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// An async enumerable of changes. - public IAsyncEnumerable GetCommitChangesStreamAsync(string projectKey, string repositorySlug, string commitId, - string? since = null, - bool withComments = true, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["since"] = since, - ["withComments"] = BitbucketHelpers.BoolToString(withComments), - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/changes") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Retrieves comments for a commit. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// The file path within the commit. - /// Optional starting comment ID. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of comments. - public async Task> GetCommitCommentsAsync(string projectKey, string repositorySlug, string commitId, - string path, - string? since = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["path"] = path, - ["since"] = since, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Creates a comment on a commit. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// The comment payload. - /// Optional starting comment ID for context. - /// Cancellation token. - /// The created comment reference. - public async Task CreateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, - CommentInfo commentInfo, string? since = null, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["since"] = since, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments") - .SetQueryParams(queryParamValues) - .SendAsync(HttpMethod.Post, CreateJsonContent(commentInfo), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a specific commit comment by ID. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// The comment ID. - /// Optional avatar size. - /// Cancellation token. - /// The requested comment reference. - public async Task GetCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates the text of a commit comment. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// The comment ID. - /// The updated comment text. - /// Cancellation token. - /// The updated comment reference. - public async Task UpdateCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, - CommentText commentText, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") - .SendAsync(HttpMethod.Put, CreateJsonContent(commentText), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a commit comment. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// The comment ID. - /// Optional comment version for concurrency. - /// Cancellation token. - /// true if deleted; otherwise, false. - public async Task DeleteCommitCommentAsync(string projectKey, string repositorySlug, string commitId, long commentId, - int version = -1, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["version"] = version, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/comments/{commentId}") - .SetQueryParams(queryParamValues) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a diff for a commit. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// Whether to auto-detect source path. - /// Context lines to include. - /// Optional since commit. - /// Optional source path filter. - /// Whitespace handling strategy. - /// Whether to include comments. - /// Cancellation token. - /// The diff result. - public async Task GetCommitDiffAsync(string projectKey, string repositorySlug, string commitId, - bool autoSrcPath = false, - int contextLines = -1, - string? since = null, - string? srcPath = null, - string whitespace = "ignore-all", - bool withComments = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), - ["contextLines"] = contextLines, - ["since"] = since, - ["srcPath"] = srcPath, - ["whitespace"] = whitespace, - ["withComments"] = BitbucketHelpers.BoolToString(withComments), - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Streams the diff for a specific commit, yielding individual diff entries as they are parsed. - /// This is more memory-efficient for large diffs. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// Auto source path. - /// Number of context lines. - /// Since commit. - /// Source path filter. - /// Whitespace handling. - /// Include comments. - /// Cancellation token. - /// An async enumerable of diffs. - public async IAsyncEnumerable GetCommitDiffStreamAsync(string projectKey, string repositorySlug, string commitId, - bool autoSrcPath = false, - int contextLines = -1, - string? since = null, - string? srcPath = null, - string whitespace = "ignore-all", - bool withComments = true, - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["autoSrcPath"] = BitbucketHelpers.BoolToString(autoSrcPath), - ["contextLines"] = contextLines, - ["since"] = since, - ["srcPath"] = srcPath, - ["whitespace"] = whitespace, - ["withComments"] = BitbucketHelpers.BoolToString(withComments), - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/diff") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); - var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); - - await using (responseStream.ConfigureAwait(false)) - { - await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) - { - yield return diff; - } - } - } - - /// - /// Starts watching a commit for notifications. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// Cancellation token. - /// true if watch was created; otherwise, false. - public async Task CreateCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") - .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Stops watching a commit. - /// - /// The project key. - /// The repository slug. - /// The commit ID. - /// Cancellation token. - /// true if the watch was removed; otherwise, false. - public async Task DeleteCommitWatchAsync(string projectKey, string repositorySlug, string commitId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/commits/{commitId}/watch") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Compares two refs and returns the list of changes. - /// - /// The project key. - /// The repository slug. - /// The source ref. - /// The target ref. - /// Optional source repository key for cross-repo compare. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of changes between the refs. - public async Task> GetRepositoryCompareChangesAsync(string projectKey, string repositorySlug, string from, string to, - string? fromRepo = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["from"] = from, - ["to"] = to, - ["fromRepo"] = fromRepo, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/changes") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Compares two refs and returns a diff. - /// - /// The project key. - /// The repository slug. - /// The source ref. - /// The target ref. - /// Optional source repository key for cross-repo compare. - /// Optional source path filter. - /// Number of context lines. - /// Whitespace handling strategy. - /// Cancellation token. - /// The diff between the refs. - public async Task GetRepositoryCompareDiffAsync(string projectKey, string repositorySlug, string from, string to, - string? fromRepo = null, - string? srcPath = null, - int contextLines = -1, - string whitespace = "ignore-all", - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["from"] = from, - ["to"] = to, - ["fromRepo"] = fromRepo, - ["srcPath"] = srcPath, - ["contextLines"] = contextLines, - ["whitespace"] = whitespace, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Streams the compare diff between two refs, yielding individual diff entries as they are parsed. - /// This is more memory-efficient for large diffs. - /// - /// The project key. - /// The repository slug. - /// The source ref (branch, tag, or commit). - /// The target ref (branch, tag, or commit). - /// Optional source repository if comparing across forks. - /// Source path filter. - /// Number of context lines. - /// Whitespace handling. - /// Cancellation token. - /// An async enumerable of diffs. - public async IAsyncEnumerable GetRepositoryCompareDiffStreamAsync(string projectKey, string repositorySlug, string from, string to, - string? fromRepo = null, - string? srcPath = null, - int contextLines = -1, - string whitespace = "ignore-all", - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["from"] = from, - ["to"] = to, - ["fromRepo"] = fromRepo, - ["srcPath"] = srcPath, - ["contextLines"] = contextLines, - ["whitespace"] = whitespace, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/diff") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); - var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); - - await using (responseStream.ConfigureAwait(false)) - { - await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) - { - yield return diff; - } - } - } - - /// - /// Compares two refs and returns the commits between them. - /// - /// The project key. - /// The repository slug. - /// The source ref. - /// The target ref. - /// Optional source repository key for cross-repo compare. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of commits between the refs. - public async Task> GetRepositoryCompareCommitsAsync(string projectKey, string repositorySlug, string from, string to, - string? fromRepo = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["from"] = from, - ["to"] = to, - ["fromRepo"] = fromRepo, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/compare/commits") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Retrieves a repository diff between two commits. - /// - /// The project key. - /// The repository slug. - /// The target commit ID. - /// Number of context lines. - /// Optional starting commit ID. - /// Optional source path filter. - /// Whitespace handling strategy. - /// Cancellation token. - /// The diff result. - public async Task GetRepositoryDiffAsync(string projectKey, string repositorySlug, string until, - int contextLines = -1, - string? since = null, - string? srcPath = null, - string whitespace = "ignore-all", - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["contextLines"] = contextLines, - ["since"] = since, - ["srcPath"] = srcPath, - ["until"] = until, - ["whitespace"] = whitespace, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Streams the repository diff, yielding individual diff entries as they are parsed. - /// This is more memory-efficient for large diffs. - /// - /// The project key. - /// The repository slug. - /// The commit ID to diff until. - /// Number of context lines. - /// The commit ID to diff since. - /// Source path filter. - /// Whitespace handling. - /// Cancellation token. - /// An async enumerable of diffs. - public async IAsyncEnumerable GetRepositoryDiffStreamAsync(string projectKey, string repositorySlug, string until, - int contextLines = -1, - string? since = null, - string? srcPath = null, - string whitespace = "ignore-all", - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["contextLines"] = contextLines, - ["since"] = since, - ["srcPath"] = srcPath, - ["until"] = until, - ["whitespace"] = whitespace, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/diff") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); - var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); - - await using (responseStream.ConfigureAwait(false)) - { - await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) - { - yield return diff; - } - } - } - - /// - /// Retrieves file paths in a repository at the specified ref. - /// - /// The project key. - /// The repository slug. - /// Optional ref (branch, tag, commit). - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of file paths. - public async Task> GetRepositoryFilesAsync(string projectKey, string repositorySlug, string? at = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["at"] = at, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/files") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Retrieves last-modified metadata for a repository at a ref. - /// - /// The project key. - /// The repository slug. - /// The ref (branch, tag, or commit). - /// Cancellation token. - /// Last modified information. - public async Task GetProjectRepositoryLastModifiedAsync(string projectKey, string repositorySlug, string at, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/last-modified") - .SetQueryParam("at", at) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves participants related to pull requests in a repository. - /// - /// The project key. - /// The repository slug. - /// Direction of pull requests to consider. - /// Optional filter string. - /// Optional role filter. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of identities. - public async Task> GetRepositoryParticipantsAsync(string projectKey, string repositorySlug, - PullRequestDirections direction = PullRequestDirections.Incoming, - string? filter = null, - Roles? role = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), - ["filter"] = filter, - ["role"] = BitbucketHelpers.RoleToString(role), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/participants") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Retrieves pull requests for a repository with optional filtering. - /// - /// The project key. - /// The repository slug. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Pull request direction filter. - /// Optional branch filter. - /// Pull request state. - /// Ordering option. - /// Whether to include attributes. - /// Whether to include properties. - /// Cancellation token. - /// A collection of pull requests. - public async Task> GetPullRequestsAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - PullRequestDirections direction = PullRequestDirections.Incoming, - string? branchId = null, - PullRequestStates state = PullRequestStates.Open, - PullRequestOrders order = PullRequestOrders.Newest, - bool withAttributes = true, - bool withProperties = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), - ["at"] = branchId, - ["state"] = BitbucketHelpers.PullRequestStateToString(state), - ["order"] = BitbucketHelpers.PullRequestOrderToString(order), - ["withAttributes"] = BitbucketHelpers.BoolToString(withAttributes), - ["withProperties"] = BitbucketHelpers.BoolToString(withProperties), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams all pull requests for a repository as an IAsyncEnumerable. - /// - public IAsyncEnumerable GetPullRequestsStreamAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - PullRequestDirections direction = PullRequestDirections.Incoming, - string? branchId = null, - PullRequestStates state = PullRequestStates.Open, - PullRequestOrders order = PullRequestOrders.Newest, - bool withAttributes = true, - bool withProperties = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["direction"] = BitbucketHelpers.PullRequestDirectionToString(direction), - ["at"] = branchId, - ["state"] = BitbucketHelpers.PullRequestStateToString(state), - ["order"] = BitbucketHelpers.PullRequestOrderToString(order), - ["withAttributes"] = BitbucketHelpers.BoolToString(withAttributes), - ["withProperties"] = BitbucketHelpers.BoolToString(withProperties), - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Creates a pull request in a repository. - /// - /// The project key. - /// The repository slug. - /// The pull request payload. - /// Cancellation token. - /// The created pull request. - public async Task CreatePullRequestAsync(string projectKey, string repositorySlug, PullRequestInfo pullRequestInfo, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/pull-requests") - .SendAsync(HttpMethod.Post, CreateJsonContent(pullRequestInfo), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a pull request by ID. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Cancellation token. - /// The requested pull request. - public async Task GetPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The update payload. - /// Cancellation token. - /// The updated pull request. - public async Task UpdatePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, PullRequestUpdate pullRequestUpdate, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") - .SendAsync(HttpMethod.Put, CreateJsonContent(pullRequestUpdate), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Version info for optimistic concurrency. - /// Cancellation token. - /// true if the pull request was deleted; otherwise, false. - public async Task DeletePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, VersionInfo versionInfo, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}") - .SendAsync(HttpMethod.Delete, CreateJsonContent(versionInfo), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves activities for a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional starting activity ID. - /// Optional activity type filter. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size. - /// Cancellation token. - /// A collection of pull request activities. - public async Task> GetPullRequestActivitiesAsync(string projectKey, string repositorySlug, long pullRequestId, - long? fromId = null, - PullRequestFromTypes? fromType = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["fromId"] = fromId, - ["fromType"] = BitbucketHelpers.PullRequestFromTypeToString(fromType), - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/activities") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams activities for a pull request, yielding items as they are retrieved. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional starting activity ID. - /// Optional from type filter. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size. - /// Token to cancel the operation. - /// An async enumerable of pull request activities. - public IAsyncEnumerable GetPullRequestActivitiesStreamAsync(string projectKey, string repositorySlug, long pullRequestId, - long? fromId = null, - PullRequestFromTypes? fromType = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["fromId"] = fromId, - ["fromType"] = BitbucketHelpers.PullRequestFromTypeToString(fromType), - ["avatarSize"] = avatarSize, - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/activities") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Declines a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional version for optimistic concurrency. - /// Cancellation token. - /// true if the pull request was declined; otherwise, false. - public async Task DeclinePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["version"] = version, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/decline") - .SetQueryParams(queryParamValues) - .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves the merge state for a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional version for optimistic concurrency. - /// Cancellation token. - /// The merge state. - public async Task GetPullRequestMergeStateAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["version"] = version, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Gets the merge base (common ancestor) commit for a pull request. - /// This is the best common ancestor between the latest commits of the source and target branches. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Cancellation token. - /// The merge base commit, or null if not found (HTTP 204 - no common ancestor exists). - /// - /// This endpoint is useful for creating line-specific comments on pull requests. - /// The returned commit ID can be used as the fromHash parameter when creating anchored comments, - /// while the toHash can be obtained from on the pull request's FromRef. - /// - public async Task GetPullRequestMergeBaseAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge-base") - .AllowHttpStatus(204) - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - // HTTP 204 indicates no common ancestor exists (e.g., unrelated histories) - if (response.StatusCode == 204) - { - return null; - } - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Merges a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional version for optimistic concurrency. - /// Cancellation token. - /// The merged pull request. - public async Task MergePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["version"] = version, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/merge") - .SetQueryParams(queryParamValues) - .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Reopens a declined pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional version for optimistic concurrency. - /// Cancellation token. - /// The reopened pull request. - public async Task ReopenPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, int version = -1, CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["version"] = version, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/reopen") - .SetQueryParams(queryParamValues) - .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Approves a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Cancellation token. - /// The reviewer entry reflecting the approval. - public async Task ApprovePullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") - .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Removes an approval from a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Cancellation token. - /// The reviewer entry after removal. - public async Task DeletePullRequestApprovalAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/approve") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves changes for a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Scope of changes to include. - /// Optional since commit ID. - /// Optional until commit ID. - /// Whether to include comment counts. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of changes. - public async Task> GetPullRequestChangesAsync(string projectKey, string repositorySlug, long pullRequestId, - ChangeScopes changeScope = ChangeScopes.All, - string? sinceId = null, - string? untilId = null, - bool withComments = true, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["changeScope"] = BitbucketHelpers.ChangeScopeToString(changeScope), - ["sinceId"] = sinceId, - ["untilId"] = untilId, - ["withComments"] = BitbucketHelpers.BoolToString(withComments), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/changes") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams changes for a pull request, yielding items as they are retrieved. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The change scope filter. - /// Optional since commit ID. - /// Optional until commit ID. - /// Whether to include comment counts. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// An async enumerable of changes. - public IAsyncEnumerable GetPullRequestChangesStreamAsync(string projectKey, string repositorySlug, long pullRequestId, - ChangeScopes changeScope = ChangeScopes.All, - string? sinceId = null, - string? untilId = null, - bool withComments = true, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["changeScope"] = BitbucketHelpers.ChangeScopeToString(changeScope), - ["sinceId"] = sinceId, - ["untilId"] = untilId, - ["withComments"] = BitbucketHelpers.BoolToString(withComments), - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/changes") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Creates a comment on a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The comment text. - /// Optional parent comment ID to create a reply. - /// Optional diff type. - /// Optional from commit hash for anchoring. - /// Optional file path for anchoring. - /// Optional source path for move/rename anchors. - /// Optional to commit hash for anchoring. - /// Optional line number for anchoring. - /// Optional file type for anchoring. - /// Optional line type for anchoring. - /// Cancellation token. - /// The created comment reference. - public async Task CreatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, - string text, - string? parentId = null, - DiffTypes? diffType = null, - string? fromHash = null, - string? path = null, - string? srcPath = null, - string? toHash = null, - int? line = null, - FileTypes? fileType = null, - LineTypes? lineType = null, - CancellationToken cancellationToken = default) - { - // Build the comment payload dynamically to avoid sending empty anchor objects - // which Bitbucket Server 9.0 rejects with HTTP 500. - // See: BUG-003 - add_pull_request_comment returns 500 error - var data = new Dictionary(StringComparer.Ordinal) - { - ["text"] = text, - }; - - if (!string.IsNullOrEmpty(parentId)) - { - data["parent"] = new { id = parentId }; - } - - // Only include anchor if at least one anchor-related field is specified - // Empty anchor objects cause HTTP 500 on Bitbucket Server 9.0 - var hasAnchorData = diffType.HasValue - || !string.IsNullOrEmpty(fromHash) - || !string.IsNullOrEmpty(path) - || !string.IsNullOrEmpty(srcPath) - || !string.IsNullOrEmpty(toHash) - || line.HasValue - || fileType.HasValue - || lineType.HasValue; - - if (hasAnchorData) - { - data["anchor"] = new - { - diffType = BitbucketHelpers.DiffTypeToString(diffType), - fromHash, - path, - srcPath, - toHash, - line, - fileType = BitbucketHelpers.FileTypeToString(fileType), - lineType = BitbucketHelpers.LineTypeToString(lineType), - }; - } - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/comments") - .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves comments for a pull request path. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The file path to filter comments. - /// Anchor state filter. - /// Diff type filter. - /// Optional from commit hash. - /// Optional to commit hash. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size. - /// Cancellation token. - /// A collection of pull request comments. - public async Task> GetPullRequestCommentsAsync(string projectKey, string repositorySlug, long pullRequestId, - string path, - AnchorStates anchorState = AnchorStates.Active, - DiffTypes diffType = DiffTypes.Effective, - string? fromHash = null, - string? toHash = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - ["path"] = path, - ["anchorState"] = BitbucketHelpers.AnchorStateToString(anchorState), - ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), - ["fromHash"] = fromHash, - ["toHash"] = toHash, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/comments") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams comments for a pull request, yielding items as they are retrieved. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The file path to filter comments for. - /// The anchor state filter. - /// The diff type filter. - /// Optional from commit hash. - /// Optional to commit hash. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size. - /// Token to cancel the operation. - /// An async enumerable of comment references. - public IAsyncEnumerable GetPullRequestCommentsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, - string path, - AnchorStates anchorState = AnchorStates.Active, - DiffTypes diffType = DiffTypes.Effective, - string? fromHash = null, - string? toHash = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - ["path"] = path, - ["anchorState"] = BitbucketHelpers.AnchorStateToString(anchorState), - ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), - ["fromHash"] = fromHash, - ["toHash"] = toHash, - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/comments") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Retrieves a single pull request comment by ID. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The comment ID. - /// Optional avatar size. - /// Cancellation token. - /// The requested comment reference. - public async Task GetPullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a pull request comment. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The comment ID. - /// The comment version for optimistic concurrency. - /// The updated comment text. - /// Cancellation token. - /// The updated comment reference. - public async Task UpdatePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, - int version, string text, CancellationToken cancellationToken = default) - { - var data = new - { - version, - text, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") - .SetQueryParam("version", version) - .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a pull request comment. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The comment ID. - /// Optional version for optimistic concurrency. - /// Cancellation token. - /// true if the comment was deleted; otherwise, false. - public async Task DeletePullRequestCommentAsync(string projectKey, string repositorySlug, long pullRequestId, long commentId, - int version = -1, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/comments/{commentId}") - .SetQueryParam("version", version) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves commits associated with a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Whether to include change counts. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of commits. - public async Task> GetPullRequestCommitsAsync(string projectKey, string repositorySlug, long pullRequestId, - bool withCounts = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams all commits for a pull request as an IAsyncEnumerable. - /// - public IAsyncEnumerable GetPullRequestCommitsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, - bool withCounts = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["withCounts"] = BitbucketHelpers.BoolToString(withCounts), - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/commits") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Retrieves the diff for a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Number of context lines to include. - /// Diff type. - /// Optional since commit ID. - /// Optional source path filter. - /// Optional until commit ID. - /// Whitespace handling option. - /// Whether to include comments. - /// Cancellation token. - /// Differences for the pull request. - public async Task GetPullRequestDiffAsync(string projectKey, string repositorySlug, long pullRequestId, - int contextLines = -1, - DiffTypes diffType = DiffTypes.Effective, - string? sinceId = null, - string? srcPath = null, - string? untilId = null, - string whitespace = "ignore-all", - bool withComments = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Streams diff entries for a pull request as an . - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Number of context lines to include. - /// Diff type. - /// Optional since commit ID. - /// Optional source path filter. - /// Optional until commit ID. - /// Whitespace handling option. - /// Whether to include comments. - /// Cancellation token. - /// A stream of diff entries. - public async IAsyncEnumerable GetPullRequestDiffStreamAsync(string projectKey, string repositorySlug, long pullRequestId, - int contextLines = -1, - DiffTypes diffType = DiffTypes.Effective, - string? sinceId = null, - string? srcPath = null, - string? untilId = null, - string whitespace = "ignore-all", - bool withComments = true, - [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/diff") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); - var responseStream = await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); - - try - { - await foreach (var diff in DeserializePullRequestDiffsAsync(responseStream, cancellationToken).ConfigureAwait(false)) - { - yield return diff; - } - } - finally - { - await responseStream.DisposeAsync().ConfigureAwait(false); - } - } - - /// - /// Retrieves the diff for a specific path within a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The file path to filter by. - /// Number of context lines to include. - /// Diff type. - /// Optional since commit ID. - /// Optional source path filter. - /// Optional until commit ID. - /// Whitespace handling option. - /// Whether to include comments. - /// Cancellation token. - /// Differences for the specified path. - public async Task GetPullRequestDiffPathAsync(string projectKey, string repositorySlug, long pullRequestId, - string path, - int contextLines = -1, - DiffTypes diffType = DiffTypes.Effective, - string? sinceId = null, - string? srcPath = null, - string? untilId = null, - string whitespace = "ignore-all", - bool withComments = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = CreatePullRequestDiffQueryParams(contextLines, diffType, sinceId, srcPath, untilId, whitespace, withComments); - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/diff/{path}") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - private static Dictionary CreatePullRequestDiffQueryParams(int contextLines, DiffTypes diffType, string? sinceId, - string? srcPath, string? untilId, string whitespace, bool withComments) - { - return new Dictionary(StringComparer.Ordinal) - { - ["contextLines"] = contextLines, - ["diffType"] = BitbucketHelpers.DiffTypeToString(diffType), - ["sinceId"] = sinceId, - ["srcPath"] = srcPath, - ["untilId"] = untilId, - ["whitespace"] = whitespace, - ["withComments"] = BitbucketHelpers.BoolToString(withComments), - }; - } - - private static async IAsyncEnumerable DeserializePullRequestDiffsAsync(Stream responseStream, - [EnumeratorCancellation] CancellationToken cancellationToken) - { - await foreach (var diff in DeserializeDiffsFromStreamAsync(responseStream, cancellationToken).ConfigureAwait(false)) - { - yield return diff; - } - } - - /// - /// Deserializes diff entries from a JSON stream containing a "diffs" array. - /// Used by all diff streaming methods (commit, repository, compare, pull request). - /// Uses zero-copy deserialization directly from JsonElement to avoid intermediate string allocations. - /// - private static async IAsyncEnumerable DeserializeDiffsFromStreamAsync(Stream responseStream, - [EnumeratorCancellation] CancellationToken cancellationToken) - { - using var doc = await JsonDocument.ParseAsync(responseStream, cancellationToken: cancellationToken).ConfigureAwait(false); - - if (!doc.RootElement.TryGetProperty("diffs", out var diffsArray) || diffsArray.ValueKind != JsonValueKind.Array) - { - yield break; - } - - foreach (var diffElement in diffsArray.EnumerateArray()) - { - cancellationToken.ThrowIfCancellationRequested(); - - // Zero-copy: Deserialize directly from JsonElement instead of GetRawText() string allocation - var diff = diffElement.Deserialize(s_jsonOptions); - if (diff is not null) - { - yield return diff; - } - } - } - - // Note: MoveToDiffArrayAsync is no longer needed with System.Text.Json approach - - /// - /// Retrieves participants for a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size. - /// Cancellation token. - /// A collection of participants. - public async Task> GetPullRequestParticipantsAsync(string projectKey, string repositorySlug, long pullRequestId, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams participants for a pull request, yielding items as they are retrieved. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size. - /// Token to cancel the operation. - /// An async enumerable of participants. - public IAsyncEnumerable GetPullRequestParticipantsStreamAsync(string projectKey, string repositorySlug, long pullRequestId, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Assigns a role to a user in a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The user to assign. - /// The role to assign. - /// Cancellation token. - /// The created participant entry. - public async Task AssignUserRoleToPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, - Named named, - Roles role, - CancellationToken cancellationToken = default) - { - var data = new - { - user = named, - role = BitbucketHelpers.RoleToString(role), - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") - .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a participant from a pull request by username. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The username to remove. - /// Cancellation token. - /// true if removal succeeded; otherwise, false. - public async Task DeletePullRequestParticipantAsync(string projectKey, string repositorySlug, long pullRequestId, string userName, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants") - .SetQueryParam("username", userName) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a participant's approval status on a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The user slug to update. - /// The user identity. - /// Whether the participant approves the PR. - /// The participant status. - /// Cancellation token. - /// The updated participant entry. - public async Task UpdatePullRequestParticipantStatus(string projectKey, string repositorySlug, long pullRequestId, - string userSlug, - Named named, - bool approved, - ParticipantStatus participantStatus, - CancellationToken cancellationToken = default) - { - var data = new - { - user = named, - approved = BitbucketHelpers.BoolToString(approved), - status = BitbucketHelpers.ParticipantStatusToString(participantStatus), - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants/{userSlug}") - .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Removes a participant from a pull request by user slug. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The user slug to remove. - /// Cancellation token. - /// true if removal succeeded; otherwise, false. - public async Task UnassignUserFromPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, string userSlug, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/participants/{userSlug}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Gets tasks for a pull request using the legacy tasks endpoint. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Maximum number of pages to retrieve. - /// Maximum number of results per page. - /// Pagination start index. - /// Avatar size for user avatars. - /// Cancellation token. - /// Collection of tasks. - /// - /// - /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and returns 404 Not Found on servers version 9.0+. - /// - /// - /// For Bitbucket Server 9.0+, use instead. - /// For cross-version compatibility, use . - /// - /// - [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsAsync for 9.0+ or GetPullRequestTasksWithFallbackAsync for cross-version compatibility.")] - public async Task> GetPullRequestTasksAsync(string projectKey, string repositorySlug, long pullRequestId, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/tasks") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams tasks for a pull request, yielding items as they are retrieved. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size. - /// Token to cancel the operation. - /// An async enumerable of tasks. - /// - /// - /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and returns 404 Not Found on servers version 9.0+. - /// - /// - /// For Bitbucket Server 9.0+, use instead. - /// - /// - [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsStreamAsync for 9.0+ compatibility.")] - public IAsyncEnumerable GetPullRequestTasksStreamAsync(string projectKey, string repositorySlug, long pullRequestId, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/tasks") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Gets the task count for a pull request using the legacy tasks endpoint. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Cancellation token. - /// The task count. - /// - /// - /// Deprecation Notice: This endpoint was deprecated in Bitbucket Server 9.0 and may return 404 Not Found on servers version 9.0+. - /// - /// - /// For Bitbucket Server 9.0+, use and count the results. - /// - /// - [Obsolete("This endpoint is deprecated in Bitbucket Server 9.0+. Use GetPullRequestBlockerCommentsAsync and count the results for 9.0+ compatibility.")] - public async Task GetPullRequestTaskCountAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/tasks/count") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - #region Blocker Comments (Bitbucket Server 9.0+) - - /// - /// Gets blocker comments (tasks) for a pull request. - /// This endpoint is available in Bitbucket Server 9.0+ and replaces the legacy tasks endpoint. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional filter: , , or null for all. - /// Maximum number of pages to retrieve. - /// Maximum number of results per page. - /// Pagination start index. - /// Cancellation token. - /// Collection of blocker comments. - /// - /// - /// In Bitbucket Server 9.0+, tasks have been replaced by blocker comments. - /// A blocker comment is a comment with severity: 'BLOCKER' that must be resolved before the pull request can be merged. - /// - /// - /// For servers prior to 9.0, use instead. - /// - /// - public async Task> GetPullRequestBlockerCommentsAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - BlockerCommentState? state = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["state"] = BitbucketHelpers.BlockerCommentStateToString(state), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/blocker-comments") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams blocker comments for a pull request, yielding items as they are retrieved. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Optional blocker comment state filter. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// An async enumerable of blocker comments. - public IAsyncEnumerable GetPullRequestBlockerCommentsStreamAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - BlockerCommentState? state = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["state"] = BitbucketHelpers.BlockerCommentStateToString(state), - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/pull-requests/{pullRequestId}/blocker-comments") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Gets a single blocker comment by ID. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// Cancellation token. - /// The blocker comment. - public async Task GetPullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Creates a blocker comment (task) on a pull request. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment text. - /// Optional anchor for file/line-specific blockers. - /// Cancellation token. - /// The created blocker comment. - public async Task CreatePullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - string text, - CommentAnchor? anchor = null, - CancellationToken cancellationToken = default) - { - var data = new - { - text, - severity = "BLOCKER", - anchor, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments") - .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a blocker comment's text. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// The updated blocker comment text. - /// The version of the blocker comment (for optimistic locking). - /// Cancellation token. - /// The updated blocker comment. - public async Task UpdatePullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - string text, - int version, - CancellationToken cancellationToken = default) - { - var data = new - { - text, - version, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") - .SendAsync(HttpMethod.Put, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a blocker comment. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// The version of the blocker comment (for optimistic locking). - /// Cancellation token. - /// True if the blocker comment was deleted successfully. - public async Task DeletePullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - int version, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}") - .SetQueryParam("version", version) - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Resolves a blocker comment (marks the task as complete). - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// The version of the blocker comment (for optimistic locking). - /// Cancellation token. - /// The resolved blocker comment. - public async Task ResolvePullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - int version, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}/resolve") - .SetQueryParam("version", version) - .PutAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Reopens a resolved blocker comment. - /// This endpoint is available in Bitbucket Server 9.0+. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// The blocker comment ID. - /// The version of the blocker comment (for optimistic locking). - /// Cancellation token. - /// The reopened blocker comment. - public async Task ReopenPullRequestBlockerCommentAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - long blockerCommentId, - int version, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/blocker-comments/{blockerCommentId}/reopen") - .SetQueryParam("version", version) - .PutAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Gets pull request tasks with automatic fallback for cross-version compatibility. - /// - /// - /// - /// This method provides backward compatibility across Bitbucket Server versions: - /// - /// - /// Bitbucket Server 9.0+: Uses the new /blocker-comments endpoint. - /// Bitbucket Server < 9.0: Falls back to the legacy /tasks endpoint. - /// - /// - /// The method first tries the new blocker-comments endpoint. If it returns 404 (Not Found), - /// it automatically falls back to the legacy tasks endpoint. - /// - /// - /// For new code targeting Bitbucket Server 9.0+, prefer using - /// directly for better type safety. - /// - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Maximum number of pages to retrieve. - /// Maximum number of results per page. - /// Pagination start index. - /// Cancellation token. - /// - /// A collection of blocker comments () on Bitbucket 9.0+, - /// or legacy tasks () on older versions. - /// - public async Task> GetPullRequestTasksWithFallbackAsync( - string projectKey, - string repositorySlug, - long pullRequestId, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - try - { - // Try new blocker-comments endpoint first (Bitbucket 9.0+) - var blockerComments = await GetPullRequestBlockerCommentsAsync( - projectKey, repositorySlug, pullRequestId, - maxPages: maxPages, limit: limit, start: start, - cancellationToken: cancellationToken).ConfigureAwait(false); - - return blockerComments.Cast(); - } - catch (BitbucketNotFoundException) - { - // Fall back to legacy tasks endpoint (Bitbucket < 9.0) -#pragma warning disable CS0618 // Type or member is obsolete - intentional fallback - var tasks = await GetPullRequestTasksAsync( - projectKey, repositorySlug, pullRequestId, - maxPages: maxPages, limit: limit, start: start, - cancellationToken: cancellationToken).ConfigureAwait(false); -#pragma warning restore CS0618 - - return tasks.Cast(); - } - } - - #endregion - - /// - /// Subscribes the current user to watch a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Cancellation token. - /// true if the watch was added; otherwise, false. - public async Task WatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/watch") - .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Unsubscribes the current user from watching a pull request. - /// - /// The project key. - /// The repository slug. - /// The pull request ID. - /// Cancellation token. - /// true if the watch was removed; otherwise, false. - public async Task UnwatchPullRequestAsync(string projectKey, string repositorySlug, long pullRequestId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/pull-requests/{pullRequestId}/watch") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves raw content from a file path in a repository. - /// - /// The project key. - /// The repository slug. - /// The file path to fetch. - /// Optional ref (branch, tag, commit). - /// Whether to render markup. - /// Whether to hard wrap the output. - /// Whether to HTML-escape the output. - /// Cancellation token. - /// A stream containing the raw content. - public async Task RetrieveRawContentAsync(string projectKey, string repositorySlug, string path, - string? at = null, - bool markup = false, - bool hardWrap = true, - bool htmlEscape = true, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["at"] = at, - ["markup"] = BitbucketHelpers.BoolToString(markup), - ["hardWrap"] = BitbucketHelpers.BoolToString(hardWrap), - ["htmlEscape"] = BitbucketHelpers.BoolToString(htmlEscape), - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug) - .AppendPathSegment($"/raw/{path}") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - await HandleErrorsAsync(response, cancellationToken).ConfigureAwait(false); - return await ReadResponseStreamAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves pull request settings for a repository. - /// - /// The project key. - /// The repository slug. - /// Cancellation token. - /// The pull request settings. - public async Task GetProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates pull request settings for a repository. - /// - /// The project key. - /// The repository slug. - /// The settings payload. - /// Cancellation token. - /// The updated pull request settings. - public async Task UpdateProjectRepositoryPullRequestSettingsAsync(string projectKey, string repositorySlug, - PullRequestSettings pullRequestSettings, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/pull-requests") - .SendAsync(HttpMethod.Post, CreateJsonContent(pullRequestSettings), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves repository hooks. - /// - /// The project key. - /// The repository slug. - /// Optional hook type filter. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of hooks. - public async Task> GetProjectRepositoryHooksSettingsAsync(string projectKey, string repositorySlug, - HookTypes? hookType = null, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["type"] = hookType, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/settings/hooks") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Retrieves a specific hook's settings. - /// - /// The project key. - /// The repository slug. - /// The hook key. - /// Cancellation token. - /// The hook configuration. - public async Task GetProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a repository hook. - /// - /// The project key. - /// The repository slug. - /// The hook key. - /// Cancellation token. - /// true if deletion succeeded; otherwise, false. - public async Task DeleteProjectRepositoryHookSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Enables a repository hook, optionally providing settings. - /// - /// The project key. - /// The repository slug. - /// The hook key. - /// Optional hook settings. - /// Cancellation token. - /// The enabled hook. - public async Task EnableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, object? hookSettings = null, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") - .SendAsync(HttpMethod.Put, CreateJsonContent(hookSettings), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Disables a repository hook. - /// - /// The project key. - /// The repository slug. - /// The hook key. - /// Cancellation token. - /// The disabled hook. - public async Task DisableProjectRepositoryHookAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/enabled") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves all settings for a repository hook. - /// - /// The project key. - /// The repository slug. - /// The hook key. - /// Cancellation token. - /// A dictionary of hook settings. - public async Task> GetProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates all settings for a repository hook. - /// - /// The project key. - /// The repository slug. - /// The hook key. - /// The settings payload. - /// Cancellation token. - /// The updated settings. - public async Task> UpdateProjectRepositoryHookAllSettingsAsync(string projectKey, string repositorySlug, string hookKey, - Dictionary allSettings, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/settings/hooks/{hookKey}/settings") - .SendAsync(HttpMethod.Put, CreateJsonContent(allSettings), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves merge strategies for pull requests within a project SCM. - /// - /// The project key. - /// The SCM identifier. - /// Cancellation token. - /// The pull request settings. - public async Task GetProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, CancellationToken cancellationToken = default) - { - var response = await GetProjectUrl(projectKey) - .AppendPathSegment($"/settings/pull-requests/{scmId}") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates merge strategies for pull requests within a project SCM. - /// - /// The project key. - /// The SCM identifier. - /// The merge strategies payload. - /// Cancellation token. - /// The updated merge strategies. - public async Task UpdateProjectPullRequestsMergeStrategiesAsync(string projectKey, string scmId, MergeStrategies mergeStrategies, CancellationToken cancellationToken = default) - { - var response = await GetProjectUrl(projectKey) - .AppendPathSegment($"/settings/pull-requests/{scmId}") - .SendAsync(HttpMethod.Post, CreateJsonContent(mergeStrategies), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves tags from a repository. - /// - /// The project key. - /// The repository slug. - /// Filter text for tag names. - /// Ordering option. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of tags. - public async Task> GetProjectRepositoryTagsAsync(string projectKey, string repositorySlug, - string filterText, - BranchOrderBy orderBy, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["filterText"] = filterText, - ["orderBy"] = BitbucketHelpers.BranchOrderByToString(orderBy), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Streams tags for a repository, yielding items as they are retrieved. - /// - /// The project key. - /// The repository slug. - /// Filter text for tag names. - /// Ordering option. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Token to cancel the operation. - /// An async enumerable of tags. - public IAsyncEnumerable GetProjectRepositoryTagsStreamAsync(string projectKey, string repositorySlug, - string filterText, - BranchOrderBy orderBy, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["filterText"] = filterText, - ["orderBy"] = BitbucketHelpers.BranchOrderByToString(orderBy), - }; - - return GetPagedResultsStreamAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken); - } - - /// - /// Creates a tag in a repository. - /// - /// The project key. - /// The repository slug. - /// The tag name. - /// The starting commit or ref. - /// The tag message. - /// Cancellation token. - /// The created tag. - public async Task CreateProjectRepositoryTagAsync(string projectKey, string repositorySlug, - string name, - string startPoint, - string message, - CancellationToken cancellationToken = default) - { - var data = new - { - name, - startPoint, - message, - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/tags") - .SendAsync(HttpMethod.Post, CreateJsonContent(data), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a tag by name. - /// - /// The project key. - /// The repository slug. - /// The tag name. - /// Cancellation token. - /// The requested tag. - public async Task GetProjectRepositoryTagAsync(string projectKey, string repositorySlug, string tagName, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/tags/{tagName}") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves webhooks for a repository. - /// - /// The project key. - /// The repository slug. - /// Optional event filter. - /// Whether to include statistics. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Cancellation token. - /// A collection of webhooks. - public async Task> GetProjectRepositoryWebHooksAsync(string projectKey, string repositorySlug, - string? @event = null, - bool statistics = false, - int? maxPages = null, - int? limit = null, - int? start = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["event"] = @event, - ["statistics"] = BitbucketHelpers.BoolToString(statistics), - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Creates a webhook for a repository. - /// - /// The project key. - /// The repository slug. - /// The webhook payload. - /// Cancellation token. - /// The created webhook. - public async Task CreateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, WebHook webHook, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks") - .SendAsync(HttpMethod.Post, CreateJsonContent(webHook), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Tests a webhook delivery for a repository. - /// - /// The project key. - /// The repository slug. - /// The URL to test. - /// Cancellation token. - /// The webhook test response. - public async Task TestProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, string url, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, "/webhooks/test") - .SetQueryParam("url", url) - .SendAsync(HttpMethod.Post, new StringContent(string.Empty), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a webhook by ID. - /// - /// The project key. - /// The repository slug. - /// The webhook ID. - /// Whether to include statistics. - /// Cancellation token. - /// The webhook. - public async Task GetProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, - string webHookId, - bool statistics = false, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["statistics"] = BitbucketHelpers.BoolToString(statistics), - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a webhook. - /// - /// The project key. - /// The repository slug. - /// The webhook ID. - /// The webhook payload. - /// Cancellation token. - /// The updated webhook. - public async Task UpdateProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, - string webHookId, WebHook webHook, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") - .SendAsync(HttpMethod.Put, CreateJsonContent(webHook), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a webhook. - /// - /// The project key. - /// The repository slug. - /// The webhook ID. - /// Cancellation token. - /// true if deletion succeeded; otherwise, false. - public async Task DeleteProjectRepositoryWebHookAsync(string projectKey, string repositorySlug, - string webHookId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - //public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, - /// - /// Retrieves the latest webhook invocation summary as a string. - /// - /// The project key. - /// The repository slug. - /// The webhook ID. - /// Optional event filter. - /// Optional outcome filter. - /// Cancellation token. - /// The latest invocation payload. - public async Task GetProjectRepositoryWebHookLatestAsync(string projectKey, string repositorySlug, - string webHookId, - string? @event = null, - WebHookOutcomes? outcome = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["event"] = @event, - ["outcome"] = BitbucketHelpers.WebHookOutcomeToString(outcome), - }; - - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/latest") - .SetQueryParams(queryParamValues) - //.GetJsonAsync() - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, s => s, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves webhook statistics. - /// - /// The project key. - /// The repository slug. - /// The webhook ID. - /// Optional event filter. - /// Cancellation token. - /// Webhook statistics. - public async Task GetProjectRepositoryWebHookStatisticsAsync(string projectKey, string repositorySlug, - string webHookId, - string? @event = null, - CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics") - .SetQueryParam("event", @event) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a statistics summary for a webhook. - /// - /// The project key. - /// The repository slug. - /// The webhook ID. - /// Cancellation token. - /// A dictionary of webhook statistics counts. - public async Task> GetProjectRepositoryWebHookStatisticsSummaryAsync(string projectKey, string repositorySlug, - string webHookId, CancellationToken cancellationToken = default) - { - var response = await GetProjectsReposUrl(projectKey, repositorySlug, $"/webhooks/{webHookId}/statistics/summary") - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } -} \ No newline at end of file From 93e9837e1b209c90eb9478ca6250ed299b9d8cbb Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 12:44:49 +0000 Subject: [PATCH 45/50] chore: update CHANGELOG and README for version 0.2.0 release --- CHANGELOG.md | 58 +++++++++++++++----------- README.md | 24 ++++++++--- src/Bitbucket.Net/Bitbucket.Net.csproj | 2 +- 3 files changed, 53 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ab0253c..665cd78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,33 +7,49 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.2.0] - 2026-02-08 + ### Breaking Changes -- `Comment` no longer inherits from `PullRequestInfo`. Properties such as `Title`, `Description`, `FromRef`, `ToRef`, `Locked`, and `Reviewers` are no longer available on `Comment` objects. These properties were always null/default on comments and should not have been exposed. +- **Exception handling**: The library now throws `BitbucketApiException` (and its typed subtypes) instead of `FlurlHttpException`. Consumers catching `FlurlHttpException` must update their catch blocks. +- **`Comment` model**: No longer inherits from `PullRequestInfo`. Properties such as `Title`, `Description`, `FromRef`, `ToRef`, `Locked`, and `Reviewers` are removed from `Comment`. These were always null/default on comments and should not have been exposed. +- **`Comment.State`**: Changed from `new string?` (hiding a `PullRequestStates` enum) to a plain `string?` property. +- **Global Flurl configuration removed**: The library no longer calls `FlurlHttp.Clients.WithDefaults()`. Other Flurl consumers in the same process are no longer affected. + +### Added + +- **SourceLink**: Consumers can step into library source during debugging. +- **Symbol packages**: `.snupkg` published alongside `.nupkg`. +- **XML documentation**: IntelliSense documentation included in the NuGet package. Model classes now have comprehensive `` and `` XML docs. +- **New streaming methods**: + - `GetPullRequestActivitiesStreamAsync` + - `GetPullRequestChangesStreamAsync` + - `GetPullRequestCommentsStreamAsync` + - `GetPullRequestParticipantsStreamAsync` + - `GetPullRequestTasksStreamAsync` + - `GetPullRequestBlockerCommentsStreamAsync` + - `GetDashboardPullRequestsStreamAsync` + - `GetInboxPullRequestsStreamAsync` + - `GetProjectRepositoryTagsStreamAsync` + - `GetChangesStreamAsync` + - `GetCommitChangesStreamAsync` +- **`global.json`**: SDK version pinned for reproducible builds. +- **`Directory.Build.props`**: Centralized build configuration (TFM, language version, nullable, warnings-as-errors). +- **Code coverage**: CI collects and reports test coverage. +- **File splitting**: Monolithic `Core/Projects/BitbucketClient.cs` (4 491 lines) split into 10 focused partial-class files by domain (projects, repositories, branches, commits, compare, pull requests, PR comments, PR details, tasks, repository settings). ### Fixed -- `PullRequest.ToString()` and `Participant.ToString()` now return `"Unknown"` instead of throwing `NullReferenceException` when `Author`/`User` is null. +- **Typed exceptions now fire correctly** for all HTTP error responses. Previously, Flurl intercepted errors before the custom handling could run. +- **`CancellationToken` propagation**: Helper methods now pass the token to underlying HTTP calls. +- **`PullRequest.ToString()`**: No longer throws `NullReferenceException` when `Author` or `Author.User` is null. +- **`Participant.ToString()`**: Same null-safety fix. ### Changed - Removed commented-out `Avatar` property from `ProjectDefinition`. - Fixed duplicate `` XML doc tag on `GetRepositoriesStreamAsync`. -### Added - -- `GetPullRequestActivitiesStreamAsync` — stream PR activities as `IAsyncEnumerable` -- `GetPullRequestChangesStreamAsync` — stream PR changes as `IAsyncEnumerable` -- `GetPullRequestCommentsStreamAsync` — stream PR comments as `IAsyncEnumerable` -- `GetPullRequestParticipantsStreamAsync` — stream PR participants as `IAsyncEnumerable` -- `GetDashboardPullRequestsStreamAsync` — stream dashboard PRs as `IAsyncEnumerable` -- `GetInboxPullRequestsStreamAsync` — stream inbox PRs as `IAsyncEnumerable` -- `GetProjectRepositoryTagsStreamAsync` — stream repository tags as `IAsyncEnumerable` -- `GetPullRequestTasksStreamAsync` — stream PR tasks as `IAsyncEnumerable` (deprecated, pre-9.0) -- `GetPullRequestBlockerCommentsStreamAsync` — stream blocker comments as `IAsyncEnumerable` (9.0+) -- `GetChangesStreamAsync` — stream repository changes between refs as `IAsyncEnumerable` -- `GetCommitChangesStreamAsync` — stream commit changes as `IAsyncEnumerable` - ### Testing - Added streaming endpoint mock tests covering all 20 streaming methods (single-page, multi-page, empty result scenarios) @@ -48,15 +64,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Notes -This is the first public release of the modernized fork by -[diomonogatari](https://github.com/diomonogatari). -The version number intentionally starts at `0.x` to signal that the -library is **not yet production-ready** — it is being dog-fooded -in an MCP Server for on-prem Bitbucket Server but not every endpoint -has been exhaustively tested. - -The original [lvermeulen/Bitbucket.Net](https://github.com/lvermeulen/Bitbucket.Net) -shipped up to 0.5.0 on NuGet; this fork is versioned independently. +First public pre-release of the modernized fork. Superseded by 0.2.0. ## [2.0.0] - 2025-11-28 (internal) diff --git a/README.md b/README.md index d146665..1af2ed3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ [![codecov](https://codecov.io/gh/diomonogatari/Bitbucket.Net/branch/main/graph/badge.svg)](https://codecov.io/gh/diomonogatari/Bitbucket.Net) [![license](https://img.shields.io/github/license/diomonogatari/Bitbucket.Net.svg?maxAge=2592000)](https://github.com/diomonogatari/Bitbucket.Net/blob/main/LICENSE) ![](https://img.shields.io/badge/.net-10.0-yellowgreen.svg) -![](https://img.shields.io/badge/status-beta-orange.svg) +![](https://img.shields.io/badge/status-0.x_(pre--stable)-orange.svg) Modernized C# client for **Bitbucket Server** (Stash) REST API. @@ -18,9 +18,10 @@ Development setup (including the pre-commit formatting hook) is documented in > **Fork notice** — This is an actively maintained fork of > [lvermeulen/Bitbucket.Net](https://github.com/lvermeulen/Bitbucket.Net), > which appears to be abandoned (last release 2020). -> The fork is **not production-ready** yet — it works well for the -> author's own use case (an MCP Server for on-prem Bitbucket Server) but -> not every endpoint has been fully tested. +> The library is at **0.x** — the API surface may still change +> between minor versions. It is used in production by the author (as the +> backend for an MCP Server talking to on-prem Bitbucket Server), but +> not every endpoint has been verified against a live instance. > Contributions, bug reports, and feedback are very welcome. ### What changed from the original @@ -40,7 +41,7 @@ If you're looking for Bitbucket Cloud API, try [this repository](https://github. ## Installation ```bash -dotnet add package BitbucketServer.Net --prerelease +dotnet add package BitbucketServer.Net ``` ## Usage @@ -117,6 +118,19 @@ await foreach (var pr in client.GetPullRequestsStreamAsync("PROJ", "repo", cance { await ProcessPullRequestAsync(pr); } + +// Stream PR activities +await foreach (var activity in client.GetPullRequestActivitiesStreamAsync( + "PROJ", "repo", pullRequestId: 42)) +{ + ProcessActivity(activity); +} + +// Stream dashboard PRs +await foreach (var pr in client.GetDashboardPullRequestsStreamAsync()) +{ + Console.WriteLine($"#{pr.Id}: {pr.Title}"); +} ``` ### Exception Handling diff --git a/src/Bitbucket.Net/Bitbucket.Net.csproj b/src/Bitbucket.Net/Bitbucket.Net.csproj index 758233f..d40f0a9 100644 --- a/src/Bitbucket.Net/Bitbucket.Net.csproj +++ b/src/Bitbucket.Net/Bitbucket.Net.csproj @@ -2,7 +2,7 @@ BitbucketServer.Net - 0.1.0-beta.1 + 0.2.0 Diogo Carvalho Modernized fork of Bitbucket.Net — C# client for Bitbucket Server (Stash) REST API. Adds streaming, CancellationToken support, System.Text.Json, typed exceptions, and Bitbucket Server 9.0+ compatibility. bitbucket;bitbucket-server;stash;api;client;rest From 5b10bdafaa0fb202b849d8429d791e72005ccbac Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 14:35:58 +0000 Subject: [PATCH 46/50] refactor: update Unix timestamp handling to use milliseconds and adjust related converters and tests --- .../Converters/UnixDateTimeOffsetConverter.cs | 18 ++++++----- .../Common/DynamicMultipartFormDataContent.cs | 2 +- .../Common/UnixDateTimeExtensions.cs | 8 ++--- .../Models/Core/Admin/LicenseDetails.cs | 10 +++--- .../Models/Core/Projects/BlockerComment.cs | 6 ++-- .../Models/Core/Projects/Comment.cs | 6 ++-- .../Models/Core/Projects/CommentRef.cs | 4 +-- .../Models/Core/Projects/PullRequest.cs | 4 +-- .../Core/Projects/PullRequestActivity.cs | 2 +- .../Models/Core/Tasks/TaskAnchor.cs | 2 +- .../Models/Core/Tasks/TaskRef.cs | 2 +- .../UnitTests/CommonModelTests.cs | 31 ++++++++++++------- 12 files changed, 52 insertions(+), 43 deletions(-) diff --git a/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs b/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs index 1e7eb28..01c1759 100644 --- a/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs @@ -4,7 +4,8 @@ namespace Bitbucket.Net.Common.Converters; /// -/// Converts Unix timestamps (seconds since epoch) to/from DateTimeOffset. +/// Converts Unix timestamps (milliseconds since epoch) to/from DateTimeOffset. +/// Bitbucket Server returns all timestamps as epoch milliseconds. /// public sealed class UnixDateTimeOffsetConverter : JsonConverter { @@ -13,20 +14,21 @@ public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConver return reader.TokenType switch { JsonTokenType.Null => default, - JsonTokenType.Number when reader.TryGetInt64(out long unixTime) => unixTime.FromUnixTimeSeconds(), - JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) => unixTime.FromUnixTimeSeconds(), + JsonTokenType.Number when reader.TryGetInt64(out long unixTime) => unixTime.FromUnixTimeMilliseconds(), + JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) => unixTime.FromUnixTimeMilliseconds(), _ => throw new JsonException($"Cannot convert {reader.TokenType} to {nameof(DateTimeOffset)}."), }; } public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) { - writer.WriteNumberValue(value.ToUnixTimeSeconds()); + writer.WriteNumberValue(value.ToUnixTimeMilliseconds()); } } /// -/// Converts Unix timestamps (seconds since epoch) to/from nullable DateTimeOffset. +/// Converts Unix timestamps (milliseconds since epoch) to/from nullable DateTimeOffset. +/// Bitbucket Server returns all timestamps as epoch milliseconds. /// public sealed class NullableUnixDateTimeOffsetConverter : JsonConverter { @@ -35,9 +37,9 @@ public sealed class NullableUnixDateTimeOffsetConverter : JsonConverter null, - JsonTokenType.Number when reader.TryGetInt64(out long unixTime) => unixTime.FromUnixTimeSeconds(), + JsonTokenType.Number when reader.TryGetInt64(out long unixTime) => unixTime.FromUnixTimeMilliseconds(), JsonTokenType.String when string.IsNullOrEmpty(reader.GetString()) => null, - JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) => unixTime.FromUnixTimeSeconds(), + JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) => unixTime.FromUnixTimeMilliseconds(), _ => throw new JsonException($"Cannot convert {reader.TokenType} to nullable {nameof(DateTimeOffset)}."), }; } @@ -46,7 +48,7 @@ public override void Write(Utf8JsonWriter writer, DateTimeOffset? value, JsonSer { if (value.HasValue) { - writer.WriteNumberValue(value.Value.ToUnixTimeSeconds()); + writer.WriteNumberValue(value.Value.ToUnixTimeMilliseconds()); } else { diff --git a/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs b/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs index 3080606..74bc25c 100644 --- a/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs +++ b/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs @@ -4,7 +4,7 @@ namespace Bitbucket.Net.Common; public class DynamicMultipartFormDataContent : IEnumerable { - private readonly MultipartFormDataContent _multipartFormDataContent = []; + private readonly MultipartFormDataContent _multipartFormDataContent = new MultipartFormDataContent(); public void Add(HttpContent value, string key) { diff --git a/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs b/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs index 0cdc1a6..fce2dfe 100644 --- a/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs +++ b/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs @@ -2,14 +2,14 @@ public static class UnixDateTimeExtensions { - public static DateTimeOffset FromUnixTimeSeconds(this long value) + public static DateTimeOffset FromUnixTimeMilliseconds(this long value) { - return DateTimeOffset.UnixEpoch.AddMilliseconds(value) + return DateTimeOffset.FromUnixTimeMilliseconds(value) .ToLocalTime(); } - public static long ToUnixTimeSeconds(this DateTimeOffset dateTimeOffset) + public static long ToUnixTimeMilliseconds(this DateTimeOffset dateTimeOffset) { - return dateTimeOffset.Subtract(DateTimeOffset.UnixEpoch).Ticks; + return dateTimeOffset.ToUnixTimeMilliseconds(); } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs b/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs index 8b4f906..d50ee1f 100644 --- a/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs +++ b/src/Bitbucket.Net/Models/Core/Admin/LicenseDetails.cs @@ -5,17 +5,17 @@ namespace Bitbucket.Net.Models.Core.Admin; public class LicenseDetails : LicenseInfo { - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? CreationDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? PurchaseDate { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? ExpiryDate { get; set; } public int NumberOfDaysBeforeExpiry { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? MaintenanceExpiryDate { get; set; } public int NumberOfDaysBeforeMaintenanceExpiry { get; set; } - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? GracePeriodEndDate { get; set; } public int NumberOfDaysBeforeGracePeriodExpiry { get; set; } public int MaximumNumberOfUsers { get; set; } diff --git a/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs b/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs index 8ac0612..3fca691 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/BlockerComment.cs @@ -45,13 +45,13 @@ public class BlockerComment /// /// When the blocker comment was created. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } /// /// When the blocker comment was last updated. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? UpdatedDate { get; set; } /// @@ -74,7 +74,7 @@ public class BlockerComment /// /// When the blocker comment was resolved. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? ResolvedDate { get; set; } /// diff --git a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs index b2dd738..8b24dba 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Comment.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Comment.cs @@ -45,19 +45,19 @@ public class Comment /// /// When the comment thread was resolved (when resolved). /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? ResolvedDate { get; set; } /// /// Gets or sets the date and time when the comment was created. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } /// /// Gets or sets the date and time when the comment was last updated. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? UpdatedDate { get; set; } /// diff --git a/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs b/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs index ecd4c13..826fbb5 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/CommentRef.cs @@ -38,13 +38,13 @@ public class CommentRef /// /// Gets or sets the date and time when the comment was created. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } /// /// Gets or sets the date and time when the comment was last updated. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? UpdatedDate { get; set; } /// diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs index 33f9933..922cc44 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs @@ -21,13 +21,13 @@ public class PullRequest : PullRequestInfo /// /// Gets or sets the date and time when the pull request was created. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } /// /// Gets or sets the date and time when the pull request was last updated. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? UpdatedDate { get; set; } /// diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs index f60301b..a378678 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequestActivity.cs @@ -17,7 +17,7 @@ public class PullRequestActivity /// /// Gets or sets the date and time when the activity occurred. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } /// diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs index ae3b006..2dae176 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskAnchor.cs @@ -17,7 +17,7 @@ public class TaskAnchor : TaskRef /// /// Gets or sets the date and time when the anchor was last updated. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? UpdatedDate { get; set; } /// diff --git a/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs b/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs index 9306753..a7ab03e 100644 --- a/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs +++ b/src/Bitbucket.Net/Models/Core/Tasks/TaskRef.cs @@ -33,7 +33,7 @@ public abstract class TaskRef /// /// Gets or sets the date and time when the task was created. /// - [JsonConverter(typeof(UnixDateTimeOffsetConverter))] + [JsonConverter(typeof(NullableUnixDateTimeOffsetConverter))] public DateTimeOffset? CreatedDate { get; set; } /// diff --git a/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs b/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs index de706e5..a4552f0 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs @@ -253,45 +253,52 @@ public void IsNullableType_ListOfInt_ReturnsFalse() #region UnixDateTimeExtensions Tests [Fact] - public void FromUnixTimeSeconds_ZeroReturnsEpoch() + public void FromUnixTimeMilliseconds_ZeroReturnsEpoch() { long timestamp = 0; - var result = timestamp.FromUnixTimeSeconds(); + var result = timestamp.FromUnixTimeMilliseconds(); - // The method uses AddMilliseconds, so 0 should give us the epoch converted to local time var expected = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).ToLocalTime(); Assert.Equal(expected, result); } [Fact] - public void FromUnixTimeSeconds_KnownTimestamp_ReturnsCorrectDate() + public void FromUnixTimeMilliseconds_KnownTimestamp_ReturnsCorrectDate() { // 1609459200000 milliseconds = Jan 1, 2021 00:00:00 UTC long timestamp = 1609459200000; - var result = timestamp.FromUnixTimeSeconds(); + var result = timestamp.FromUnixTimeMilliseconds(); var expected = new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero).ToLocalTime(); Assert.Equal(expected, result); } [Fact] - public void ToUnixTimeSeconds_Epoch_ReturnsZero() + public void ToUnixTimeMilliseconds_Epoch_ReturnsZero() { var epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); - var result = epoch.ToUnixTimeSeconds(); + var result = epoch.ToUnixTimeMilliseconds(); Assert.Equal(0, result); } [Fact] - public void ToUnixTimeSeconds_KnownDate_ReturnsCorrectValue() + public void ToUnixTimeMilliseconds_KnownDate_ReturnsCorrectValue() { var dateTime = new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero); - var result = dateTime.ToUnixTimeSeconds(); + var result = dateTime.ToUnixTimeMilliseconds(); - // Note: The method name says "Seconds" but implementation returns Ticks - // This test verifies the actual behavior - Assert.True(result > 0); + Assert.Equal(1609459200000, result); + } + + [Fact] + public void UnixTimeMilliseconds_RoundTrip() + { + var original = new DateTimeOffset(2025, 6, 15, 12, 30, 45, 123, TimeSpan.Zero); + var milliseconds = original.ToUnixTimeMilliseconds(); + var restored = milliseconds.FromUnixTimeMilliseconds(); + + Assert.Equal(original.ToLocalTime(), restored); } #endregion From 8c48bee6b6e54e6037ff50a1bba127c35b6a9b5e Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 14:52:43 +0000 Subject: [PATCH 47/50] refactor: improve UnixDateTimeExtensions and related tests for consistency in DateTimeOffset handling --- .../Common/UnixDateTimeExtensions.cs | 17 +++++++++++++++-- .../UnitTests/CommonModelTests.cs | 6 +++--- .../UnitTests/JsonConverterTests.cs | 11 +++++------ 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs b/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs index fce2dfe..b69a343 100644 --- a/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs +++ b/src/Bitbucket.Net/Common/UnixDateTimeExtensions.cs @@ -1,13 +1,26 @@ namespace Bitbucket.Net.Common; +/// +/// Extension methods for converting between Unix epoch milliseconds and . +/// Bitbucket Server represents all timestamps as milliseconds since the Unix epoch (1970-01-01T00:00:00Z). +/// public static class UnixDateTimeExtensions { + /// + /// Converts a Unix epoch millisecond timestamp to a UTC . + /// + /// The number of milliseconds since 1970-01-01T00:00:00Z. + /// A in UTC representing the given timestamp. public static DateTimeOffset FromUnixTimeMilliseconds(this long value) { - return DateTimeOffset.FromUnixTimeMilliseconds(value) - .ToLocalTime(); + return DateTimeOffset.FromUnixTimeMilliseconds(value); } + /// + /// Converts a to Unix epoch milliseconds. + /// + /// The date and time to convert. + /// The number of milliseconds since 1970-01-01T00:00:00Z. public static long ToUnixTimeMilliseconds(this DateTimeOffset dateTimeOffset) { return dateTimeOffset.ToUnixTimeMilliseconds(); diff --git a/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs b/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs index a4552f0..fab23eb 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/CommonModelTests.cs @@ -258,7 +258,7 @@ public void FromUnixTimeMilliseconds_ZeroReturnsEpoch() long timestamp = 0; var result = timestamp.FromUnixTimeMilliseconds(); - var expected = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero).ToLocalTime(); + var expected = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); Assert.Equal(expected, result); } @@ -269,7 +269,7 @@ public void FromUnixTimeMilliseconds_KnownTimestamp_ReturnsCorrectDate() long timestamp = 1609459200000; var result = timestamp.FromUnixTimeMilliseconds(); - var expected = new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero).ToLocalTime(); + var expected = new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero); Assert.Equal(expected, result); } @@ -298,7 +298,7 @@ public void UnixTimeMilliseconds_RoundTrip() var milliseconds = original.ToUnixTimeMilliseconds(); var restored = milliseconds.FromUnixTimeMilliseconds(); - Assert.Equal(original.ToLocalTime(), restored); + Assert.Equal(original, restored); } #endregion diff --git a/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs b/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs index f9afa7e..748a9f5 100644 --- a/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs +++ b/test/Bitbucket.Net.Tests/UnitTests/JsonConverterTests.cs @@ -31,11 +31,10 @@ public class JsonConverterTests [Fact] public void UnixDateTimeOffsetConverter_Read_FromNumber_ReturnsCorrectValue() { - // The converter uses milliseconds internally (despite the "seconds" naming) + // The converter uses milliseconds internally var json = "1609459200000"; // 2021-01-01 00:00:00 UTC in milliseconds var result = JsonSerializer.Deserialize(json, s_options); - // The result will be in local time - Assert.Equal(new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero).ToLocalTime(), result); + Assert.Equal(new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero), result); } [Fact] @@ -43,7 +42,7 @@ public void UnixDateTimeOffsetConverter_Read_FromString_ReturnsCorrectValue() { var json = "\"1609459200000\""; // 2021-01-01 00:00:00 UTC in milliseconds var result = JsonSerializer.Deserialize(json, s_options); - Assert.Equal(new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero).ToLocalTime(), result); + Assert.Equal(new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero), result); } [Fact] @@ -73,7 +72,7 @@ public void NullableUnixDateTimeOffsetConverter_Read_FromNumber_ReturnsCorrectVa { var json = "1609459200000"; // 2021-01-01 00:00:00 UTC in milliseconds var result = JsonSerializer.Deserialize(json, s_options); - Assert.Equal(new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero).ToLocalTime(), result); + Assert.Equal(new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero), result); } [Fact] @@ -81,7 +80,7 @@ public void NullableUnixDateTimeOffsetConverter_Read_FromString_ReturnsCorrectVa { var json = "\"1609459200000\""; // 2021-01-01 00:00:00 UTC in milliseconds var result = JsonSerializer.Deserialize(json, s_options); - Assert.Equal(new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero).ToLocalTime(), result); + Assert.Equal(new DateTimeOffset(2021, 1, 1, 0, 0, 0, TimeSpan.Zero), result); } [Fact] From 24029a9b7bd56d46ef514e05efb3b278c6b2c459 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:17:19 +0000 Subject: [PATCH 48/50] feat: Add XML documentation comments to converters and models --- src/Bitbucket.Net/Common/BitbucketHelpers.cs | 305 ++++++++++++++++++ .../Common/Converters/FileTypesConverter.cs | 5 + .../Common/Converters/HookTypesConverter.cs | 5 + .../Common/Converters/JsonEnumConverter.cs | 4 + .../Common/Converters/LineTypesConverter.cs | 5 + .../Converters/ParticipantStatusConverter.cs | 5 + .../Common/Converters/PermissionsConverter.cs | 10 + .../Converters/PullRequestStatesConverter.cs | 5 + .../RefRestrictionTypesConverter.cs | 5 + .../Common/Converters/RolesConverter.cs | 5 + .../Common/Converters/ScopeTypesConverter.cs | 5 + .../Converters/SynchronizeActionsConverter.cs | 5 + .../Converters/UnixDateTimeOffsetConverter.cs | 4 + .../Converters/WebHookOutcomesConverter.cs | 5 + .../Common/DynamicMultipartFormDataContent.cs | 21 ++ .../Common/FlurlRequestExtensions.cs | 17 + .../Common/Models/PagedResults.cs | 14 + src/Bitbucket.Net/Common/TypeExtensions.cs | 8 + .../Models/Core/Projects/Branch.cs | 3 + .../Models/Core/Projects/Link.cs | 3 + .../Models/Core/Projects/Participant.cs | 3 + .../Models/Core/Projects/Project.cs | 3 + .../Models/Core/Projects/PullRequest.cs | 3 + .../Models/Core/Projects/Repository.cs | 3 + src/Bitbucket.Net/Models/Core/Users/User.cs | 3 + 25 files changed, 454 insertions(+) diff --git a/src/Bitbucket.Net/Common/BitbucketHelpers.cs b/src/Bitbucket.Net/Common/BitbucketHelpers.cs index 3302cc6..e6e3e9b 100644 --- a/src/Bitbucket.Net/Common/BitbucketHelpers.cs +++ b/src/Bitbucket.Net/Common/BitbucketHelpers.cs @@ -7,18 +7,36 @@ namespace Bitbucket.Net.Common; +/// +/// Helper methods for converting between Bitbucket enum values and their wire-format string representations. +/// public static class BitbucketHelpers { #region Bool + /// + /// Converts a boolean value to the lowercase string expected by Bitbucket query parameters. + /// + /// The boolean value to convert. + /// "true" or "false". public static string BoolToString(bool value) => value ? "true" : "false"; + /// + /// Converts an optional boolean value to the lowercase string expected by Bitbucket query parameters. + /// + /// The optional boolean value to convert. + /// "true", "false", or when no value is supplied. public static string? BoolToString(bool? value) => value.HasValue ? BoolToString(value.Value) : null; + /// + /// Parses a case-insensitive boolean string returned by the Bitbucket API. + /// + /// The string to parse. + /// when the value is "true"; otherwise . public static bool StringToBool(string value) => value.Equals("true", StringComparison.OrdinalIgnoreCase); #endregion @@ -31,6 +49,12 @@ public static string BoolToString(bool value) => value [BranchOrderBy.Modification] = "MODIFICATION", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The ordering to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string BranchOrderByToString(BranchOrderBy orderBy) { if (!s_stringByBranchOrderBy.TryGetValue(orderBy, out string? result)) @@ -51,6 +75,12 @@ public static string BranchOrderByToString(BranchOrderBy orderBy) [PullRequestDirections.Outgoing] = "OUTGOING", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The direction to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string PullRequestDirectionToString(PullRequestDirections direction) { if (!s_stringByPullRequestDirection.TryGetValue(direction, out string? result)) @@ -73,6 +103,12 @@ public static string PullRequestDirectionToString(PullRequestDirections directio [PullRequestStates.All] = "ALL", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The state to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string PullRequestStateToString(PullRequestStates state) { if (!s_stringByPullRequestState.TryGetValue(state, out string? result)) @@ -83,10 +119,21 @@ public static string PullRequestStateToString(PullRequestStates state) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The state to convert. + /// The API string representation or when no state is provided. public static string? PullRequestStateToString(PullRequestStates? state) => state.HasValue ? PullRequestStateToString(state.Value) : null; + /// + /// Parses a Bitbucket pull request state string into a value. + /// + /// The string returned by the API. + /// The parsed state. + /// Thrown when the value is not recognized. public static PullRequestStates StringToPullRequestState(string s) { var pair = s_stringByPullRequestState.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -109,6 +156,12 @@ public static PullRequestStates StringToPullRequestState(string s) [PullRequestOrders.Oldest] = "OLDEST", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The order to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string PullRequestOrderToString(PullRequestOrders order) { if (!s_stringByPullRequestOrder.TryGetValue(order, out string? result)) @@ -119,6 +172,11 @@ public static string PullRequestOrderToString(PullRequestOrders order) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The order to convert. + /// The API string representation or when no order is provided. public static string? PullRequestOrderToString(PullRequestOrders? order) => order.HasValue ? PullRequestOrderToString(order.Value) : null; @@ -143,6 +201,11 @@ private static string PullRequestFromTypeToString(PullRequestFromTypes fromType) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The source type to convert. + /// The API string representation or when no source is provided. public static string? PullRequestFromTypeToString(PullRequestFromTypes? fromType) => fromType.HasValue ? PullRequestFromTypeToString(fromType.Value) : null; @@ -166,6 +229,12 @@ private static string PullRequestFromTypeToString(PullRequestFromTypes fromType) [Permissions.SysAdmin] = "SYS_ADMIN", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The permission to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string PermissionToString(Permissions permission) { if (!s_stringByPermissions.TryGetValue(permission, out string? result)) @@ -176,10 +245,21 @@ public static string PermissionToString(Permissions permission) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The permission to convert. + /// The API string representation or when not supplied. public static string? PermissionToString(Permissions? permission) => permission.HasValue ? PermissionToString(permission.Value) : null; + /// + /// Parses a Bitbucket permission string into a value. + /// + /// The string returned by the API. + /// The parsed permission. + /// Thrown when the value is not recognized. public static Permissions StringToPermission(string s) { var pair = s_stringByPermissions.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -203,6 +283,12 @@ public static Permissions StringToPermission(string s) [MergeCommits.Only] = "only", }; + /// + /// Converts a preference to the Bitbucket API string. + /// + /// The merge commit option. + /// The API string representation. + /// Thrown when the value is not recognized. public static string MergeCommitsToString(MergeCommits mergeCommits) { if (!s_stringByMergeCommits.TryGetValue(mergeCommits, out string? result)) @@ -224,6 +310,12 @@ public static string MergeCommitsToString(MergeCommits mergeCommits) [Roles.Participant] = "PARTICIPANT", }; + /// + /// Converts a pull request value to the Bitbucket API string. + /// + /// The role to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string RoleToString(Roles role) { if (!s_stringByRoles.TryGetValue(role, out string? result)) @@ -234,10 +326,21 @@ public static string RoleToString(Roles role) return result; } + /// + /// Converts an optional pull request value to the Bitbucket API string. + /// + /// The role to convert. + /// The API string representation or when not supplied. public static string? RoleToString(Roles? role) => role.HasValue ? RoleToString(role.Value) : null; + /// + /// Parses a pull request role string into a value. + /// + /// The string returned by the API. + /// The parsed role. + /// Thrown when the value is not recognized. public static Roles StringToRole(string s) { var pair = s_stringByRoles.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -261,6 +364,12 @@ public static Roles StringToRole(string s) [LineTypes.Context] = "CONTEXT", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The line type to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string LineTypeToString(LineTypes lineType) { if (!s_stringByLineTypes.TryGetValue(lineType, out string? result)) @@ -271,6 +380,11 @@ public static string LineTypeToString(LineTypes lineType) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The line type to convert. + /// The API string representation or when not supplied. public static string? LineTypeToString(LineTypes? lineType) { return lineType.HasValue @@ -278,6 +392,12 @@ public static string LineTypeToString(LineTypes lineType) : null; } + /// + /// Parses a line type string into a value. + /// + /// The string returned by the API. + /// The parsed line type. + /// Thrown when the value is not recognized. public static LineTypes StringToLineType(string s) { var pair = s_stringByLineTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -300,6 +420,12 @@ public static LineTypes StringToLineType(string s) [FileTypes.To] = "TO", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The file type to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string FileTypeToString(FileTypes fileType) { if (!s_stringByFileTypes.TryGetValue(fileType, out string? result)) @@ -310,6 +436,11 @@ public static string FileTypeToString(FileTypes fileType) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The file type to convert. + /// The API string representation or when not supplied. public static string? FileTypeToString(FileTypes? fileType) { return fileType.HasValue @@ -317,6 +448,12 @@ public static string FileTypeToString(FileTypes fileType) : null; } + /// + /// Parses a file type string into a value. + /// + /// The string returned by the API. + /// The parsed file type. + /// Thrown when the value is not recognized. public static FileTypes StringToFileType(string s) { var pair = s_stringByFileTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -340,6 +477,12 @@ public static FileTypes StringToFileType(string s) [ChangeScopes.Range] = "RANGE", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The change scope to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string ChangeScopeToString(ChangeScopes changeScope) { if (!s_stringByChangeScopes.TryGetValue(changeScope, out string? result)) @@ -363,6 +506,12 @@ public static string ChangeScopeToString(ChangeScopes changeScope) [LogLevels.Error] = "ERROR", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The log level to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string LogLevelToString(LogLevels logLevel) { if (!s_stringByLogLevels.TryGetValue(logLevel, out string? result)) @@ -373,6 +522,12 @@ public static string LogLevelToString(LogLevels logLevel) return result; } + /// + /// Parses a log level string into a value. + /// + /// The string returned by the API. + /// The parsed log level. + /// Thrown when the value is not recognized. public static LogLevels StringToLogLevel(string s) { var pair = s_stringByLogLevels.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -396,6 +551,12 @@ public static LogLevels StringToLogLevel(string s) [ParticipantStatus.Unapproved] = "UNAPPROVED", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The status to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string ParticipantStatusToString(ParticipantStatus participantStatus) { if (!s_stringByParticipantStatus.TryGetValue(participantStatus, out string? result)) @@ -406,6 +567,12 @@ public static string ParticipantStatusToString(ParticipantStatus participantStat return result; } + /// + /// Parses a participant status string into a value. + /// + /// The string returned by the API. + /// The parsed status. + /// Thrown when the value is not recognized. public static ParticipantStatus StringToParticipantStatus(string s) { var pair = s_stringByParticipantStatus.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -429,6 +596,12 @@ public static ParticipantStatus StringToParticipantStatus(string s) [HookTypes.PrePullRequestMerge] = "PRE_PULL_REQUEST_MERGE", }; + /// + /// Converts a hook value to the Bitbucket API string. + /// + /// The hook type to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string HookTypeToString(HookTypes hookType) { if (!s_stringByHookTypes.TryGetValue(hookType, out string? result)) @@ -439,6 +612,12 @@ public static string HookTypeToString(HookTypes hookType) return result; } + /// + /// Parses a hook type string into a value. + /// + /// The string returned by the API. + /// The parsed hook type. + /// Thrown when the value is not recognized. public static HookTypes StringToHookType(string s) { var pair = s_stringByHookTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -461,6 +640,12 @@ public static HookTypes StringToHookType(string s) [ScopeTypes.Repository] = "REPOSITORY", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The scope type to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string ScopeTypeToString(ScopeTypes scopeType) { if (!s_stringByScopeTypes.TryGetValue(scopeType, out string? result)) @@ -471,6 +656,12 @@ public static string ScopeTypeToString(ScopeTypes scopeType) return result; } + /// + /// Parses a scope type string into a value. + /// + /// The string returned by the API. + /// The parsed scope type. + /// Thrown when the value is not recognized. public static ScopeTypes StringToScopeType(string s) { var pair = s_stringByScopeTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -495,6 +686,12 @@ public static ScopeTypes StringToScopeType(string s) [ArchiveFormats.Tgz] = "tgz", }; + /// + /// Converts an value to the Bitbucket API string. + /// + /// The archive format to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string ArchiveFormatToString(ArchiveFormats archiveFormat) { if (!s_stringByArchiveFormats.TryGetValue(archiveFormat, out string? result)) @@ -516,6 +713,12 @@ public static string ArchiveFormatToString(ArchiveFormats archiveFormat) [WebHookOutcomes.Error] = "ERROR", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The outcome to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string WebHookOutcomeToString(WebHookOutcomes webHookOutcome) { if (!s_stringByWebHookOutcomes.TryGetValue(webHookOutcome, out string? result)) @@ -526,10 +729,21 @@ public static string WebHookOutcomeToString(WebHookOutcomes webHookOutcome) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The outcome to convert. + /// The API string representation or when not supplied. public static string? WebHookOutcomeToString(WebHookOutcomes? webHookOutcome) => webHookOutcome.HasValue ? WebHookOutcomeToString(webHookOutcome.Value) : null; + /// + /// Parses a webhook outcome string into a value. + /// + /// The string returned by the API. + /// The parsed outcome. + /// Thrown when the value is not recognized. public static WebHookOutcomes StringToWebHookOutcome(string s) { var pair = s_stringByWebHookOutcomes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -553,6 +767,12 @@ public static WebHookOutcomes StringToWebHookOutcome(string s) [AnchorStates.All] = "ALL", }; + /// + /// Converts an value to the Bitbucket API string. + /// + /// The anchor state to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string AnchorStateToString(AnchorStates anchorState) { if (!s_stringByAnchorStates.TryGetValue(anchorState, out string? result)) @@ -574,6 +794,12 @@ public static string AnchorStateToString(AnchorStates anchorState) [DiffTypes.Commit] = "COMMIT", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The diff type to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string DiffTypeToString(DiffTypes diffType) { if (!s_stringByDiffTypes.TryGetValue(diffType, out string? result)) @@ -584,6 +810,11 @@ public static string DiffTypeToString(DiffTypes diffType) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The diff type to convert. + /// The API string representation or when not supplied. public static string? DiffTypeToString(DiffTypes? diffType) { return diffType.HasValue @@ -601,6 +832,12 @@ public static string DiffTypeToString(DiffTypes diffType) [TagTypes.Annotated] = "ANNOTATED", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The tag type to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string TagTypeToString(TagTypes tagType) { if (!s_stringByTagTypes.TryGetValue(tagType, out string? result)) @@ -623,6 +860,12 @@ public static string TagTypeToString(TagTypes tagType) [RefRestrictionTypes.ChangesWithoutPullRequest] = "pull-request-only", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The restriction to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string RefRestrictionTypeToString(RefRestrictionTypes refRestrictionType) { if (!s_stringByRefRestrictionTypes.TryGetValue(refRestrictionType, out string? result)) @@ -633,6 +876,11 @@ public static string RefRestrictionTypeToString(RefRestrictionTypes refRestricti return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The restriction to convert. + /// The API string representation or when not supplied. public static string? RefRestrictionTypeToString(RefRestrictionTypes? refRestrictionType) { return refRestrictionType.HasValue @@ -640,6 +888,12 @@ public static string RefRestrictionTypeToString(RefRestrictionTypes refRestricti : null; } + /// + /// Parses a ref restriction string into a value. + /// + /// The string returned by the API. + /// The parsed restriction. + /// Thrown when the value is not recognized. public static RefRestrictionTypes StringToRefRestrictionType(string s) { var pair = s_stringByRefRestrictionTypes.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -674,6 +928,11 @@ private static string RefMatcherTypeToString(RefMatcherTypes refMatcherType) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The matcher type to convert. + /// The API string representation or when not supplied. public static string? RefMatcherTypeToString(RefMatcherTypes? refMatcherType) { return refMatcherType.HasValue @@ -691,6 +950,12 @@ private static string RefMatcherTypeToString(RefMatcherTypes refMatcherType) [SynchronizeActions.Discard] = "DISCARD", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The synchronization action to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string SynchronizeActionToString(SynchronizeActions synchronizeAction) { if (!s_stringBySynchronizeActions.TryGetValue(synchronizeAction, out string? result)) @@ -701,6 +966,12 @@ public static string SynchronizeActionToString(SynchronizeActions synchronizeAct return result; } + /// + /// Parses a synchronization action string into a value. + /// + /// The string returned by the API. + /// The parsed action. + /// Thrown when the value is not recognized. public static SynchronizeActions StringToSynchronizeAction(string s) { var pair = s_stringBySynchronizeActions.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -723,6 +994,12 @@ public static SynchronizeActions StringToSynchronizeAction(string s) [BlockerCommentState.Resolved] = "RESOLVED", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The blocker comment state to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string BlockerCommentStateToString(BlockerCommentState state) { if (!s_stringByBlockerCommentState.TryGetValue(state, out string? result)) @@ -733,10 +1010,21 @@ public static string BlockerCommentStateToString(BlockerCommentState state) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The blocker comment state to convert. + /// The API string representation or when not supplied. public static string? BlockerCommentStateToString(BlockerCommentState? state) => state.HasValue ? BlockerCommentStateToString(state.Value) : null; + /// + /// Parses a blocker comment state string into a value. + /// + /// The string returned by the API. + /// The parsed state. + /// Thrown when the value is not recognized. public static BlockerCommentState StringToBlockerCommentState(string s) { var pair = s_stringByBlockerCommentState.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); @@ -759,6 +1047,12 @@ public static BlockerCommentState StringToBlockerCommentState(string s) [CommentSeverity.Blocker] = "BLOCKER", }; + /// + /// Converts a value to the Bitbucket API string. + /// + /// The comment severity to convert. + /// The API string representation. + /// Thrown when the value is not recognized. public static string CommentSeverityToString(CommentSeverity severity) { if (!s_stringByCommentSeverity.TryGetValue(severity, out string? result)) @@ -769,10 +1063,21 @@ public static string CommentSeverityToString(CommentSeverity severity) return result; } + /// + /// Converts an optional value to the Bitbucket API string. + /// + /// The comment severity to convert. + /// The API string representation or when not supplied. public static string? CommentSeverityToString(CommentSeverity? severity) => severity.HasValue ? CommentSeverityToString(severity.Value) : null; + /// + /// Parses a comment severity string into a value. + /// + /// The string returned by the API. + /// The parsed severity. + /// Thrown when the value is not recognized. public static CommentSeverity StringToCommentSeverity(string s) { var pair = s_stringByCommentSeverity.FirstOrDefault(kvp => kvp.Value.Equals(s, StringComparison.OrdinalIgnoreCase)); diff --git a/src/Bitbucket.Net/Common/Converters/FileTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/FileTypesConverter.cs index 5e297bb..0eaec6b 100644 --- a/src/Bitbucket.Net/Common/Converters/FileTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/FileTypesConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for Bitbucket values. +/// public class FileTypesConverter : JsonEnumConverter { + /// protected override string ConvertToString(FileTypes value) { return BitbucketHelpers.FileTypeToString(value); } + /// protected override FileTypes ConvertFromString(string s) { return BitbucketHelpers.StringToFileType(s); diff --git a/src/Bitbucket.Net/Common/Converters/HookTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/HookTypesConverter.cs index a6bd45e..44d41f0 100644 --- a/src/Bitbucket.Net/Common/Converters/HookTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/HookTypesConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for Bitbucket hook type values. +/// public class HookTypesConverter : JsonEnumConverter { + /// protected override string ConvertToString(HookTypes value) { return BitbucketHelpers.HookTypeToString(value); } + /// protected override HookTypes ConvertFromString(string s) { return BitbucketHelpers.StringToHookType(s); diff --git a/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs b/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs index a5059a9..cc8ec6b 100644 --- a/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/JsonEnumConverter.cs @@ -20,6 +20,7 @@ public abstract class JsonEnumConverter : JsonConverter /// protected abstract TEnum ConvertFromString(string s); + /// public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) @@ -36,6 +37,7 @@ public override TEnum Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSe throw new JsonException($"Unexpected token {reader.TokenType} when parsing enum {typeof(TEnum).Name}."); } + /// public override void Write(Utf8JsonWriter writer, TEnum value, JsonSerializerOptions options) { writer.WriteStringValue(ConvertToString(value)); @@ -59,6 +61,7 @@ public abstract class JsonEnumListConverter : JsonConverter?> /// protected abstract TEnum ConvertFromString(string s); + /// public override List? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) @@ -88,6 +91,7 @@ public abstract class JsonEnumListConverter : JsonConverter?> throw new JsonException("Unexpected end of JSON while reading array."); } + /// public override void Write(Utf8JsonWriter writer, List? value, JsonSerializerOptions options) { if (value is null) diff --git a/src/Bitbucket.Net/Common/Converters/LineTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/LineTypesConverter.cs index 90dd770..f1bcf51 100644 --- a/src/Bitbucket.Net/Common/Converters/LineTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/LineTypesConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for Bitbucket line classification values. +/// public class LineTypesConverter : JsonEnumConverter { + /// protected override string ConvertToString(LineTypes value) { return BitbucketHelpers.LineTypeToString(value); } + /// protected override LineTypes ConvertFromString(string s) { return BitbucketHelpers.StringToLineType(s); diff --git a/src/Bitbucket.Net/Common/Converters/ParticipantStatusConverter.cs b/src/Bitbucket.Net/Common/Converters/ParticipantStatusConverter.cs index b0d4f7f..f2d1b20 100644 --- a/src/Bitbucket.Net/Common/Converters/ParticipantStatusConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/ParticipantStatusConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for Bitbucket participant status values. +/// public class ParticipantStatusConverter : JsonEnumConverter { + /// protected override string ConvertToString(ParticipantStatus value) { return BitbucketHelpers.ParticipantStatusToString(value); } + /// protected override ParticipantStatus ConvertFromString(string s) { return BitbucketHelpers.StringToParticipantStatus(s); diff --git a/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs b/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs index 28e1149..decbb44 100644 --- a/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/PermissionsConverter.cs @@ -2,26 +2,36 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for Bitbucket permission values. +/// public class PermissionsConverter : JsonEnumConverter { + /// protected override string ConvertToString(Permissions value) { return BitbucketHelpers.PermissionToString(value); } + /// protected override Permissions ConvertFromString(string s) { return BitbucketHelpers.StringToPermission(s); } } +/// +/// JSON converter for lists of Bitbucket permission values. +/// public class PermissionsListConverter : JsonEnumListConverter { + /// protected override string ConvertToString(Permissions value) { return BitbucketHelpers.PermissionToString(value); } + /// protected override Permissions ConvertFromString(string s) { return BitbucketHelpers.StringToPermission(s); diff --git a/src/Bitbucket.Net/Common/Converters/PullRequestStatesConverter.cs b/src/Bitbucket.Net/Common/Converters/PullRequestStatesConverter.cs index 1e4a0f8..f1ee423 100644 --- a/src/Bitbucket.Net/Common/Converters/PullRequestStatesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/PullRequestStatesConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for Bitbucket pull request states. +/// public class PullRequestStatesConverter : JsonEnumConverter { + /// protected override string ConvertToString(PullRequestStates value) { return BitbucketHelpers.PullRequestStateToString(value); } + /// protected override PullRequestStates ConvertFromString(string s) { return BitbucketHelpers.StringToPullRequestState(s); diff --git a/src/Bitbucket.Net/Common/Converters/RefRestrictionTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/RefRestrictionTypesConverter.cs index 8ffb269..e0641f5 100644 --- a/src/Bitbucket.Net/Common/Converters/RefRestrictionTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/RefRestrictionTypesConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for repository ref restriction types. +/// public class RefRestrictionTypesConverter : JsonEnumConverter { + /// protected override string ConvertToString(RefRestrictionTypes value) { return BitbucketHelpers.RefRestrictionTypeToString(value); } + /// protected override RefRestrictionTypes ConvertFromString(string s) { return BitbucketHelpers.StringToRefRestrictionType(s); diff --git a/src/Bitbucket.Net/Common/Converters/RolesConverter.cs b/src/Bitbucket.Net/Common/Converters/RolesConverter.cs index 5a10b18..b9f2431 100644 --- a/src/Bitbucket.Net/Common/Converters/RolesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/RolesConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for pull request role values. +/// public class RolesConverter : JsonEnumConverter { + /// protected override string ConvertToString(Roles value) { return BitbucketHelpers.RoleToString(value); } + /// protected override Roles ConvertFromString(string s) { return BitbucketHelpers.StringToRole(s); diff --git a/src/Bitbucket.Net/Common/Converters/ScopeTypesConverter.cs b/src/Bitbucket.Net/Common/Converters/ScopeTypesConverter.cs index 462082e..7a3e859 100644 --- a/src/Bitbucket.Net/Common/Converters/ScopeTypesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/ScopeTypesConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for Bitbucket permission scope types. +/// public class ScopeTypesConverter : JsonEnumConverter { + /// protected override string ConvertToString(ScopeTypes value) { return BitbucketHelpers.ScopeTypeToString(value); } + /// protected override ScopeTypes ConvertFromString(string s) { return BitbucketHelpers.StringToScopeType(s); diff --git a/src/Bitbucket.Net/Common/Converters/SynchronizeActionsConverter.cs b/src/Bitbucket.Net/Common/Converters/SynchronizeActionsConverter.cs index 0e38a17..9eb61bd 100644 --- a/src/Bitbucket.Net/Common/Converters/SynchronizeActionsConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/SynchronizeActionsConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for repository synchronization actions. +/// public class SynchronizeActionsConverter : JsonEnumConverter { + /// protected override string ConvertToString(SynchronizeActions value) { return BitbucketHelpers.SynchronizeActionToString(value); } + /// protected override SynchronizeActions ConvertFromString(string s) { return BitbucketHelpers.StringToSynchronizeAction(s); diff --git a/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs b/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs index 01c1759..91e4a87 100644 --- a/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/UnixDateTimeOffsetConverter.cs @@ -9,6 +9,7 @@ namespace Bitbucket.Net.Common.Converters; /// public sealed class UnixDateTimeOffsetConverter : JsonConverter { + /// public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.TokenType switch @@ -20,6 +21,7 @@ JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) = }; } + /// public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options) { writer.WriteNumberValue(value.ToUnixTimeMilliseconds()); @@ -32,6 +34,7 @@ public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSeri /// public sealed class NullableUnixDateTimeOffsetConverter : JsonConverter { + /// public override DateTimeOffset? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { return reader.TokenType switch @@ -44,6 +47,7 @@ JsonTokenType.String when long.TryParse(reader.GetString(), out long unixTime) = }; } + /// public override void Write(Utf8JsonWriter writer, DateTimeOffset? value, JsonSerializerOptions options) { if (value.HasValue) diff --git a/src/Bitbucket.Net/Common/Converters/WebHookOutcomesConverter.cs b/src/Bitbucket.Net/Common/Converters/WebHookOutcomesConverter.cs index d906cc6..322a512 100644 --- a/src/Bitbucket.Net/Common/Converters/WebHookOutcomesConverter.cs +++ b/src/Bitbucket.Net/Common/Converters/WebHookOutcomesConverter.cs @@ -2,13 +2,18 @@ namespace Bitbucket.Net.Common.Converters; +/// +/// JSON converter for webhook outcome values. +/// public class WebHookOutcomesConverter : JsonEnumConverter { + /// protected override string ConvertToString(WebHookOutcomes value) { return BitbucketHelpers.WebHookOutcomeToString(value); } + /// protected override WebHookOutcomes ConvertFromString(string s) { return BitbucketHelpers.StringToWebHookOutcome(s); diff --git a/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs b/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs index 74bc25c..dfe9cda 100644 --- a/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs +++ b/src/Bitbucket.Net/Common/DynamicMultipartFormDataContent.cs @@ -2,15 +2,30 @@ namespace Bitbucket.Net.Common; +/// +/// A helper for building multipart/form-data payloads where parts are added conditionally. +/// public class DynamicMultipartFormDataContent : IEnumerable { private readonly MultipartFormDataContent _multipartFormDataContent = new MultipartFormDataContent(); + /// + /// Adds a required multipart section. + /// + /// The HTTP content to add. + /// The form field name. public void Add(HttpContent value, string key) { _multipartFormDataContent.Add(value, key); } + /// + /// Adds a multipart section when a value is provided and not equal to the default for . + /// + /// The type of the guard value. + /// The guard value to test. + /// The HTTP content to add when is present. + /// The form field name. public void Add(T t, HttpContent? value, string key) { if (!EqualityComparer.Default.Equals(t, default) && value is not null) @@ -19,13 +34,19 @@ public void Add(T t, HttpContent? value, string key) } } + /// + /// Finalizes the builder and returns the underlying instance. + /// + /// The built multipart form data content. public MultipartFormDataContent ToMultipartFormDataContent() => _multipartFormDataContent; + /// public IEnumerator GetEnumerator() { return _multipartFormDataContent.GetEnumerator(); } + /// IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs b/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs index 5c43e7c..911c252 100644 --- a/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs +++ b/src/Bitbucket.Net/Common/FlurlRequestExtensions.cs @@ -2,8 +2,19 @@ namespace Bitbucket.Net.Common; +/// +/// Extension methods for configuring and invoking Flurl requests in the Bitbucket client. +/// public static class FlurlRequestExtensions { + /// + /// Applies either bearer-token or basic authentication to the request. + /// + /// The request to decorate. + /// Delegate that supplies an OAuth bearer token. When provided, bearer auth is used. + /// The user name for basic authentication. + /// The password for basic authentication. + /// The authenticated . public static IFlurlRequest WithAuthentication(this IFlurlRequest request, Func? getToken, string? userName, string? password) { if (getToken != null) @@ -15,6 +26,12 @@ public static IFlurlRequest WithAuthentication(this IFlurlRequest request, Func< return request.WithBasicAuth(userName, password); } + /// + /// Sends a GET request, honoring the provided cancellation token. + /// + /// The request to execute. + /// The cancellation token. + /// The Flurl response. public static Task GetAsync(this IFlurlRequest request, CancellationToken cancellationToken) { return request.GetAsync(HttpCompletionOption.ResponseContentRead, cancellationToken); diff --git a/src/Bitbucket.Net/Common/Models/PagedResults.cs b/src/Bitbucket.Net/Common/Models/PagedResults.cs index 5698614..75e00bf 100644 --- a/src/Bitbucket.Net/Common/Models/PagedResults.cs +++ b/src/Bitbucket.Net/Common/Models/PagedResults.cs @@ -1,9 +1,23 @@ namespace Bitbucket.Net.Common.Models; +/// +/// Represents a Bitbucket paged response containing a collection of items. +/// public class PagedResults : PagedResultsBase { + /// + /// Gets or sets the maximum number of items returned in this page. + /// public int Limit { get; set; } + + /// + /// Gets or sets the page of items returned by the request. + /// public List Values { get; set; } = []; + + /// + /// Gets or sets the starting offset of the next page, when available. + /// public int? NextPageStart { get; set; } /// diff --git a/src/Bitbucket.Net/Common/TypeExtensions.cs b/src/Bitbucket.Net/Common/TypeExtensions.cs index da895e3..9dadc55 100644 --- a/src/Bitbucket.Net/Common/TypeExtensions.cs +++ b/src/Bitbucket.Net/Common/TypeExtensions.cs @@ -2,7 +2,15 @@ namespace Bitbucket.Net.Common; +/// +/// Provides reflection-based helpers for working with instances. +/// public static class TypeExtensions { + /// + /// Determines whether the specified is a . + /// + /// The type to inspect. + /// when the type is a nullable value type; otherwise . public static bool IsNullableType(Type type) => type.GetTypeInfo().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs index 419388f..904400a 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Branch.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Branch.cs @@ -88,5 +88,8 @@ public BranchMetaData? BranchMetadata [JsonPropertyName("metadata")] public JsonElement? Metadata { get; set; } + /// + /// Returns the branch display identifier. + /// public override string ToString() => DisplayId ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Link.cs b/src/Bitbucket.Net/Models/Core/Projects/Link.cs index 898d940..e357710 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Link.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Link.cs @@ -10,5 +10,8 @@ public class Link /// public string? Href { get; set; } + /// + /// Returns the link URL or an empty string when not set. + /// public override string ToString() => Href ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs index ffc0f00..1a89859 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Participant.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Participant.cs @@ -31,5 +31,8 @@ public class Participant [JsonConverter(typeof(ParticipantStatusConverter))] public ParticipantStatus Status { get; set; } + /// + /// Returns the participant's display name when available. + /// public override string ToString() => User?.DisplayName ?? "Unknown"; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Project.cs b/src/Bitbucket.Net/Models/Core/Projects/Project.cs index 8f35fc2..998f60b 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Project.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Project.cs @@ -25,5 +25,8 @@ public class Project : ProjectDefinition /// public Links? Links { get; set; } + /// + /// Returns the project name, when available. + /// public override string ToString() => Name ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs index 922cc44..4123cc9 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/PullRequest.cs @@ -45,5 +45,8 @@ public class PullRequest : PullRequestInfo /// public Links? Links { get; set; } + /// + /// Returns a human-readable label combining the author display name and title. + /// public override string ToString() => $"{Author?.User?.DisplayName ?? "Unknown"}: {Title ?? "(untitled)"}"; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Projects/Repository.cs b/src/Bitbucket.Net/Models/Core/Projects/Repository.cs index f38b6fe..b901be7 100644 --- a/src/Bitbucket.Net/Models/Core/Projects/Repository.cs +++ b/src/Bitbucket.Net/Models/Core/Projects/Repository.cs @@ -40,5 +40,8 @@ public class Repository : RepositoryRef /// public CloneLinks? Links { get; set; } + /// + /// Returns the repository name, when available. + /// public override string ToString() => Name ?? string.Empty; } \ No newline at end of file diff --git a/src/Bitbucket.Net/Models/Core/Users/User.cs b/src/Bitbucket.Net/Models/Core/Users/User.cs index 4405c1a..a4390b1 100644 --- a/src/Bitbucket.Net/Models/Core/Users/User.cs +++ b/src/Bitbucket.Net/Models/Core/Users/User.cs @@ -35,5 +35,8 @@ public class User : Identity /// public string? AvatarUrl { get; set; } + /// + /// Returns the user's display name when available. + /// public override string ToString() => DisplayName ?? string.Empty; } \ No newline at end of file From 955ac2a3c2cb8b87241940f598a34deb13961807 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:54:53 +0000 Subject: [PATCH 49/50] refactor: normalize line endings --- .gitattributes | 2 + src/Bitbucket.Net/Audit/BitbucketClient.cs | 190 +++--- .../Core/Tasks/BitbucketClient.cs | 188 +++--- .../DefaultReviewers/BitbucketClient.cs | 398 ++++++------- .../PersonalAccessTokens/BitbucketClient.cs | 256 ++++---- .../RefRestrictions/BitbucketClient.cs | 556 +++++++++--------- 6 files changed, 796 insertions(+), 794 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fbdb6a0 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +# Normalize all text files to CRLF on checkout (matches .editorconfig end_of_line = crlf) +* text=auto eol=crlf diff --git a/src/Bitbucket.Net/Audit/BitbucketClient.cs b/src/Bitbucket.Net/Audit/BitbucketClient.cs index ee74f26..c2969f8 100644 --- a/src/Bitbucket.Net/Audit/BitbucketClient.cs +++ b/src/Bitbucket.Net/Audit/BitbucketClient.cs @@ -1,96 +1,96 @@ -using Bitbucket.Net.Common; -using Bitbucket.Net.Common.Models; -using Bitbucket.Net.Models.Audit; -using Flurl.Http; - -namespace Bitbucket.Net; - -public partial class BitbucketClient -{ - /// - /// Gets the base audit URL. - /// - /// An representing the audit endpoint. - private IFlurlRequest GetAuditUrl() => GetBaseUrl("/audit"); - - /// - /// Gets the audit URL with the specified path appended. - /// - /// The path to append to the audit URL. - /// An representing the audit endpoint with the specified path. - private IFlurlRequest GetAuditUrl(string path) => GetAuditUrl() - .AppendPathSegment(path); - - /// - /// Retrieves audit events for a specific project. - /// - /// The key of the project. - /// The maximum number of pages to retrieve. If null, all pages are retrieved. - /// The maximum number of results per page. - /// The starting index for pagination. - /// The size of user avatars to include in the response. - /// A cancellation token that can be used to cancel the operation. - /// A task that represents the asynchronous operation. The task result contains a collection of objects. - public async Task> GetProjectAuditEventsAsync(string projectKey, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetAuditUrl($"/projects/{projectKey}/events") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Retrieves audit events for a specific repository within a project. - /// - /// The key of the project. - /// The slug (identifier) of the repository. - /// The maximum number of pages to retrieve. If , all pages are retrieved. - /// The maximum number of results per page. - /// The starting index for pagination. - /// The size of user avatars to include in the response. - /// A cancellation token that can be used to cancel the operation. - /// A task that represents the asynchronous operation. The task result contains a collection of objects. - public async Task> GetProjectRepoAuditEventsAsync(string projectKey, string repositorySlug, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetAuditUrl($"/projects/{projectKey}/repos/{repositorySlug}/events") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.Audit; +using Flurl.Http; + +namespace Bitbucket.Net; + +public partial class BitbucketClient +{ + /// + /// Gets the base audit URL. + /// + /// An representing the audit endpoint. + private IFlurlRequest GetAuditUrl() => GetBaseUrl("/audit"); + + /// + /// Gets the audit URL with the specified path appended. + /// + /// The path to append to the audit URL. + /// An representing the audit endpoint with the specified path. + private IFlurlRequest GetAuditUrl(string path) => GetAuditUrl() + .AppendPathSegment(path); + + /// + /// Retrieves audit events for a specific project. + /// + /// The key of the project. + /// The maximum number of pages to retrieve. If null, all pages are retrieved. + /// The maximum number of results per page. + /// The starting index for pagination. + /// The size of user avatars to include in the response. + /// A cancellation token that can be used to cancel the operation. + /// A task that represents the asynchronous operation. The task result contains a collection of objects. + public async Task> GetProjectAuditEventsAsync(string projectKey, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetAuditUrl($"/projects/{projectKey}/events") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Retrieves audit events for a specific repository within a project. + /// + /// The key of the project. + /// The slug (identifier) of the repository. + /// The maximum number of pages to retrieve. If , all pages are retrieved. + /// The maximum number of results per page. + /// The starting index for pagination. + /// The size of user avatars to include in the response. + /// A cancellation token that can be used to cancel the operation. + /// A task that represents the asynchronous operation. The task result contains a collection of objects. + public async Task> GetProjectRepoAuditEventsAsync(string projectKey, string repositorySlug, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetAuditUrl($"/projects/{projectKey}/repos/{repositorySlug}/events") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } } \ No newline at end of file diff --git a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs index 43ef07a..839636e 100644 --- a/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs +++ b/src/Bitbucket.Net/Core/Tasks/BitbucketClient.cs @@ -1,95 +1,95 @@ -using Bitbucket.Net.Common; -using Bitbucket.Net.Models.Core.Tasks; -using Flurl.Http; - -namespace Bitbucket.Net; - -/// -/// Provides task-related Bitbucket API operations. -/// -public partial class BitbucketClient -{ - /// - /// Gets the base tasks URL. - /// - /// An targeting the tasks endpoint. - private IFlurlRequest GetTasksUrl() => GetBaseUrl() - .AppendPathSegment("/tasks"); - - /// - /// Gets the tasks URL for the specified path. - /// - /// The path to append to the tasks endpoint. - /// An pointing to the tasks path. - private IFlurlRequest GetTasksUrl(string path) => GetTasksUrl() - .AppendPathSegment(path); - - /// - /// Creates a task. - /// - /// The task information. - /// Token to cancel the operation. - /// The created task. - public async Task CreateTaskAsync(TaskInfo taskInfo, CancellationToken cancellationToken = default) - { - var response = await GetTasksUrl() - .SendAsync(HttpMethod.Post, CreateJsonContent(taskInfo), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a task by identifier. - /// - /// The task identifier. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// The requested task. - public async Task GetTaskAsync(long taskId, int? avatarSize = null, CancellationToken cancellationToken = default) - { - var response = await GetTasksUrl($"/{taskId}") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a task's text. - /// - /// The task identifier. - /// The updated task text. - /// Token to cancel the operation. - /// The updated task. - public async Task UpdateTaskAsync(long taskId, string text, CancellationToken cancellationToken = default) - { - var obj = new - { - id = taskId, - text, - }; - - var response = await GetTasksUrl($"/{taskId}") - .SendAsync(HttpMethod.Put, CreateJsonContent(obj), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a task. - /// - /// The task identifier. - /// Token to cancel the operation. - /// true if the task was deleted; otherwise, false. - public async Task DeleteTaskAsync(long taskId, CancellationToken cancellationToken = default) - { - var response = await GetTasksUrl($"/{taskId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } +using Bitbucket.Net.Common; +using Bitbucket.Net.Models.Core.Tasks; +using Flurl.Http; + +namespace Bitbucket.Net; + +/// +/// Provides task-related Bitbucket API operations. +/// +public partial class BitbucketClient +{ + /// + /// Gets the base tasks URL. + /// + /// An targeting the tasks endpoint. + private IFlurlRequest GetTasksUrl() => GetBaseUrl() + .AppendPathSegment("/tasks"); + + /// + /// Gets the tasks URL for the specified path. + /// + /// The path to append to the tasks endpoint. + /// An pointing to the tasks path. + private IFlurlRequest GetTasksUrl(string path) => GetTasksUrl() + .AppendPathSegment(path); + + /// + /// Creates a task. + /// + /// The task information. + /// Token to cancel the operation. + /// The created task. + public async Task CreateTaskAsync(TaskInfo taskInfo, CancellationToken cancellationToken = default) + { + var response = await GetTasksUrl() + .SendAsync(HttpMethod.Post, CreateJsonContent(taskInfo), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a task by identifier. + /// + /// The task identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// The requested task. + public async Task GetTaskAsync(long taskId, int? avatarSize = null, CancellationToken cancellationToken = default) + { + var response = await GetTasksUrl($"/{taskId}") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a task's text. + /// + /// The task identifier. + /// The updated task text. + /// Token to cancel the operation. + /// The updated task. + public async Task UpdateTaskAsync(long taskId, string text, CancellationToken cancellationToken = default) + { + var obj = new + { + id = taskId, + text, + }; + + var response = await GetTasksUrl($"/{taskId}") + .SendAsync(HttpMethod.Put, CreateJsonContent(obj), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a task. + /// + /// The task identifier. + /// Token to cancel the operation. + /// true if the task was deleted; otherwise, false. + public async Task DeleteTaskAsync(long taskId, CancellationToken cancellationToken = default) + { + var response = await GetTasksUrl($"/{taskId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } } \ No newline at end of file diff --git a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs index 5666ff9..5af1890 100644 --- a/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs +++ b/src/Bitbucket.Net/DefaultReviewers/BitbucketClient.cs @@ -1,200 +1,200 @@ -using Bitbucket.Net.Models.Core.Users; -using Bitbucket.Net.Models.DefaultReviewers; -using Flurl.Http; - -namespace Bitbucket.Net; - -/// -/// Provides default reviewer related Bitbucket API operations. -/// -public partial class BitbucketClient -{ - /// - /// Gets the base default reviewers URL. - /// - /// An targeting the default reviewers root. - private IFlurlRequest GetDefaultReviewersUrl() => GetBaseUrl("/default-reviewers"); - - /// - /// Gets the default reviewers URL for the specified path. - /// - /// The path to append to the default reviewers root. - /// An pointing to the requested default reviewers path. - private IFlurlRequest GetDefaultReviewersUrl(string path) => GetDefaultReviewersUrl() - .AppendPathSegment(path); - - /// - /// Retrieves default reviewer conditions for a project. - /// - /// The project key. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// A collection of default reviewer conditions. - public async Task> GetDefaultReviewerConditionsAsync(string projectKey, - int? avatarSize = null, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Creates a default reviewer condition for a project. - /// - /// The project key. - /// The condition to create. - /// Token to cancel the operation. - /// The created default reviewer condition. - public async Task CreateDefaultReviewerConditionAsync(string projectKey, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") - .SendAsync(HttpMethod.Post, CreateJsonContent(condition), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a default reviewer condition for a project. - /// - /// The project key. - /// The condition identifier. - /// The updated condition. - /// Token to cancel the operation. - /// The updated default reviewer condition. - public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") - .SendAsync(HttpMethod.Put, CreateJsonContent(condition), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a default reviewer condition from a project. - /// - /// The project key. - /// The condition identifier. - /// Token to cancel the operation. - /// true if the condition was deleted; otherwise, false. - public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves default reviewer conditions for a repository. - /// - /// The project key. - /// The repository slug. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// A collection of default reviewer conditions. - public async Task> GetDefaultReviewerConditionsAsync(string projectKey, string repositorySlug, - int? avatarSize = null, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Creates a default reviewer condition for a repository. - /// - /// The project key. - /// The repository slug. - /// The condition to create. - /// Token to cancel the operation. - /// The created default reviewer condition. - public async Task CreateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") - .SendAsync(HttpMethod.Post, CreateJsonContent(condition), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a default reviewer condition for a repository. - /// - /// The project key. - /// The repository slug. - /// The condition identifier. - /// The updated condition. - /// Token to cancel the operation. - /// The updated default reviewer condition. - public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") - .SendAsync(HttpMethod.Put, CreateJsonContent(condition), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a default reviewer condition from a repository. - /// - /// The project key. - /// The repository slug. - /// The condition identifier. - /// Token to cancel the operation. - /// true if the condition was deleted; otherwise, false. - public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) - { - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves default reviewers for a repository. - /// - /// The project key. - /// The repository slug. - /// Optional source repository identifier. - /// Optional target repository identifier. - /// Optional source reference identifier. - /// Optional target reference identifier. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// A collection of default reviewers. - public async Task> GetDefaultReviewersAsync(string projectKey, string repositorySlug, - int? sourceRepoId = null, - int? targetRepoId = null, - string? sourceRefId = null, - string? targetRefId = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["sourceRepoId"] = sourceRepoId, - ["targetRepoId"] = targetRepoId, - ["sourceRefId"] = sourceRefId, - ["targetRefId"] = targetRefId, - ["avatarSize"] = avatarSize, - }; - - var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/reviewers") - .SetQueryParams(queryParamValues) - .GetAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } +using Bitbucket.Net.Models.Core.Users; +using Bitbucket.Net.Models.DefaultReviewers; +using Flurl.Http; + +namespace Bitbucket.Net; + +/// +/// Provides default reviewer related Bitbucket API operations. +/// +public partial class BitbucketClient +{ + /// + /// Gets the base default reviewers URL. + /// + /// An targeting the default reviewers root. + private IFlurlRequest GetDefaultReviewersUrl() => GetBaseUrl("/default-reviewers"); + + /// + /// Gets the default reviewers URL for the specified path. + /// + /// The path to append to the default reviewers root. + /// An pointing to the requested default reviewers path. + private IFlurlRequest GetDefaultReviewersUrl(string path) => GetDefaultReviewersUrl() + .AppendPathSegment(path); + + /// + /// Retrieves default reviewer conditions for a project. + /// + /// The project key. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of default reviewer conditions. + public async Task> GetDefaultReviewerConditionsAsync(string projectKey, + int? avatarSize = null, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Creates a default reviewer condition for a project. + /// + /// The project key. + /// The condition to create. + /// Token to cancel the operation. + /// The created default reviewer condition. + public async Task CreateDefaultReviewerConditionAsync(string projectKey, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions") + .SendAsync(HttpMethod.Post, CreateJsonContent(condition), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a default reviewer condition for a project. + /// + /// The project key. + /// The condition identifier. + /// The updated condition. + /// Token to cancel the operation. + /// The updated default reviewer condition. + public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") + .SendAsync(HttpMethod.Put, CreateJsonContent(condition), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a default reviewer condition from a project. + /// + /// The project key. + /// The condition identifier. + /// Token to cancel the operation. + /// true if the condition was deleted; otherwise, false. + public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/conditions/{defaultReviewerPullRequestConditionId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves default reviewer conditions for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of default reviewer conditions. + public async Task> GetDefaultReviewerConditionsAsync(string projectKey, string repositorySlug, + int? avatarSize = null, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Creates a default reviewer condition for a repository. + /// + /// The project key. + /// The repository slug. + /// The condition to create. + /// Token to cancel the operation. + /// The created default reviewer condition. + public async Task CreateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions") + .SendAsync(HttpMethod.Post, CreateJsonContent(condition), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a default reviewer condition for a repository. + /// + /// The project key. + /// The repository slug. + /// The condition identifier. + /// The updated condition. + /// Token to cancel the operation. + /// The updated default reviewer condition. + public async Task UpdateDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, DefaultReviewerPullRequestCondition condition, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") + .SendAsync(HttpMethod.Put, CreateJsonContent(condition), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a default reviewer condition from a repository. + /// + /// The project key. + /// The repository slug. + /// The condition identifier. + /// Token to cancel the operation. + /// true if the condition was deleted; otherwise, false. + public async Task DeleteDefaultReviewerConditionAsync(string projectKey, string repositorySlug, string defaultReviewerPullRequestConditionId, CancellationToken cancellationToken = default) + { + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/conditions/{defaultReviewerPullRequestConditionId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves default reviewers for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional source repository identifier. + /// Optional target repository identifier. + /// Optional source reference identifier. + /// Optional target reference identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of default reviewers. + public async Task> GetDefaultReviewersAsync(string projectKey, string repositorySlug, + int? sourceRepoId = null, + int? targetRepoId = null, + string? sourceRefId = null, + string? targetRefId = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["sourceRepoId"] = sourceRepoId, + ["targetRepoId"] = targetRepoId, + ["sourceRefId"] = sourceRefId, + ["targetRefId"] = targetRefId, + ["avatarSize"] = avatarSize, + }; + + var response = await GetDefaultReviewersUrl($"/projects/{projectKey}/repos/{repositorySlug}/reviewers") + .SetQueryParams(queryParamValues) + .GetAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } } \ No newline at end of file diff --git a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs index 0d94a33..8843715 100644 --- a/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs +++ b/src/Bitbucket.Net/PersonalAccessTokens/BitbucketClient.cs @@ -1,129 +1,129 @@ -using Bitbucket.Net.Common; -using Bitbucket.Net.Common.Models; -using Bitbucket.Net.Models.PersonalAccessTokens; -using Flurl.Http; - -namespace Bitbucket.Net; - -/// -/// Provides personal access token related Bitbucket API operations. -/// -public partial class BitbucketClient -{ - /// - /// Gets the base personal access token URL. - /// - /// An targeting the PAT root. - private IFlurlRequest GetPatUrl() => GetBaseUrl("/access-tokens"); - - /// - /// Gets the personal access token URL for the specified path. - /// - /// The path to append to the PAT root. - /// An pointing to the PAT path. - private IFlurlRequest GetPatUrl(string path) => GetPatUrl() - .AppendPathSegment(path); - - /// - /// Retrieves access tokens for a user. - /// - /// The user slug. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// A collection of access tokens. - public async Task> GetUserAccessTokensAsync(string userSlug, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetPatUrl($"/users/{userSlug}") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Creates a personal access token for a user. - /// - /// The user slug. - /// The token creation payload. - /// Token to cancel the operation. - /// The created access token including secret. - public async Task CreateAccessTokenAsync(string userSlug, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) - { - var response = await GetPatUrl($"/users/{userSlug}") - .SendAsync(HttpMethod.Put, CreateJsonContent(accessToken), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a personal access token by identifier. - /// - /// The user slug. - /// The token identifier. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// The access token details. - public async Task GetUserAccessTokenAsync(string userSlug, string tokenId, int? avatarSize = null, CancellationToken cancellationToken = default) - { - var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Updates a personal access token. - /// - /// The user slug. - /// The token identifier. - /// The updated token payload. - /// Token to cancel the operation. - /// The updated access token details. - public async Task ChangeUserAccessTokenAsync(string userSlug, string tokenId, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) - { - var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") - .SendAsync(HttpMethod.Post, CreateJsonContent(accessToken), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a personal access token. - /// - /// The user slug. - /// The token identifier. - /// Token to cancel the operation. - /// true if the token was deleted; otherwise, false. - public async Task DeleteUserAccessTokenAsync(string userSlug, string tokenId, CancellationToken cancellationToken = default) - { - var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.PersonalAccessTokens; +using Flurl.Http; + +namespace Bitbucket.Net; + +/// +/// Provides personal access token related Bitbucket API operations. +/// +public partial class BitbucketClient +{ + /// + /// Gets the base personal access token URL. + /// + /// An targeting the PAT root. + private IFlurlRequest GetPatUrl() => GetBaseUrl("/access-tokens"); + + /// + /// Gets the personal access token URL for the specified path. + /// + /// The path to append to the PAT root. + /// An pointing to the PAT path. + private IFlurlRequest GetPatUrl(string path) => GetPatUrl() + .AppendPathSegment(path); + + /// + /// Retrieves access tokens for a user. + /// + /// The user slug. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of access tokens. + public async Task> GetUserAccessTokensAsync(string userSlug, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetPatUrl($"/users/{userSlug}") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Creates a personal access token for a user. + /// + /// The user slug. + /// The token creation payload. + /// Token to cancel the operation. + /// The created access token including secret. + public async Task CreateAccessTokenAsync(string userSlug, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) + { + var response = await GetPatUrl($"/users/{userSlug}") + .SendAsync(HttpMethod.Put, CreateJsonContent(accessToken), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a personal access token by identifier. + /// + /// The user slug. + /// The token identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// The access token details. + public async Task GetUserAccessTokenAsync(string userSlug, string tokenId, int? avatarSize = null, CancellationToken cancellationToken = default) + { + var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Updates a personal access token. + /// + /// The user slug. + /// The token identifier. + /// The updated token payload. + /// Token to cancel the operation. + /// The updated access token details. + public async Task ChangeUserAccessTokenAsync(string userSlug, string tokenId, AccessTokenCreate accessToken, CancellationToken cancellationToken = default) + { + var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") + .SendAsync(HttpMethod.Post, CreateJsonContent(accessToken), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a personal access token. + /// + /// The user slug. + /// The token identifier. + /// Token to cancel the operation. + /// true if the token was deleted; otherwise, false. + public async Task DeleteUserAccessTokenAsync(string userSlug, string tokenId, CancellationToken cancellationToken = default) + { + var response = await GetPatUrl($"/users/{userSlug}/{tokenId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } } \ No newline at end of file diff --git a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs index 7633205..9f13adf 100644 --- a/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs +++ b/src/Bitbucket.Net/RefRestrictions/BitbucketClient.cs @@ -1,279 +1,279 @@ -using Bitbucket.Net.Common; -using Bitbucket.Net.Common.Models; -using Bitbucket.Net.Models.RefRestrictions; -using Flurl.Http; - -namespace Bitbucket.Net; - -/// -/// Provides reference restriction (branch permissions) Bitbucket API operations. -/// -public partial class BitbucketClient -{ - /// - /// Gets the base ref restrictions URL. - /// - /// An targeting the branch permissions root. - private IFlurlRequest GetRefRestrictionsUrl() => GetBaseUrl("/branch-permissions", "2.0"); - - /// - /// Gets the ref restrictions URL for the specified path. - /// - /// The path to append to the branch permissions root. - /// An pointing to the requested branch permissions path. - private IFlurlRequest GetRefRestrictionsUrl(string path) => GetRefRestrictionsUrl() - .AppendPathSegment(path); - - /// - /// Retrieves reference restrictions for a project. - /// - /// The project key. - /// Optional restriction type filter. - /// Optional matcher type filter. - /// Optional matcher identifier filter. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// A collection of reference restrictions. - public async Task> GetProjectRefRestrictionsAsync(string projectKey, - RefRestrictionTypes? type = null, - RefMatcherTypes? matcherType = null, - string? matcherId = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), - ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), - ["matcherId"] = matcherId, - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Creates multiple reference restrictions for a project. - /// - /// The project key. - /// Token to cancel the operation. - /// The reference restrictions to create. - /// The created reference restrictions. - public async Task> CreateProjectRefRestrictionsAsync(string projectKey, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") - .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") - .SendAsync(HttpMethod.Post, CreateJsonContent(refRestrictions), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Creates multiple reference restrictions for a project using the default cancellation token. - /// - /// The project key. - /// The reference restrictions to create. - /// The created reference restrictions. - public async Task> CreateProjectRefRestrictionsAsync(string projectKey, params RefRestrictionCreate[] refRestrictions) - { - return await CreateProjectRefRestrictionsAsync(projectKey, default, refRestrictions).ConfigureAwait(false); - } - - /// - /// Creates a single reference restriction for a project. - /// - /// The project key. - /// The reference restriction to create. - /// Token to cancel the operation. - /// The created reference restriction. - public async Task CreateProjectRefRestrictionAsync(string projectKey, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") - .SendAsync(HttpMethod.Post, CreateJsonContent(refRestriction), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a specific project reference restriction. - /// - /// The project key. - /// The restriction identifier. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// The requested reference restriction. - public async Task GetProjectRefRestrictionAsync(string projectKey, int refRestrictionId, int? avatarSize = null, CancellationToken cancellationToken = default) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a project reference restriction. - /// - /// The project key. - /// The restriction identifier. - /// Token to cancel the operation. - /// true if the restriction was deleted; otherwise, false. - public async Task DeleteProjectRefRestrictionAsync(string projectKey, int refRestrictionId, CancellationToken cancellationToken = default) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves reference restrictions for a repository. - /// - /// The project key. - /// The repository slug. - /// Optional restriction type filter. - /// Optional matcher type filter. - /// Optional matcher identifier filter. - /// Optional maximum number of pages to retrieve. - /// Optional page size. - /// Optional starting index for pagination. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// A collection of reference restrictions. - public async Task> GetRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, - RefRestrictionTypes? type = null, - RefMatcherTypes? matcherType = null, - string? matcherId = null, - int? maxPages = null, - int? limit = null, - int? start = null, - int? avatarSize = null, - CancellationToken cancellationToken = default) - { - var queryParamValues = new Dictionary(StringComparer.Ordinal) - { - ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), - ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), - ["matcherId"] = matcherId, - ["limit"] = limit, - ["start"] = start, - ["avatarSize"] = avatarSize, - }; - - return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") - .SetQueryParams(qpv) - .GetAsync(ct) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); - }, cancellationToken) - .ConfigureAwait(false); - } - - /// - /// Creates multiple reference restrictions for a repository. - /// - /// The project key. - /// The repository slug. - /// Token to cancel the operation. - /// The reference restrictions to create. - /// The created reference restrictions. - public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") - .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") - .SendAsync(HttpMethod.Post, CreateJsonContent(refRestrictions), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Creates multiple reference restrictions for a repository using the default cancellation token. - /// - /// The project key. - /// The repository slug. - /// The reference restrictions to create. - /// The created reference restrictions. - public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, params RefRestrictionCreate[] refRestrictions) - { - return await CreateRepositoryRefRestrictionsAsync(projectKey, repositorySlug, default, refRestrictions).ConfigureAwait(false); - } - - /// - /// Creates a single reference restriction for a repository. - /// - /// The project key. - /// The repository slug. - /// The reference restriction to create. - /// Token to cancel the operation. - /// The created reference restriction. - public async Task CreateRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") - .SendAsync(HttpMethod.Post, CreateJsonContent(refRestriction), cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Retrieves a specific repository reference restriction. - /// - /// The project key. - /// The repository slug. - /// The restriction identifier. - /// Optional avatar size for returned users. - /// Token to cancel the operation. - /// The requested reference restriction. - public async Task GetRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, - int? avatarSize = null, CancellationToken cancellationToken = default) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") - .SetQueryParam("avatarSize", avatarSize) - .GetAsync(cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); - } - - /// - /// Deletes a repository reference restriction. - /// - /// The project key. - /// The repository slug. - /// The restriction identifier. - /// Token to cancel the operation. - /// true if the restriction was deleted; otherwise, false. - public async Task DeleteRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, CancellationToken cancellationToken = default) - { - var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") - .DeleteAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); - - return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); - } +using Bitbucket.Net.Common; +using Bitbucket.Net.Common.Models; +using Bitbucket.Net.Models.RefRestrictions; +using Flurl.Http; + +namespace Bitbucket.Net; + +/// +/// Provides reference restriction (branch permissions) Bitbucket API operations. +/// +public partial class BitbucketClient +{ + /// + /// Gets the base ref restrictions URL. + /// + /// An targeting the branch permissions root. + private IFlurlRequest GetRefRestrictionsUrl() => GetBaseUrl("/branch-permissions", "2.0"); + + /// + /// Gets the ref restrictions URL for the specified path. + /// + /// The path to append to the branch permissions root. + /// An pointing to the requested branch permissions path. + private IFlurlRequest GetRefRestrictionsUrl(string path) => GetRefRestrictionsUrl() + .AppendPathSegment(path); + + /// + /// Retrieves reference restrictions for a project. + /// + /// The project key. + /// Optional restriction type filter. + /// Optional matcher type filter. + /// Optional matcher identifier filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of reference restrictions. + public async Task> GetProjectRefRestrictionsAsync(string projectKey, + RefRestrictionTypes? type = null, + RefMatcherTypes? matcherType = null, + string? matcherId = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), + ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), + ["matcherId"] = matcherId, + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Creates multiple reference restrictions for a project. + /// + /// The project key. + /// Token to cancel the operation. + /// The reference restrictions to create. + /// The created reference restrictions. + public async Task> CreateProjectRefRestrictionsAsync(string projectKey, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") + .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") + .SendAsync(HttpMethod.Post, CreateJsonContent(refRestrictions), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Creates multiple reference restrictions for a project using the default cancellation token. + /// + /// The project key. + /// The reference restrictions to create. + /// The created reference restrictions. + public async Task> CreateProjectRefRestrictionsAsync(string projectKey, params RefRestrictionCreate[] refRestrictions) + { + return await CreateProjectRefRestrictionsAsync(projectKey, default, refRestrictions).ConfigureAwait(false); + } + + /// + /// Creates a single reference restriction for a project. + /// + /// The project key. + /// The reference restriction to create. + /// Token to cancel the operation. + /// The created reference restriction. + public async Task CreateProjectRefRestrictionAsync(string projectKey, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions") + .SendAsync(HttpMethod.Post, CreateJsonContent(refRestriction), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a specific project reference restriction. + /// + /// The project key. + /// The restriction identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// The requested reference restriction. + public async Task GetProjectRefRestrictionAsync(string projectKey, int refRestrictionId, int? avatarSize = null, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a project reference restriction. + /// + /// The project key. + /// The restriction identifier. + /// Token to cancel the operation. + /// true if the restriction was deleted; otherwise, false. + public async Task DeleteProjectRefRestrictionAsync(string projectKey, int refRestrictionId, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/restrictions/{refRestrictionId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves reference restrictions for a repository. + /// + /// The project key. + /// The repository slug. + /// Optional restriction type filter. + /// Optional matcher type filter. + /// Optional matcher identifier filter. + /// Optional maximum number of pages to retrieve. + /// Optional page size. + /// Optional starting index for pagination. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// A collection of reference restrictions. + public async Task> GetRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, + RefRestrictionTypes? type = null, + RefMatcherTypes? matcherType = null, + string? matcherId = null, + int? maxPages = null, + int? limit = null, + int? start = null, + int? avatarSize = null, + CancellationToken cancellationToken = default) + { + var queryParamValues = new Dictionary(StringComparer.Ordinal) + { + ["type"] = BitbucketHelpers.RefRestrictionTypeToString(type), + ["matcherType"] = BitbucketHelpers.RefMatcherTypeToString(matcherType), + ["matcherId"] = matcherId, + ["limit"] = limit, + ["start"] = start, + ["avatarSize"] = avatarSize, + }; + + return await GetPagedResultsAsync(maxPages, queryParamValues, async (qpv, ct) => + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") + .SetQueryParams(qpv) + .GetAsync(ct) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: ct).ConfigureAwait(false); + }, cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Creates multiple reference restrictions for a repository. + /// + /// The project key. + /// The repository slug. + /// Token to cancel the operation. + /// The reference restrictions to create. + /// The created reference restrictions. + public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, CancellationToken cancellationToken, params RefRestrictionCreate[] refRestrictions) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") + .WithHeader("Accept", "application/vnd.atl.bitbucket.bulk+json") + .SendAsync(HttpMethod.Post, CreateJsonContent(refRestrictions), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync>(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Creates multiple reference restrictions for a repository using the default cancellation token. + /// + /// The project key. + /// The repository slug. + /// The reference restrictions to create. + /// The created reference restrictions. + public async Task> CreateRepositoryRefRestrictionsAsync(string projectKey, string repositorySlug, params RefRestrictionCreate[] refRestrictions) + { + return await CreateRepositoryRefRestrictionsAsync(projectKey, repositorySlug, default, refRestrictions).ConfigureAwait(false); + } + + /// + /// Creates a single reference restriction for a repository. + /// + /// The project key. + /// The repository slug. + /// The reference restriction to create. + /// Token to cancel the operation. + /// The created reference restriction. + public async Task CreateRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, RefRestrictionCreate refRestriction, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions") + .SendAsync(HttpMethod.Post, CreateJsonContent(refRestriction), cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Retrieves a specific repository reference restriction. + /// + /// The project key. + /// The repository slug. + /// The restriction identifier. + /// Optional avatar size for returned users. + /// Token to cancel the operation. + /// The requested reference restriction. + public async Task GetRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, + int? avatarSize = null, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") + .SetQueryParam("avatarSize", avatarSize) + .GetAsync(cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken: cancellationToken).ConfigureAwait(false); + } + + /// + /// Deletes a repository reference restriction. + /// + /// The project key. + /// The repository slug. + /// The restriction identifier. + /// Token to cancel the operation. + /// true if the restriction was deleted; otherwise, false. + public async Task DeleteRepositoryRefRestrictionAsync(string projectKey, string repositorySlug, int refRestrictionId, CancellationToken cancellationToken = default) + { + var response = await GetRefRestrictionsUrl($"/projects/{projectKey}/repos/{repositorySlug}/restrictions/{refRestrictionId}") + .DeleteAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); + + return await HandleResponseAsync(response, cancellationToken).ConfigureAwait(false); + } } \ No newline at end of file From 6e5c24385e51d2739740d0cb1f14fb2c1d78ce06 Mon Sep 17 00:00:00 2001 From: diomonogatari <17832206+diomonogatari@users.noreply.github.com> Date: Sun, 8 Feb 2026 15:57:13 +0000 Subject: [PATCH 50/50] refactor: update .gitattributes to ensure shell scripts maintain LF line endings --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitattributes b/.gitattributes index fbdb6a0..fde4c26 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,5 @@ # Normalize all text files to CRLF on checkout (matches .editorconfig end_of_line = crlf) * text=auto eol=crlf + +# Shell scripts must keep LF to run on Linux/macOS +*.sh text eol=lf