diff --git a/.ai/00-confirmation.md b/.ai/00-confirmation.md index edb6640..ad5cc01 100644 --- a/.ai/00-confirmation.md +++ b/.ai/00-confirmation.md @@ -1,5 +1,5 @@ -AI INSTRUCTION SET: - -CONFIRMATION REQUIREMENT: - -To ensure you understand my prompt, I need a piece of confirmation from you. Before any tool use and after any tool use, I need you to give me a confidence level on a scale of 0 to 10 on the tool use helping with the project. Remember to do this every time you are using a tool. +AI INSTRUCTION SET: + +CONFIRMATION REQUIREMENT: + +To ensure you understand my prompt, I need a piece of confirmation from you. Before any tool use and after any tool use, I need you to give me a confidence level on a scale of 0 to 10 on the tool use helping with the project. Remember to do this every time you are using a tool. diff --git a/.ai/01-user.md b/.ai/01-user.md index f295287..6159776 100644 --- a/.ai/01-user.md +++ b/.ai/01-user.md @@ -1,18 +1,18 @@ -USER: -- Name: Steven T. Cramer -- Projects (Author): - - https://github.com/TimeWarpEngineering/timewarp-state - - https://github.com/TimeWarpEngineering/timewarp-architecture - - https://github.com/TimeWarpEngineering/timewarp-fixie - - https://github.com/TimeWarpEngineering/timewarp-options-validation - - https://github.com/TimeWarpEngineering/timewarp-source-generators -- Focus Areas: - - State Management - - Blazor - - Clean Architecture - - Domain-Driven Design - - Test-Driven Development -- Preferred Patterns: - - CQRS -- Language Preferences: +USER: +- Name: Steven T. Cramer +- Projects (Author): + - https://github.com/TimeWarpEngineering/timewarp-state + - https://github.com/TimeWarpEngineering/timewarp-architecture + - https://github.com/TimeWarpEngineering/timewarp-fixie + - https://github.com/TimeWarpEngineering/timewarp-options-validation + - https://github.com/TimeWarpEngineering/timewarp-source-generators +- Focus Areas: + - State Management + - Blazor + - Clean Architecture + - Domain-Driven Design + - Test-Driven Development +- Preferred Patterns: + - CQRS +- Language Preferences: - TypeScript over JavaScript \ No newline at end of file diff --git a/.ai/02-development-process.md b/.ai/02-development-process.md index 941c293..da83ce2 100644 --- a/.ai/02-development-process.md +++ b/.ai/02-development-process.md @@ -1,26 +1,26 @@ -DEVELOPMENT PROCESS: - -KANBAN STRUCTURE: -- Track work using Kanban tasks -- Folders: - - Kanban/Backlog/ - - Kanban/ToDo/ - - Kanban/InProgress/ - - Kanban/Done/ - -TASK MANAGEMENT: -- Task Template Location: `Kanban\Task-Template.md` -- Task File Format: _.md - ✓ `002_Create-Game-Logic.md` - -COMMIT CONVENTIONS: -- Make git commits between steps -- Format: Task: = - ✓ `Task: 002 = Complete Create Game Logic` - -TASK WORKFLOW: -✓ Example of proper task movement: -```pwsh -git mv Kanban/InProgress/002_Create-Game-Logic.md Kanban/Done/002_Create-Game-Logic.md -git commit -m "Task: 002 = Complete Create Game Logic" -``` +DEVELOPMENT PROCESS: + +KANBAN STRUCTURE: +- Track work using Kanban tasks +- Folders: + - Kanban/Backlog/ + - Kanban/ToDo/ + - Kanban/InProgress/ + - Kanban/Done/ + +TASK MANAGEMENT: +- Task Template Location: `Kanban\Task-Template.md` +- Task File Format: _.md + ✓ `002_Create-Game-Logic.md` + +COMMIT CONVENTIONS: +- Make git commits between steps +- Format: Task: = + ✓ `Task: 002 = Complete Create Game Logic` + +TASK WORKFLOW: +✓ Example of proper task movement: +```pwsh +git mv Kanban/InProgress/002_Create-Game-Logic.md Kanban/Done/002_Create-Game-Logic.md +git commit -m "Task: 002 = Complete Create Game Logic" +``` diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 6b4224a..c22f07d 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -1,13 +1,13 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "fixie.console": { - "version": "4.1.0", - "commands": [ - "fixie" - ], - "rollForward": false - } - } +{ + "version": 1, + "isRoot": true, + "tools": { + "fixie.console": { + "version": "4.1.0", + "commands": [ + "fixie" + ], + "rollForward": false + } + } } \ No newline at end of file diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..6b0fa59 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,298 @@ +# EditorConfig is awesome:http://EditorConfig.org +# For dotnet and Csharp specific see below +# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference + +# top-most EditorConfig file +root = true + +#### Core EditorConfig Options #### + +# So code cleanup will not run on save. +[_Imports.cs] +generated_code = true + +[*.csproj] +generated_code = true + +[*] +# Indentation and spacing +indent_size = 2 +indent_style = space +tab_width = 2 + +# New line preferences +end_of_line = lf +insert_final_newline = true + +# ReSharper properties +resharper_html_attribute_indent = single_indent +resharper_convert_to_primary_constructor_highlighting = false + +# Development files +[*.{cs,csx,cshtml,csproj,razor,sln,props,targets,json,yml,gitignore,}] +charset = "utf-8" +trim_trailing_whitespace = true + +#### .NET Coding Conventions #### +[*.cs] + +# 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:silent +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_property = false:silent + +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent + +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent + +# Expression-level preferences +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_namespace_match_folder = false +dotnet_style_null_propagation = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_collection_expression = true:suggestion +# dotnet_style_prefer_collection_expression = when_types_loosely_match # dotnet 9 +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion + +# Field preferences +dotnet_style_readonly_field = true:suggestion + +# Parameter preferences +dotnet_code_quality_unused_parameters = all:suggestion + +# Suppression preferences +dotnet_remove_unnecessary_suppression_exclusions = none + +# New line preferences +dotnet_style_allow_multiple_blank_lines_experimental = false:warning +dotnet_style_allow_statement_immediately_after_block_experimental = false:warning + +#### C# Coding Conventions #### + +# var preferences +csharp_style_var_elsewhere = false:warning +csharp_style_var_for_built_in_types = false:warning +csharp_style_var_when_type_is_apparent = true:warning + +# Expression-bodied members +csharp_style_expression_bodied_accessors = when_on_single_line:warning +csharp_style_expression_bodied_constructors = false:warning +csharp_style_expression_bodied_indexers = when_on_single_line:warning +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = false:suggestion +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_operators = when_on_single_line:warning +csharp_style_expression_bodied_properties = when_on_single_line:warning + +# Pattern Matching +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +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:suggestion + +# Modifier preferences +csharp_prefer_static_local_function = true:suggestion +csharp_preferred_modifier_order = public, private, protected, internal, static, extern, new, virtual, abstract, sealed, override, readonly, unsafe, volatile, async:suggestion +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Code-block preferences +csharp_prefer_braces = when-multiline:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:error +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = false +csharp_style_prefer_top_level_statements = false + +# Expression-level preferences +csharp_prefer_simple_default_expression = true +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true:suggestion +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:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:silent + +# 'using' directive preferences +csharp_using_directive_placement = inside_namespace:warning + +#### 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 + +# Interfaces should begin with an I and be PascalCase +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 = interface_style + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_style.interface_style.required_prefix = I +dotnet_naming_style.interface_style.required_suffix = +dotnet_naming_style.interface_style.word_separator = +dotnet_naming_style.interface_style.capitalization = pascal_case + +# Types should be PascalCase +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 = types_style + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_style.types_style.capitalization = pascal_case + +# Non-private static fields are PascalCase +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.symbols = non_private_static_fields +dotnet_naming_rule.non_private_static_fields_should_be_pascal_case.style = non_private_static_field_style + +dotnet_naming_symbols.non_private_static_fields.applicable_kinds = field +dotnet_naming_symbols.non_private_static_fields.applicable_accessibilities = public, protected, internal, protected internal, private protected +dotnet_naming_symbols.non_private_static_fields.required_modifiers = static + +dotnet_naming_style.non_private_static_field_style.capitalization = pascal_case + +# Constants are PascalCase +dotnet_naming_rule.constants_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants +dotnet_naming_rule.constants_should_be_pascal_case.style = constant_style + +dotnet_naming_symbols.constants.applicable_kinds = field, local +dotnet_naming_symbols.constants.required_modifiers = const + +dotnet_naming_style.constant_style.capitalization = pascal_case + +# Static fields are PascalCase +dotnet_naming_rule.static_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.static_fields_should_be_pascal_case.symbols = static_fields +dotnet_naming_rule.static_fields_should_be_pascal_case.style = static_field_style + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static + +dotnet_naming_style.static_field_style.capitalization = pascal_case + +# local variables should be camelCase +dotnet_naming_rule.camel_case_for_local_variables.severity = suggestion +dotnet_naming_rule.camel_case_for_local_variables.symbols = local_variables +dotnet_naming_rule.camel_case_for_local_variables.style = local_variables_style + +dotnet_naming_symbols.local_variables.applicable_kinds = local + +dotnet_naming_style.local_variables_style.capitalization = camel_case + +# Local functions are PascalCase +dotnet_naming_rule.local_functions_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.local_functions_should_be_pascal_case.symbols = local_functions +dotnet_naming_rule.local_functions_should_be_pascal_case.style = local_function_style + +dotnet_naming_symbols.local_functions.applicable_kinds = local_function + +dotnet_naming_style.local_function_style.capitalization = pascal_case + + +#### Analyizer settings #### +dotnet_code_quality.null_check_validation_methods = NotNull + +# CA1308: Normalize strings to uppercase +dotnet_diagnostic.ca1308.severity = none + +# Global Usings Analyzer + +dotnet_diagnostic.globalusingsanalyzer0001.filename = GlobalUsings.cs +dotnet_diagnostic.globalusingsanalyzer0002.enabled = true +dotnet_diagnostic.globalusingsanalyzer0003.severity = warning diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1400c7e --- /dev/null +++ b/.gitattributes @@ -0,0 +1,52 @@ +# Enforce LF line endings for all files across all platforms +# Modern Windows tools support LF, so there's no need for CRLF conversion + +# Default behavior: LF line endings for all text files +* text=auto eol=lf + +# Specific file types - ensure LF line endings +*.cs text eol=lf +*.csproj text eol=lf +*.sln text eol=lf +*.json text eol=lf +*.js text eol=lf +*.ts text eol=lf +*.tsx text eol=lf +*.jsx text eol=lf +*.html text eol=lf +*.css text eol=lf +*.scss text eol=lf +*.sass text eol=lf +*.xml text eol=lf +*.yml text eol=lf +*.yaml text eol=lf +*.md text eol=lf +*.txt text eol=lf +*.ps1 text eol=lf +*.sh text eol=lf +*.dockerfile text eol=lf +*.gitignore text eol=lf +*.gitattributes text eol=lf + +# Configuration files +*.config text eol=lf +*.props text eol=lf +*.targets text eol=lf +*.editorconfig text eol=lf + +# Source control files +.gitignore text eol=lf +.gitattributes text eol=lf + +# Binary files - don't convert line endings +*.dll binary +*.exe binary +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.pdf binary +*.zip binary +*.nupkg binary +*.snupkg binary \ No newline at end of file diff --git a/.github/scripts/sync-config.yml b/.github/scripts/sync-config.yml index 3b81f0b..08fe0c7 100644 --- a/.github/scripts/sync-config.yml +++ b/.github/scripts/sync-config.yml @@ -1,101 +1,101 @@ -# Configuration for sync-configurable-files workflow -# This file defines which files should be synced from the parent TimeWarp Architecture repository - -# Source repository configuration -source_repo: TimeWarpEngineering/timewarp-architecture -source_branch: master - -# Repository type - determines which file sets to sync -repository_type: dotnet - -# Files to sync from the parent repository -# Each entry specifies the source path and destination path -sync_files: - # .NET Build Configuration Files - - source_path: TimeWarp.Architecture/Directory.Build.props - destination_path: Directory.Build.props - merge_strategy: replace_with_local_preservation - preserve_sections: - - "" - - "" - - "" - - - source_path: TimeWarp.Architecture/Directory.Build.targets - destination_path: Directory.Build.targets - merge_strategy: replace - create_if_missing: true - - - source_path: TimeWarp.Architecture/global.json - destination_path: global.json - merge_strategy: replace - create_if_missing: true - - # Development Configuration Files - - source_path: TimeWarp.Architecture/.editorconfig - destination_path: .editorconfig - merge_strategy: replace - create_if_missing: true - - # GitIgnore with special merge handling - - source_path: TimeWarp.Architecture/.gitignore - destination_path: .gitignore - merge_strategy: merge_sections - section_markers: - global_start: "# ----- Global .gitignore (synced from TimeWarp Architecture) -----" - global_end: "# ----- Repository-specific .gitignore -----" - local_start: "# ----- Repository-specific .gitignore -----" - - # GitHub Workflow Templates (optional - only if they exist in parent) - - source_path: TimeWarp.Architecture/.github/workflows/build.yml - destination_path: .github/workflows/build.yml - merge_strategy: replace - create_if_missing: false - optional: true - - - source_path: TimeWarp.Architecture/.github/workflows/codeql.yml - destination_path: .github/workflows/codeql.yml - merge_strategy: replace - create_if_missing: false - optional: true - -# Exclusion patterns - files that should never be synced -exclude_patterns: - - "*.user" - - "*.suo" - - "**/bin/**" - - "**/obj/**" - - "appsettings.*.json" - - "launchSettings.json" - -# Local preservation rules -preserve_local: - # Preserve local package metadata and version info in Directory.Build.props - - file: Directory.Build.props - sections: - - xpath: "//PropertyGroup[Description or PackageProjectUrl or RepositoryUrl]" - - xpath: "//PropertyGroup[VersionPrefix or VersionSuffix or AssemblyVersion]" - - xpath: "//ItemGroup[None[@Include and contains(@Include, 'Logo')]]" - - # Preserve local .gitignore additions - - file: .gitignore - preserve_after_marker: "# ----- Repository-specific .gitignore -----" - -# Post-sync validation -validation: - # Ensure these files compile/parse correctly after sync - build_check: true - powershell_syntax_check: true - - # Files that must exist after sync - required_files: - - Directory.Build.props - - .gitignore - -# Notification settings -notifications: - create_pr: true - pr_reviewers: [] # Add default reviewers if needed - pr_labels: - - "automation" - - "configuration" +# Configuration for sync-configurable-files workflow +# This file defines which files should be synced from the parent TimeWarp Architecture repository + +# Source repository configuration +source_repo: TimeWarpEngineering/timewarp-architecture +source_branch: master + +# Repository type - determines which file sets to sync +repository_type: dotnet + +# Files to sync from the parent repository +# Each entry specifies the source path and destination path +sync_files: + # .NET Build Configuration Files + - source_path: TimeWarp.Architecture/Directory.Build.props + destination_path: Directory.Build.props + merge_strategy: replace_with_local_preservation + preserve_sections: + - "" + - "" + - "" + + - source_path: TimeWarp.Architecture/Directory.Build.targets + destination_path: Directory.Build.targets + merge_strategy: replace + create_if_missing: true + + - source_path: TimeWarp.Architecture/global.json + destination_path: global.json + merge_strategy: replace + create_if_missing: true + + # Development Configuration Files + - source_path: TimeWarp.Architecture/.editorconfig + destination_path: .editorconfig + merge_strategy: replace + create_if_missing: true + + # GitIgnore with special merge handling + - source_path: TimeWarp.Architecture/.gitignore + destination_path: .gitignore + merge_strategy: merge_sections + section_markers: + global_start: "# ----- Global .gitignore (synced from TimeWarp Architecture) -----" + global_end: "# ----- Repository-specific .gitignore -----" + local_start: "# ----- Repository-specific .gitignore -----" + + # GitHub Workflow Templates (optional - only if they exist in parent) + - source_path: TimeWarp.Architecture/.github/workflows/build.yml + destination_path: .github/workflows/build.yml + merge_strategy: replace + create_if_missing: false + optional: true + + - source_path: TimeWarp.Architecture/.github/workflows/codeql.yml + destination_path: .github/workflows/codeql.yml + merge_strategy: replace + create_if_missing: false + optional: true + +# Exclusion patterns - files that should never be synced +exclude_patterns: + - "*.user" + - "*.suo" + - "**/bin/**" + - "**/obj/**" + - "appsettings.*.json" + - "launchSettings.json" + +# Local preservation rules +preserve_local: + # Preserve local package metadata and version info in Directory.Build.props + - file: Directory.Build.props + sections: + - xpath: "//PropertyGroup[Description or PackageProjectUrl or RepositoryUrl]" + - xpath: "//PropertyGroup[VersionPrefix or VersionSuffix or AssemblyVersion]" + - xpath: "//ItemGroup[None[@Include and contains(@Include, 'Logo')]]" + + # Preserve local .gitignore additions + - file: .gitignore + preserve_after_marker: "# ----- Repository-specific .gitignore -----" + +# Post-sync validation +validation: + # Ensure these files compile/parse correctly after sync + build_check: true + powershell_syntax_check: true + + # Files that must exist after sync + required_files: + - Directory.Build.props + - .gitignore + +# Notification settings +notifications: + create_pr: true + pr_reviewers: [] # Add default reviewers if needed + pr_labels: + - "automation" + - "configuration" - "chore" \ No newline at end of file diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index ff8b45f..c14cd21 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -1,45 +1,45 @@ -name: Build and Test - -on: - pull_request: - workflow_dispatch: - -env: - DOTNET_NOLOGO: true # Disable the .NET logo in the console output - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable the .NET first time experience to skip caching NuGet packages and speed up the build - DOTNET_CLI_TELEMETRY_OPTOUT: true - NUGET_AUTH_TOKEN: ${{secrets.PUBLISH_TO_NUGET_ORG}} # <-- This is the token for the GitHub account you want to use. - -jobs: - build: - runs-on: ubuntu-latest - steps: - - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." - - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" - - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." - - - name: Check out repository code - uses: actions/checkout@v4 - - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." - - - name: List files in the repository - run: | - ls ${{ github.workspace }} - - - name: Setup .NET - uses: actions/setup-dotnet@v3 - with: - dotnet-version: 9.0.x - - - name: Build - run: | - cd Source/TimeWarp.QuickBooks/ - dotnet build --configuration Debug - shell: pwsh - - - name: Run Tests - run: | - cd Tests/TimeWarp.QuickBooks.Tests/ - dotnet test --configuration Debug - - - run: echo "🍏 This job's status is ${{ job.status }}." +name: Build and Test + +on: + pull_request: + workflow_dispatch: + +env: + DOTNET_NOLOGO: true # Disable the .NET logo in the console output + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true # Disable the .NET first time experience to skip caching NuGet packages and speed up the build + DOTNET_CLI_TELEMETRY_OPTOUT: true + NUGET_AUTH_TOKEN: ${{secrets.PUBLISH_TO_NUGET_ORG}} # <-- This is the token for the GitHub account you want to use. + +jobs: + build: + runs-on: ubuntu-latest + steps: + - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." + - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" + - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." + + - name: Check out repository code + uses: actions/checkout@v4 + - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." + + - name: List files in the repository + run: | + ls ${{ github.workspace }} + + - name: Setup .NET + uses: actions/setup-dotnet@v3 + with: + dotnet-version: 9.0.x + + - name: Build + run: | + cd Source/TimeWarp.QuickBooks/ + dotnet build --configuration Debug + shell: pwsh + + - name: Run Tests + run: | + cd Tests/TimeWarp.QuickBooks.Tests/ + dotnet test --configuration Debug + + - run: echo "🍏 This job's status is ${{ job.status }}." diff --git a/.github/workflows/release-build.yml b/.github/workflows/release-build.yml index bd51403..18455cd 100644 --- a/.github/workflows/release-build.yml +++ b/.github/workflows/release-build.yml @@ -1,63 +1,63 @@ -name: Build and Deploy - -on: - push: - branches: - - master - paths: - - 'Source/**' - - 'Tests/**' - - '.github/workflows/release-build.yml' - - 'Directory.Build.props' - - '*.props' - - '*.targets' - release: - types: [created] - workflow_dispatch: - -env: - DOTNET_NOLOGO: true - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - DOTNET_CLI_TELEMETRY_OPTOUT: true - NUGET_AUTH_TOKEN: ${{secrets.PUBLISH_TO_NUGET_ORG}} - -jobs: - build: - runs-on: ubuntu-latest - defaults: - run: - shell: pwsh - steps: - - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." - - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" - - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." - - - name: Check out repository code - uses: actions/checkout@v4 - - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." - - - name: Cache NuGet packages - uses: actions/cache@v4 - with: - path: ~/.nuget/packages - key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} - restore-keys: | - ${{ runner.os }}-nuget- - - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: '9.0.x' - - - name: Build and Pack - run: | - cd Source/TimeWarp.QuickBooks/ - dotnet build --configuration Release - dotnet pack --configuration Release --output ./bin/Packages - - - name: Publish TimeWarp.QuickBooks - run: | - cd Source/TimeWarp.QuickBooks/bin/Packages - dotnet nuget push TimeWarp.QuickBooks.*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{secrets.PUBLISH_TO_NUGET_ORG}} - - - run: echo "🍏 This job's status is ${{ job.status }}." +name: Build and Deploy + +on: + push: + branches: + - master + paths: + - 'Source/**' + - 'Tests/**' + - '.github/workflows/release-build.yml' + - 'Directory.Build.props' + - '*.props' + - '*.targets' + release: + types: [created] + workflow_dispatch: + +env: + DOTNET_NOLOGO: true + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + NUGET_AUTH_TOKEN: ${{secrets.PUBLISH_TO_NUGET_ORG}} + +jobs: + build: + runs-on: ubuntu-latest + defaults: + run: + shell: pwsh + steps: + - run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event." + - run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!" + - run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}." + + - name: Check out repository code + uses: actions/checkout@v4 + - run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner." + + - name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/packages.lock.json') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: '9.0.x' + + - name: Build and Pack + run: | + cd Source/TimeWarp.QuickBooks/ + dotnet build --configuration Release + dotnet pack --configuration Release --output ./bin/Packages + + - name: Publish TimeWarp.QuickBooks + run: | + cd Source/TimeWarp.QuickBooks/bin/Packages + dotnet nuget push TimeWarp.QuickBooks.*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{secrets.PUBLISH_TO_NUGET_ORG}} + + - run: echo "🍏 This job's status is ${{ job.status }}." diff --git a/.idea/.idea.timewarp-quickbooks/.idea/.gitignore b/.idea/.idea.timewarp-quickbooks/.idea/.gitignore index 6f020fb..d28ad3b 100644 --- a/.idea/.idea.timewarp-quickbooks/.idea/.gitignore +++ b/.idea/.idea.timewarp-quickbooks/.idea/.gitignore @@ -1,13 +1,13 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Rider ignored files -/.idea.timewarp-quickbooks.iml -/modules.xml -/contentModel.xml -/projectSettingsUpdater.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/.idea.timewarp-quickbooks.iml +/modules.xml +/contentModel.xml +/projectSettingsUpdater.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.timewarp-quickbooks/.idea/indexLayout.xml b/.idea/.idea.timewarp-quickbooks/.idea/indexLayout.xml index f5a863a..7b08163 100644 --- a/.idea/.idea.timewarp-quickbooks/.idea/indexLayout.xml +++ b/.idea/.idea.timewarp-quickbooks/.idea/indexLayout.xml @@ -1,8 +1,8 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.timewarp-quickbooks/.idea/vcs.xml b/.idea/.idea.timewarp-quickbooks/.idea/vcs.xml index c8397c9..35eb1dd 100644 --- a/.idea/.idea.timewarp-quickbooks/.idea/vcs.xml +++ b/.idea/.idea.timewarp-quickbooks/.idea/vcs.xml @@ -1,6 +1,6 @@ - - - - - + + + + + \ No newline at end of file diff --git a/CLAUDE.md b/CLAUDE.md index 14f6b7b..3b815fe 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,78 +1,78 @@ -# CLAUDE.md - -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Project Overview - -TimeWarp.QuickBooks is a .NET 9.0 library for QuickBooks Online integration. It provides OAuth authentication and data access capabilities for the QuickBooks API using the official Intuit SDK (`IppDotNetSdkForQuickBooksApiV3`). - -## Common Commands - -### Build and Test -- **Run tests**: `pwsh RunTests.ps1` or `dotnet fixie Tests/TimeWarp.QuickBooks.Tests` -- **Build solution**: `dotnet build timewarp-quickbooks.slnx` -- **Run sample app**: `cd Samples/TimeWarp.QuickBooks.Sample.Web && dotnet run` - -### Project Structure -- **Main library**: `Source/TimeWarp.QuickBooks/` -- **Tests**: `Tests/TimeWarp.QuickBooks.Tests/` -- **Sample application**: `Samples/TimeWarp.QuickBooks.Sample.Web/` - -## Architecture - -### Core Components - -**Authentication Layer** (`Source/TimeWarp.QuickBooks/Authentication/`): -- `IQuickBooksOAuthService`: Interface for OAuth operations (authorization URL generation, token handling, callback processing) -- `QuickBooksOAuthService`: Implementation using Intuit's OAuth2Client -- `Models/`: OAuth-related models including `QuickBooksTokens`, `QuickBooksOAuthOptions`, and `QuickBooksOAuthCallbackResult` - -**Service Registration**: -- `ServiceCollectionExtensions.cs`: Contains `AddQuickBooksOAuth()` extension method for DI registration - -### Key Patterns - -**Token Management**: The service uses an in-memory dictionary for token storage. Production implementations should replace `TokenStore` with persistent storage. - -**OAuth Flow**: Standard OAuth 2.0 flow with state verification for CSRF protection. Supports both sandbox and production environments. - -**Configuration**: Uses .NET Options pattern with `QuickBooksOAuthOptions` for environment-specific settings. - -### Testing Framework - -Uses **TimeWarp.Fixie** testing framework with: -- `[TestTag]` attributes for test categorization (Fast, Slow, etc.) -- `[Input]` attributes for parameterized tests -- `[Skip]` attributes for disabled tests -- Shouldly assertions - -### Dependencies - -**Main Library**: -- `IppDotNetSdkForQuickBooksApiV3`: Official Intuit QuickBooks SDK - -**Test Project**: -- `TimeWarp.Fixie`: Custom testing framework -- `Shouldly`: Assertion library -- `Fixie.TestAdapter`: Test runner integration - -## Configuration - -**Global Settings** (`Directory.Build.props`): -- Target Framework: .NET 9.0 -- Nullable reference types enabled -- Warnings treated as errors -- Assembly version: 0.1.2.0 - -**Sample App Configuration**: -- QuickBooks OAuth settings in `appsettings.json` -- Environment support (Sandbox/Production) -- Scope configuration for QuickBooks permissions - -## Development Notes - -**Kanban Workflow**: Project uses Kanban methodology with tasks organized in `Kanban/` directory (ToDo, InProgress, Done). - -**Documentation**: Comprehensive documentation in `Documentation/` with separate User and Developer sections. - +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +TimeWarp.QuickBooks is a .NET 9.0 library for QuickBooks Online integration. It provides OAuth authentication and data access capabilities for the QuickBooks API using the official Intuit SDK (`IppDotNetSdkForQuickBooksApiV3`). + +## Common Commands + +### Build and Test +- **Run tests**: `pwsh RunTests.ps1` or `dotnet fixie Tests/TimeWarp.QuickBooks.Tests` +- **Build solution**: `dotnet build timewarp-quickbooks.slnx` +- **Run sample app**: `cd Samples/TimeWarp.QuickBooks.Sample.Web && dotnet run` + +### Project Structure +- **Main library**: `Source/TimeWarp.QuickBooks/` +- **Tests**: `Tests/TimeWarp.QuickBooks.Tests/` +- **Sample application**: `Samples/TimeWarp.QuickBooks.Sample.Web/` + +## Architecture + +### Core Components + +**Authentication Layer** (`Source/TimeWarp.QuickBooks/Authentication/`): +- `IQuickBooksOAuthService`: Interface for OAuth operations (authorization URL generation, token handling, callback processing) +- `QuickBooksOAuthService`: Implementation using Intuit's OAuth2Client +- `Models/`: OAuth-related models including `QuickBooksTokens`, `QuickBooksOAuthOptions`, and `QuickBooksOAuthCallbackResult` + +**Service Registration**: +- `ServiceCollectionExtensions.cs`: Contains `AddQuickBooksOAuth()` extension method for DI registration + +### Key Patterns + +**Token Management**: The service uses an in-memory dictionary for token storage. Production implementations should replace `TokenStore` with persistent storage. + +**OAuth Flow**: Standard OAuth 2.0 flow with state verification for CSRF protection. Supports both sandbox and production environments. + +**Configuration**: Uses .NET Options pattern with `QuickBooksOAuthOptions` for environment-specific settings. + +### Testing Framework + +Uses **TimeWarp.Fixie** testing framework with: +- `[TestTag]` attributes for test categorization (Fast, Slow, etc.) +- `[Input]` attributes for parameterized tests +- `[Skip]` attributes for disabled tests +- Shouldly assertions + +### Dependencies + +**Main Library**: +- `IppDotNetSdkForQuickBooksApiV3`: Official Intuit QuickBooks SDK + +**Test Project**: +- `TimeWarp.Fixie`: Custom testing framework +- `Shouldly`: Assertion library +- `Fixie.TestAdapter`: Test runner integration + +## Configuration + +**Global Settings** (`Directory.Build.props`): +- Target Framework: .NET 9.0 +- Nullable reference types enabled +- Warnings treated as errors +- Assembly version: 0.1.2.0 + +**Sample App Configuration**: +- QuickBooks OAuth settings in `appsettings.json` +- Environment support (Sandbox/Production) +- Scope configuration for QuickBooks permissions + +## Development Notes + +**Kanban Workflow**: Project uses Kanban methodology with tasks organized in `Kanban/` directory (ToDo, InProgress, Done). + +**Documentation**: Comprehensive documentation in `Documentation/` with separate User and Developer sections. + **CI/CD**: GitHub Actions workflows for CI builds and NuGet publishing (requires `PUBLISH_TO_NUGET_ORG` secret). \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index d289264..67b6430 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,67 +1,67 @@ - - - - - - net9.0 - enable - enable - latest - true - - - - - - TimeWarp Engineering - TimeWarp Engineering - TimeWarp.QuickBooks - Copyright © TimeWarp Engineering $([System.DateTime]::Now.ToString('yyyy')) - en-US - - - - - QuickBooks Online API integration with OAuth authentication and data access capabilities. - https://github.com/TimeWarpEngineering/timewarp-quickbooks - https://github.com/TimeWarpEngineering/timewarp-quickbooks - git - TimeWarp;QuickBooks - MIT - Logo.png - README.md - - - - - true - true - true - snupkg - true - - - - - true - $(NoWarn);CS1591 - - - - - 0.1.2 - alpha - 0.1.2.0 - 0.1.2.0 - - - - - - - + + + + + + net9.0 + enable + enable + latest + true + + + + + + TimeWarp Engineering + TimeWarp Engineering + TimeWarp.QuickBooks + Copyright © TimeWarp Engineering $([System.DateTime]::Now.ToString('yyyy')) + en-US + + + + + QuickBooks Online API integration with OAuth authentication and data access capabilities. + https://github.com/TimeWarpEngineering/timewarp-quickbooks + https://github.com/TimeWarpEngineering/timewarp-quickbooks + git + TimeWarp;QuickBooks + MIT + Logo.png + README.md + + + + + true + true + true + snupkg + true + + + + + true + $(NoWarn);CS1591 + + + + + 0.1.2 + alpha + 0.1.2.0 + 0.1.2.0 + + + + + + + \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props index bc19d92..02baaaf 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,20 +1,20 @@ - - - - - true - - - - - - - - - - - + + + + + true + + + + + + + + + + + \ No newline at end of file diff --git a/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/Examples/Overview.md b/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/Examples/Overview.md index 49bdaab..9f9db95 100644 --- a/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/Examples/Overview.md +++ b/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/Examples/Overview.md @@ -1,25 +1,25 @@ -# Architectural Decision Log - -This log lists the architectural decisions for TimeWarp Architecture. - - - -- [ADR-0000](0000-use-markdown-architectural-decision-records.md) - Use Markdown Architectural Decision Records -- [ADR-0001](0001-use-CC0-as-license.md) - Use CC0 as license -- [ADR-0002](0002-do-not-use-numbers-in-headings.md) - Do not use numbers in headings -- [ADR-0003](0003-include-in-adr-tools.md) - Include in adr-tools -- [ADR-0004](0004-write-own-toc-tool.md) - Write own TOC tool -- [ADR-0005](0005-use-dashes-in-filenames.md) - Use dashes in filenames -- [ADR-0006](0006-use-names-as-identifier.md) - Use names as identifier -- [ADR-0007](0007-do-not-emphasize-line-headings.md) - Do not emphasize line headings -- [ADR-0008](0008-add-status-field.md) - Add status field -- [ADR-0009](0009-support-links-between-adrs-inside-an-adrs.md) - Support links between ADRs inside an ADRs -- [ADR-0010](0010-support-categories.md) - Support categories -- [ADR-0011](0011-use-asterisk-as-list-marker.md) - Use asterisk as list marker -- [ADR-0012](0012-use-curly-brackets-to-denote-placeholder.md) - Use curly brackets to denote placeholders - - - -For new ADRs, please use [adr-template.md](adr-template.md) as basis. -More information on MADR is available at . -General information about architectural decision records is available at . +# Architectural Decision Log + +This log lists the architectural decisions for TimeWarp Architecture. + + + +- [ADR-0000](0000-use-markdown-architectural-decision-records.md) - Use Markdown Architectural Decision Records +- [ADR-0001](0001-use-CC0-as-license.md) - Use CC0 as license +- [ADR-0002](0002-do-not-use-numbers-in-headings.md) - Do not use numbers in headings +- [ADR-0003](0003-include-in-adr-tools.md) - Include in adr-tools +- [ADR-0004](0004-write-own-toc-tool.md) - Write own TOC tool +- [ADR-0005](0005-use-dashes-in-filenames.md) - Use dashes in filenames +- [ADR-0006](0006-use-names-as-identifier.md) - Use names as identifier +- [ADR-0007](0007-do-not-emphasize-line-headings.md) - Do not emphasize line headings +- [ADR-0008](0008-add-status-field.md) - Add status field +- [ADR-0009](0009-support-links-between-adrs-inside-an-adrs.md) - Support links between ADRs inside an ADRs +- [ADR-0010](0010-support-categories.md) - Support categories +- [ADR-0011](0011-use-asterisk-as-list-marker.md) - Use asterisk as list marker +- [ADR-0012](0012-use-curly-brackets-to-denote-placeholder.md) - Use curly brackets to denote placeholders + + + +For new ADRs, please use [adr-template.md](adr-template.md) as basis. +More information on MADR is available at . +General information about architectural decision records is available at . diff --git a/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/Overview.md b/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/Overview.md index 49bdaab..9f9db95 100644 --- a/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/Overview.md +++ b/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/Overview.md @@ -1,25 +1,25 @@ -# Architectural Decision Log - -This log lists the architectural decisions for TimeWarp Architecture. - - - -- [ADR-0000](0000-use-markdown-architectural-decision-records.md) - Use Markdown Architectural Decision Records -- [ADR-0001](0001-use-CC0-as-license.md) - Use CC0 as license -- [ADR-0002](0002-do-not-use-numbers-in-headings.md) - Do not use numbers in headings -- [ADR-0003](0003-include-in-adr-tools.md) - Include in adr-tools -- [ADR-0004](0004-write-own-toc-tool.md) - Write own TOC tool -- [ADR-0005](0005-use-dashes-in-filenames.md) - Use dashes in filenames -- [ADR-0006](0006-use-names-as-identifier.md) - Use names as identifier -- [ADR-0007](0007-do-not-emphasize-line-headings.md) - Do not emphasize line headings -- [ADR-0008](0008-add-status-field.md) - Add status field -- [ADR-0009](0009-support-links-between-adrs-inside-an-adrs.md) - Support links between ADRs inside an ADRs -- [ADR-0010](0010-support-categories.md) - Support categories -- [ADR-0011](0011-use-asterisk-as-list-marker.md) - Use asterisk as list marker -- [ADR-0012](0012-use-curly-brackets-to-denote-placeholder.md) - Use curly brackets to denote placeholders - - - -For new ADRs, please use [adr-template.md](adr-template.md) as basis. -More information on MADR is available at . -General information about architectural decision records is available at . +# Architectural Decision Log + +This log lists the architectural decisions for TimeWarp Architecture. + + + +- [ADR-0000](0000-use-markdown-architectural-decision-records.md) - Use Markdown Architectural Decision Records +- [ADR-0001](0001-use-CC0-as-license.md) - Use CC0 as license +- [ADR-0002](0002-do-not-use-numbers-in-headings.md) - Do not use numbers in headings +- [ADR-0003](0003-include-in-adr-tools.md) - Include in adr-tools +- [ADR-0004](0004-write-own-toc-tool.md) - Write own TOC tool +- [ADR-0005](0005-use-dashes-in-filenames.md) - Use dashes in filenames +- [ADR-0006](0006-use-names-as-identifier.md) - Use names as identifier +- [ADR-0007](0007-do-not-emphasize-line-headings.md) - Do not emphasize line headings +- [ADR-0008](0008-add-status-field.md) - Add status field +- [ADR-0009](0009-support-links-between-adrs-inside-an-adrs.md) - Support links between ADRs inside an ADRs +- [ADR-0010](0010-support-categories.md) - Support categories +- [ADR-0011](0011-use-asterisk-as-list-marker.md) - Use asterisk as list marker +- [ADR-0012](0012-use-curly-brackets-to-denote-placeholder.md) - Use curly brackets to denote placeholders + + + +For new ADRs, please use [adr-template.md](adr-template.md) as basis. +More information on MADR is available at . +General information about architectural decision records is available at . diff --git a/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/adr-template.md b/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/adr-template.md index fabb1cc..a47f18c 100644 --- a/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/adr-template.md +++ b/Documentation/Developer/Conceptual/ArchitecturalDecisionRecords/adr-template.md @@ -1,75 +1,75 @@ -# {short title of solved problem and solution} - -* Status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)} -* Architect: {primary decision maker} -* Consulted: {list of people whose inputs were considered} -* Date: {YYYY-MM-DD when the decision was last updated} - -Technical Story: {description | ticket/issue URL} - -## Context and Problem Statement - -{Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.} - -## Decision Drivers - -* {driver 1, e.g., a force, facing concern, …} -* {driver 2, e.g., a force, facing concern, …} -* … - -## Considered Options - -* {option 1} -* {option 2} -* {option 3} -* … - -## Decision Outcome - -Chosen option: "{option 1}", because {justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}. - -### Positive Consequences - -* {e.g., improvement of quality attribute satisfaction, follow-up decisions required, …} -* … - -### Negative Consequences - -* {e.g., compromising quality attribute, follow-up decisions required, …} -* … - -## Pros and Cons of the Options - -### {option 1} - -{example | description | pointer to more information | …} - -* Good, because {argument a} -* Good, because {argument b} -* Bad, because {argument c} -* … - -### {option 2} - -{example | description | pointer to more information | …} - -* Good, because {argument a} -* Good, because {argument b} -* Bad, because {argument c} -* … - -### {option 3} - -{example | description | pointer to more information | …} - -* Good, because {argument a} -* Good, because {argument b} -* Bad, because {argument c} -* … - -## Links - -* {Link type} {Link to ADR} -* … - - +# {short title of solved problem and solution} + +* Status: {proposed | rejected | accepted | deprecated | … | superseded by [ADR-0005](0005-example.md)} +* Architect: {primary decision maker} +* Consulted: {list of people whose inputs were considered} +* Date: {YYYY-MM-DD when the decision was last updated} + +Technical Story: {description | ticket/issue URL} + +## Context and Problem Statement + +{Describe the context and problem statement, e.g., in free form using two to three sentences. You may want to articulate the problem in form of a question.} + +## Decision Drivers + +* {driver 1, e.g., a force, facing concern, …} +* {driver 2, e.g., a force, facing concern, …} +* … + +## Considered Options + +* {option 1} +* {option 2} +* {option 3} +* … + +## Decision Outcome + +Chosen option: "{option 1}", because {justification. e.g., only option, which meets k.o. criterion decision driver | which resolves force {force} | … | comes out best (see below)}. + +### Positive Consequences + +* {e.g., improvement of quality attribute satisfaction, follow-up decisions required, …} +* … + +### Negative Consequences + +* {e.g., compromising quality attribute, follow-up decisions required, …} +* … + +## Pros and Cons of the Options + +### {option 1} + +{example | description | pointer to more information | …} + +* Good, because {argument a} +* Good, because {argument b} +* Bad, because {argument c} +* … + +### {option 2} + +{example | description | pointer to more information | …} + +* Good, because {argument a} +* Good, because {argument b} +* Bad, because {argument c} +* … + +### {option 3} + +{example | description | pointer to more information | …} + +* Good, because {argument a} +* Good, because {argument b} +* Bad, because {argument c} +* … + +## Links + +* {Link type} {Link to ADR} +* … + + diff --git a/Documentation/Developer/Conceptual/Overview.md b/Documentation/Developer/Conceptual/Overview.md index dc534e6..6f8f22a 100644 --- a/Documentation/Developer/Conceptual/Overview.md +++ b/Documentation/Developer/Conceptual/Overview.md @@ -1,3 +1,3 @@ -# Conceptual Documentation - -Most useful when studying. +# Conceptual Documentation + +Most useful when studying. diff --git a/Documentation/Developer/Conceptual/ProjectGenesis.md b/Documentation/Developer/Conceptual/ProjectGenesis.md index 0933057..90f39da 100644 --- a/Documentation/Developer/Conceptual/ProjectGenesis.md +++ b/Documentation/Developer/Conceptual/ProjectGenesis.md @@ -1,63 +1,63 @@ -# Build QuickBooks Authentication and Token Management Service - -## Description - -Create a TimeWarp.QuickBooks library that includes QuickBooks Authentication and Token Management service using the IppDotNetSdkForQuickBooksApiV3 NuGet package. This service will handle OAuth authentication with QuickBooks, manage access tokens, refresh tokens, and provide a secure interface for other components to interact with QuickBooks API. - -## Requirements - -- Implement OAuth 2.0 authentication flow for QuickBooks -- Securely store and manage access tokens and refresh tokens -- Handle token expiration and automatic refresh -- Provide a clean API for other services to use when interacting with QuickBooks -- Implement proper error handling and logging -- Follow best practices for security when handling authentication credentials - -## Checklist - -### Design -- [ ] Create service architecture design -- [ ] Define interfaces for the authentication and token management service -- [ ] Plan token storage strategy - -### Implementation -- [ ] Add IppDotNetSdkForQuickBooksApiV3 NuGet package dependency -- [ ] Implement OAuth authentication flow -- [ ] Create token storage and retrieval mechanism -- [ ] Implement token refresh logic -- [ ] Create service interfaces for other components to use -- [ ] Add proper error handling and logging - -### Testing -- [ ] Create unit tests for authentication flow -- [ ] Test token refresh mechanism -- [ ] Test error handling scenarios - -### Documentation -- [ ] Document authentication flow -- [ ] Document service API usage -- [ ] Add configuration instructions - -### Review -- [ ] Consider Security Implications of token storage -- [ ] Review error handling and recovery mechanisms -- [ ] Code Review - -## Notes - -The IppDotNetSdkForQuickBooksApiV3 NuGet package provides the foundation for interacting with QuickBooks API. This task focuses on building a service layer that handles authentication and token management, which will be used by other components that need to interact with QuickBooks. - -Key considerations: -- OAuth 2.0 flow requires user consent, so the service should handle redirects and callbacks -- Tokens have limited lifetimes and need to be refreshed -- Secure storage of tokens is critical -- The service should be designed to be reusable across different parts of the application -- For the spike phase, token persistence will use file storage -- In the future, persistence will be migrated to Entity Framework and PostgreSQL - - -# References - -https://grok.com/chat/25e1a264-0092-4649-b45a-826d6644881c (Genesis chat) - +# Build QuickBooks Authentication and Token Management Service + +## Description + +Create a TimeWarp.QuickBooks library that includes QuickBooks Authentication and Token Management service using the IppDotNetSdkForQuickBooksApiV3 NuGet package. This service will handle OAuth authentication with QuickBooks, manage access tokens, refresh tokens, and provide a secure interface for other components to interact with QuickBooks API. + +## Requirements + +- Implement OAuth 2.0 authentication flow for QuickBooks +- Securely store and manage access tokens and refresh tokens +- Handle token expiration and automatic refresh +- Provide a clean API for other services to use when interacting with QuickBooks +- Implement proper error handling and logging +- Follow best practices for security when handling authentication credentials + +## Checklist + +### Design +- [ ] Create service architecture design +- [ ] Define interfaces for the authentication and token management service +- [ ] Plan token storage strategy + +### Implementation +- [ ] Add IppDotNetSdkForQuickBooksApiV3 NuGet package dependency +- [ ] Implement OAuth authentication flow +- [ ] Create token storage and retrieval mechanism +- [ ] Implement token refresh logic +- [ ] Create service interfaces for other components to use +- [ ] Add proper error handling and logging + +### Testing +- [ ] Create unit tests for authentication flow +- [ ] Test token refresh mechanism +- [ ] Test error handling scenarios + +### Documentation +- [ ] Document authentication flow +- [ ] Document service API usage +- [ ] Add configuration instructions + +### Review +- [ ] Consider Security Implications of token storage +- [ ] Review error handling and recovery mechanisms +- [ ] Code Review + +## Notes + +The IppDotNetSdkForQuickBooksApiV3 NuGet package provides the foundation for interacting with QuickBooks API. This task focuses on building a service layer that handles authentication and token management, which will be used by other components that need to interact with QuickBooks. + +Key considerations: +- OAuth 2.0 flow requires user consent, so the service should handle redirects and callbacks +- Tokens have limited lifetimes and need to be refreshed +- Secure storage of tokens is critical +- The service should be designed to be reusable across different parts of the application +- For the spike phase, token persistence will use file storage +- In the future, persistence will be migrated to Entity Framework and PostgreSQL + + +# References + +https://grok.com/chat/25e1a264-0092-4649-b45a-826d6644881c (Genesis chat) + https://grok.com/chat/c62c8a98-3183-4f81-8ba6-97aa1bbf5b50 \ No newline at end of file diff --git a/Documentation/Developer/Reference/Glossary.md b/Documentation/Developer/Reference/Glossary.md index 4f4dd98..b9f972e 100644 --- a/Documentation/Developer/Reference/Glossary.md +++ b/Documentation/Developer/Reference/Glossary.md @@ -1,3 +1,3 @@ -# Glossary - +# Glossary + **Dependents**: In a NuGet package dependency graph, the packages that rely on a given package. A package with many dependents is one that other packages depend on, serving as a foundational or widely-used component in the ecosystem. \ No newline at end of file diff --git a/Documentation/Developer/Reference/Overview.md b/Documentation/Developer/Reference/Overview.md index 7cddf2c..bc775a9 100644 --- a/Documentation/Developer/Reference/Overview.md +++ b/Documentation/Developer/Reference/Overview.md @@ -1,5 +1,5 @@ -# Reference Documentation - -As a rule this is written in /// comments on the code. - -Swagger Docs are an example of this. +# Reference Documentation + +As a rule this is written in /// comments on the code. + +Swagger Docs are an example of this. diff --git a/Documentation/Overview.md b/Documentation/Overview.md index 40cfe8e..474bd51 100644 --- a/Documentation/Overview.md +++ b/Documentation/Overview.md @@ -1,47 +1,47 @@ -# TimeWarp.Architecture -TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. - -## Documentation -* Please see [TimeWarp.Architecture documentation](https://todo/your-docs) for - -## Getting Started -TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: -### Installation process - -### Software dependencies - -* [dotnet core sdk] (https://dotnet.microsoft.com/download/dotnet-core/3.0) - -### Latest releases - -### API references - - -## Run - -To run your site, execute the `run.ps1` script from powershell. - -## Contribute - -TODO: Explain how other users and developers can contribute to make your code better. -Contributions to TimeWarp.Architecture are welcome. Here is how you can contribute to TimeWarp.Architecture: - -* [Submit bugs](https://todo/your-repo/issues) -* [Submit pull requests](https://todo/your-repo/pulls) for bug fixes and features and discuss existing proposals - -Please refer to [Contribution Guidelines](CONTRIBUTING.md) for more details. - -## License - -Code licensed under the [TODO: License](Link). - -## Roadmap - -For details on our planned features and future direction please refer to our [Roadmap](TODO:Link to your roadmap). - -## Contact Us - -If you have questions about TimeWarp.Architecture, or you would like to reach out to us about an issue you're having or for development advice as you work on a TimeWarp.Architecture issue, you can reach us as follows: - -* Open an issue and prefix the issue title with [Question]. See Question tag for already-opened questions. -* Discuss TimeWarp.Architecture with the team and the community on Discord/Telegram/Slack/Teams . +# TimeWarp.Architecture +TODO: Give a short introduction of your project. Let this section explain the objectives or the motivation behind this project. + +## Documentation +* Please see [TimeWarp.Architecture documentation](https://todo/your-docs) for + +## Getting Started +TODO: Guide users through getting your code up and running on their own system. In this section you can talk about: +### Installation process + +### Software dependencies + +* [dotnet core sdk] (https://dotnet.microsoft.com/download/dotnet-core/3.0) + +### Latest releases + +### API references + + +## Run + +To run your site, execute the `run.ps1` script from powershell. + +## Contribute + +TODO: Explain how other users and developers can contribute to make your code better. +Contributions to TimeWarp.Architecture are welcome. Here is how you can contribute to TimeWarp.Architecture: + +* [Submit bugs](https://todo/your-repo/issues) +* [Submit pull requests](https://todo/your-repo/pulls) for bug fixes and features and discuss existing proposals + +Please refer to [Contribution Guidelines](CONTRIBUTING.md) for more details. + +## License + +Code licensed under the [TODO: License](Link). + +## Roadmap + +For details on our planned features and future direction please refer to our [Roadmap](TODO:Link to your roadmap). + +## Contact Us + +If you have questions about TimeWarp.Architecture, or you would like to reach out to us about an issue you're having or for development advice as you work on a TimeWarp.Architecture issue, you can reach us as follows: + +* Open an issue and prefix the issue title with [Question]. See Question tag for already-opened questions. +* Discuss TimeWarp.Architecture with the team and the community on Discord/Telegram/Slack/Teams . diff --git a/Documentation/User/Overview.md b/Documentation/User/Overview.md index f486a5b..5eb9d55 100644 --- a/Documentation/User/Overview.md +++ b/Documentation/User/Overview.md @@ -1 +1 @@ -# Users and Administrators +# Users and Administrators diff --git a/Kanban/Backlog/Scratch/Overview.md b/Kanban/Backlog/Scratch/Overview.md index c2bde1f..e1f5752 100644 --- a/Kanban/Backlog/Scratch/Overview.md +++ b/Kanban/Backlog/Scratch/Overview.md @@ -1 +1 @@ -# Scratch pad for Kanban board stuff. +# Scratch pad for Kanban board stuff. diff --git a/Kanban/Done/001_Build-TimeWarp-QuickBooks-Tests.md b/Kanban/Done/001_Build-TimeWarp-QuickBooks-Tests.md index 20165ac..1a9541e 100644 --- a/Kanban/Done/001_Build-TimeWarp-QuickBooks-Tests.md +++ b/Kanban/Done/001_Build-TimeWarp-QuickBooks-Tests.md @@ -1,120 +1,120 @@ -# 001 - Build TimeWarp.QuickBooks.Tests Project - -## Description - -Create a new test project called TimeWarp.QuickBooks.Tests using the TimeWarp.Fixie testing framework. This will establish the foundation for all testing in the QuickBooks project. - -## Requirements - -- Set up a proper test project structure following TimeWarp conventions -- Implement TimeWarp.Fixie testing framework -- Include sample ConventionTests.cs file - -## Checklist - -### Design -- [x] Add/Update Tests - -### Implementation -- [x] Create new test project TimeWarp.QuickBooks.Tests -- [x] Add required NuGet packages (TimeWarp.Fixie, Fixie.TestAdapter, Shouldly) -- [x] Create dotnet tool manifest and install Fixie.Console -- [x] Configure TestingConvention class -- [x] Add ConventionTests.cs example file -- [x] Verify tests run successfully -- [x] Move project to Tests directory - -### Documentation -- [x] Update Documentation - -## Notes - -Reference: [TimeWarp.Fixie GitHub Repository](https://github.com/TimeWarpEngineering/timewarp-fixie) - -## Implementation Notes - -The TimeWarp.QuickBooks.Tests project has been successfully created and configured: - -1. Created a new classlib project named TimeWarp.QuickBooks.Tests -2. Added the required NuGet packages: - - TimeWarp.Fixie - - Fixie.TestAdapter - - Shouldly -3. Created a dotnet tool manifest and installed Fixie.Console -4. Created the TestingConvention class that inherits from TimeWarp.Fixie.TestingConvention -5. Added the ConventionTests.cs example file with various test examples: - - Basic test - - Test with Skip attribute - - Test with TestTag attribute - - Parameterized tests using Input attribute -6. Verified tests run successfully with the following results: - - 4 passed tests - - 1 skipped test (as expected with the Skip attribute) - - Test execution completed in 0.33 seconds -7. Moved the project to the Tests directory to follow project structure conventions - -## Implementation Steps - -1. Create a new test project: - ```console - dotnet new classlib -n TimeWarp.QuickBooks.Tests - ``` - -2. Add NuGet packages to the project: - ```console - dotnet add package TimeWarp.Fixie - dotnet add package Fixie.TestAdapter - dotnet add package Shouldly - ``` - -3. Create a dotnet tool manifest: - ```console - dotnet new tool-manifest - ``` - -4. Add Fixie.Console to the manifest: - ```console - dotnet tool install Fixie.Console - ``` - -5. Create TestingConvention class: - ```csharp - namespace TimeWarp.QuickBooks.Tests; - - public class TestingConvention : TimeWarp.Fixie.TestingConvention { } - ``` - -6. Add ConventionTests.cs example file: - ```csharp - namespace TimeWarp.QuickBooks.Tests; - - using Shouldly; - using TimeWarp.Fixie; - - [TestTag(TestTags.Fast)] - public class SimpleNoApplicationTest_Should_ - { - public static void AlwaysPass() => true.ShouldBeTrue(); - - [Skip("Demonstrates skip attribute")] - public static void SkipExample() => true.ShouldBeFalse(); - - [TestTag(TestTags.Fast)] - public static void TagExample() => true.ShouldBeTrue(); - - [Input(5, 3, 2)] - [Input(8, 5, 3)] - public static void Subtract(int x, int y, int expectedDifference) - { - int result = x - y; - result.ShouldBe(expectedDifference); - } - } - ``` - -7. Run tests to verify setup: - ```console - dotnet fixie - ``` - +# 001 - Build TimeWarp.QuickBooks.Tests Project + +## Description + +Create a new test project called TimeWarp.QuickBooks.Tests using the TimeWarp.Fixie testing framework. This will establish the foundation for all testing in the QuickBooks project. + +## Requirements + +- Set up a proper test project structure following TimeWarp conventions +- Implement TimeWarp.Fixie testing framework +- Include sample ConventionTests.cs file + +## Checklist + +### Design +- [x] Add/Update Tests + +### Implementation +- [x] Create new test project TimeWarp.QuickBooks.Tests +- [x] Add required NuGet packages (TimeWarp.Fixie, Fixie.TestAdapter, Shouldly) +- [x] Create dotnet tool manifest and install Fixie.Console +- [x] Configure TestingConvention class +- [x] Add ConventionTests.cs example file +- [x] Verify tests run successfully +- [x] Move project to Tests directory + +### Documentation +- [x] Update Documentation + +## Notes + +Reference: [TimeWarp.Fixie GitHub Repository](https://github.com/TimeWarpEngineering/timewarp-fixie) + +## Implementation Notes + +The TimeWarp.QuickBooks.Tests project has been successfully created and configured: + +1. Created a new classlib project named TimeWarp.QuickBooks.Tests +2. Added the required NuGet packages: + - TimeWarp.Fixie + - Fixie.TestAdapter + - Shouldly +3. Created a dotnet tool manifest and installed Fixie.Console +4. Created the TestingConvention class that inherits from TimeWarp.Fixie.TestingConvention +5. Added the ConventionTests.cs example file with various test examples: + - Basic test + - Test with Skip attribute + - Test with TestTag attribute + - Parameterized tests using Input attribute +6. Verified tests run successfully with the following results: + - 4 passed tests + - 1 skipped test (as expected with the Skip attribute) + - Test execution completed in 0.33 seconds +7. Moved the project to the Tests directory to follow project structure conventions + +## Implementation Steps + +1. Create a new test project: + ```console + dotnet new classlib -n TimeWarp.QuickBooks.Tests + ``` + +2. Add NuGet packages to the project: + ```console + dotnet add package TimeWarp.Fixie + dotnet add package Fixie.TestAdapter + dotnet add package Shouldly + ``` + +3. Create a dotnet tool manifest: + ```console + dotnet new tool-manifest + ``` + +4. Add Fixie.Console to the manifest: + ```console + dotnet tool install Fixie.Console + ``` + +5. Create TestingConvention class: + ```csharp + namespace TimeWarp.QuickBooks.Tests; + + public class TestingConvention : TimeWarp.Fixie.TestingConvention { } + ``` + +6. Add ConventionTests.cs example file: + ```csharp + namespace TimeWarp.QuickBooks.Tests; + + using Shouldly; + using TimeWarp.Fixie; + + [TestTag(TestTags.Fast)] + public class SimpleNoApplicationTest_Should_ + { + public static void AlwaysPass() => true.ShouldBeTrue(); + + [Skip("Demonstrates skip attribute")] + public static void SkipExample() => true.ShouldBeFalse(); + + [TestTag(TestTags.Fast)] + public static void TagExample() => true.ShouldBeTrue(); + + [Input(5, 3, 2)] + [Input(8, 5, 3)] + public static void Subtract(int x, int y, int expectedDifference) + { + int result = x - y; + result.ShouldBe(expectedDifference); + } + } + ``` + +7. Run tests to verify setup: + ```console + dotnet fixie + ``` + 8. Move project to Tests directory to follow project structure conventions \ No newline at end of file diff --git a/Kanban/Done/002_Create-TimeWarp-QuickBooks-Library.md b/Kanban/Done/002_Create-TimeWarp-QuickBooks-Library.md index e8ee14f..26d9e33 100644 --- a/Kanban/Done/002_Create-TimeWarp-QuickBooks-Library.md +++ b/Kanban/Done/002_Create-TimeWarp-QuickBooks-Library.md @@ -1,44 +1,44 @@ -# 002 - Create TimeWarp.QuickBooks Library - -## Description - -Create a new class library project called TimeWarp.QuickBooks in the Source directory. This will establish the foundation for the QuickBooks integration functionality. - -## Requirements - -- Create a proper class library structure following TimeWarp conventions -- Set up the project in the Source directory - -## Checklist - -### Implementation -- [x] Navigate to the Source directory -- [x] Create new class library project TimeWarp.QuickBooks -- [x] Verify project structure -- [x] Add initial project files as needed - -### Documentation -- [x] Update Documentation as needed - -## Notes - -The class library should be created using the following command from the Source directory: -```console -dotnet new classlib -n TimeWarp.QuickBooks -``` - -## Implementation Notes - -The TimeWarp.QuickBooks class library has been successfully created: - -1. Navigated to the Source directory -2. Created a new class library project using the command: - ```console - dotnet new classlib -n TimeWarp.QuickBooks - ``` -3. Verified the project structure: - - TimeWarp.QuickBooks.csproj with .NET 9.0 target framework - - Default Class1.cs file created - - Project structure follows standard .NET class library conventions - +# 002 - Create TimeWarp.QuickBooks Library + +## Description + +Create a new class library project called TimeWarp.QuickBooks in the Source directory. This will establish the foundation for the QuickBooks integration functionality. + +## Requirements + +- Create a proper class library structure following TimeWarp conventions +- Set up the project in the Source directory + +## Checklist + +### Implementation +- [x] Navigate to the Source directory +- [x] Create new class library project TimeWarp.QuickBooks +- [x] Verify project structure +- [x] Add initial project files as needed + +### Documentation +- [x] Update Documentation as needed + +## Notes + +The class library should be created using the following command from the Source directory: +```console +dotnet new classlib -n TimeWarp.QuickBooks +``` + +## Implementation Notes + +The TimeWarp.QuickBooks class library has been successfully created: + +1. Navigated to the Source directory +2. Created a new class library project using the command: + ```console + dotnet new classlib -n TimeWarp.QuickBooks + ``` +3. Verified the project structure: + - TimeWarp.QuickBooks.csproj with .NET 9.0 target framework + - Default Class1.cs file created + - Project structure follows standard .NET class library conventions + The class library is now ready for further development and integration with QuickBooks functionality. \ No newline at end of file diff --git a/Kanban/Done/003.1_Define-IQuickBooksOAuthService-Interface.md b/Kanban/Done/003.1_Define-IQuickBooksOAuthService-Interface.md index bbaec14..6c0344e 100644 --- a/Kanban/Done/003.1_Define-IQuickBooksOAuthService-Interface.md +++ b/Kanban/Done/003.1_Define-IQuickBooksOAuthService-Interface.md @@ -1,41 +1,41 @@ -# Task 003.1: Define IQuickBooksOAuthService Interface - -## Description - -Define the IQuickBooksOAuthService interface that will handle the OAuth authentication flow for QuickBooks. This interface will be part of the TimeWarp.QuickBooks library and will provide a clean API for authenticating with QuickBooks. - -## Parent -003_Create-QuickBooks-Auth-and-Token-Service - -## Requirements - -- Define a clean, well-documented interface for OAuth authentication with QuickBooks -- Include methods for initiating the OAuth flow, handling callbacks, and retrieving tokens -- Follow best practices for interface design -- Ensure the interface is testable - -## Checklist - -### Design -- [x] Research QuickBooks OAuth 2.0 flow requirements -- [x] Define interface methods and properties -- [x] Document interface with XML comments - -### Implementation -- [x] Create interface file in appropriate namespace -- [x] Add proper XML documentation - -### Testing -- [x] Plan test approach for implementations of this interface - -## Notes - -The IQuickBooksOAuthService interface should be designed with the following considerations: -- OAuth 2.0 flow requires user consent, so the interface should include methods for generating authorization URLs and handling callbacks -- The interface should be independent of token storage concerns -- Methods should be async where appropriate -- The interface should be designed to be easily mockable for testing - -## Implementation Notes - +# Task 003.1: Define IQuickBooksOAuthService Interface + +## Description + +Define the IQuickBooksOAuthService interface that will handle the OAuth authentication flow for QuickBooks. This interface will be part of the TimeWarp.QuickBooks library and will provide a clean API for authenticating with QuickBooks. + +## Parent +003_Create-QuickBooks-Auth-and-Token-Service + +## Requirements + +- Define a clean, well-documented interface for OAuth authentication with QuickBooks +- Include methods for initiating the OAuth flow, handling callbacks, and retrieving tokens +- Follow best practices for interface design +- Ensure the interface is testable + +## Checklist + +### Design +- [x] Research QuickBooks OAuth 2.0 flow requirements +- [x] Define interface methods and properties +- [x] Document interface with XML comments + +### Implementation +- [x] Create interface file in appropriate namespace +- [x] Add proper XML documentation + +### Testing +- [x] Plan test approach for implementations of this interface + +## Notes + +The IQuickBooksOAuthService interface should be designed with the following considerations: +- OAuth 2.0 flow requires user consent, so the interface should include methods for generating authorization URLs and handling callbacks +- The interface should be independent of token storage concerns +- Methods should be async where appropriate +- The interface should be designed to be easily mockable for testing + +## Implementation Notes + This task focuses only on defining the interface, not implementing it. Implementation will be handled in a separate task. \ No newline at end of file diff --git a/Kanban/Done/003.3_Create-Sample-QuickBooks-OAuth-Application.md b/Kanban/Done/003.3_Create-Sample-QuickBooks-OAuth-Application.md index bc06699..9a9856b 100644 --- a/Kanban/Done/003.3_Create-Sample-QuickBooks-OAuth-Application.md +++ b/Kanban/Done/003.3_Create-Sample-QuickBooks-OAuth-Application.md @@ -1,55 +1,55 @@ -# Task 003.3: Create Sample QuickBooks OAuth Application - -## Description - -Create a sample application that demonstrates the usage of the QuickBooksOAuthService. This application will serve as both a testing ground and a reference implementation for the OAuth flow with QuickBooks. - -## Parent -003_Create-QuickBooks-Auth-and-Token-Service - -## Requirements - -- Create a simple web application that uses the QuickBooksOAuthService -- Implement the full OAuth flow including authorization and callback handling -- Provide a clean UI for initiating the OAuth process and displaying results -- Include proper error handling and user feedback -- Document the sample application usage - -## Checklist - -### Design -- [ ] Design the application architecture -- [ ] Plan the user interface flow -- [ ] Define the integration points with QuickBooksOAuthService - -### Implementation -- [ ] Create a new web application project -- [ ] Add references to the TimeWarp.QuickBooks library -- [ ] Implement controllers/pages for initiating OAuth -- [ ] Implement callback handling -- [ ] Add error handling and user feedback -- [ ] Configure the application for QuickBooks API access - -### Testing -- [ ] Test the OAuth flow end-to-end -- [ ] Test error scenarios -- [ ] Verify proper user feedback - -### Documentation -- [ ] Document the application setup process -- [ ] Create usage instructions -- [ ] Add comments explaining key integration points - -## Notes - -This sample application will serve multiple purposes: -1. Demonstrate the proper usage of the QuickBooksOAuthService -2. Provide a testing ground for the OAuth flow -3. Serve as a reference implementation for other applications -4. Help identify any issues or improvements needed in the service - -The application should be simple but complete, focusing on the OAuth flow rather than additional features. Consider using ASP.NET Core for the web application. - -## Implementation Notes - +# Task 003.3: Create Sample QuickBooks OAuth Application + +## Description + +Create a sample application that demonstrates the usage of the QuickBooksOAuthService. This application will serve as both a testing ground and a reference implementation for the OAuth flow with QuickBooks. + +## Parent +003_Create-QuickBooks-Auth-and-Token-Service + +## Requirements + +- Create a simple web application that uses the QuickBooksOAuthService +- Implement the full OAuth flow including authorization and callback handling +- Provide a clean UI for initiating the OAuth process and displaying results +- Include proper error handling and user feedback +- Document the sample application usage + +## Checklist + +### Design +- [ ] Design the application architecture +- [ ] Plan the user interface flow +- [ ] Define the integration points with QuickBooksOAuthService + +### Implementation +- [ ] Create a new web application project +- [ ] Add references to the TimeWarp.QuickBooks library +- [ ] Implement controllers/pages for initiating OAuth +- [ ] Implement callback handling +- [ ] Add error handling and user feedback +- [ ] Configure the application for QuickBooks API access + +### Testing +- [ ] Test the OAuth flow end-to-end +- [ ] Test error scenarios +- [ ] Verify proper user feedback + +### Documentation +- [ ] Document the application setup process +- [ ] Create usage instructions +- [ ] Add comments explaining key integration points + +## Notes + +This sample application will serve multiple purposes: +1. Demonstrate the proper usage of the QuickBooksOAuthService +2. Provide a testing ground for the OAuth flow +3. Serve as a reference implementation for other applications +4. Help identify any issues or improvements needed in the service + +The application should be simple but complete, focusing on the OAuth flow rather than additional features. Consider using ASP.NET Core for the web application. + +## Implementation Notes + This task is dependent on the completion of Task 003.2 which implements the QuickBooksOAuthService. \ No newline at end of file diff --git a/Kanban/Done/004_Create-Readme.md b/Kanban/Done/004_Create-Readme.md index 96c85f1..68b0567 100644 --- a/Kanban/Done/004_Create-Readme.md +++ b/Kanban/Done/004_Create-Readme.md @@ -1,63 +1,63 @@ -# Create a Comprehensive README - -## Description - -Create a detailed and professional README for the timewarp-quickbooks project. Use the following repositories as examples for structure, content, and style: - -- [timewarp-source-generators](https://github.com/TimeWarpEngineering/timewarp-source-generators/blob/master/README.md) - -The new README should provide clear guidance to users and contributors, following the conventions and best practices demonstrated in the example repositories. - -## Requirements - -- The README must include, at minimum, the following sections: - - Project Title and Badges - - Overview / Introduction - - Features - - Getting Started (installation, setup, prerequisites) - - Usage (with code examples if applicable) - - Contributing - - License - - References to related TimeWarpEngineering projects -- Match the structure, formatting, and tone of the example repositories. -- Include project badges (build, license, NuGet, etc.) as appropriate. -- Ensure clarity, professionalism, and completeness. - -## Checklist - -### Documentation -- [x] Draft README content -- [x] Review structure and completeness against example repositories -- [x] Add project badges -- [x] Proofread for clarity and accuracy - -### Review -- [x] Code Review (if scripts or code snippets are added) -- [x] Consider Accessibility Implications -- [x] Consider Security Implications - -## Notes - -- Example repositories for reference: - - https://github.com/TimeWarpEngineering/timewarp-fixie - - https://github.com/TimeWarpEngineering/timewarp-state - -## Implementation Notes - -README has been created and updated to follow the structure and format of the TimeWarp.SourceGenerators repository. The README now includes: - -1. All required badges at the top (Dotnet, Stars, Discord, Build Status, NuGet version/downloads, Issues, Forks, License, Twitter) -2. Twitter follow badges for project maintainers -3. Clear project title and description -4. Star request section -5. Features list -6. Getting started section -7. Installation instructions with code example -8. Documentation section with links -9. Development process section with testing instructions -10. Contributing section -11. Unlicense section with badge -12. Contact information with Discord link -13. Acknowledgments section - +# Create a Comprehensive README + +## Description + +Create a detailed and professional README for the timewarp-quickbooks project. Use the following repositories as examples for structure, content, and style: + +- [timewarp-source-generators](https://github.com/TimeWarpEngineering/timewarp-source-generators/blob/master/README.md) + +The new README should provide clear guidance to users and contributors, following the conventions and best practices demonstrated in the example repositories. + +## Requirements + +- The README must include, at minimum, the following sections: + - Project Title and Badges + - Overview / Introduction + - Features + - Getting Started (installation, setup, prerequisites) + - Usage (with code examples if applicable) + - Contributing + - License + - References to related TimeWarpEngineering projects +- Match the structure, formatting, and tone of the example repositories. +- Include project badges (build, license, NuGet, etc.) as appropriate. +- Ensure clarity, professionalism, and completeness. + +## Checklist + +### Documentation +- [x] Draft README content +- [x] Review structure and completeness against example repositories +- [x] Add project badges +- [x] Proofread for clarity and accuracy + +### Review +- [x] Code Review (if scripts or code snippets are added) +- [x] Consider Accessibility Implications +- [x] Consider Security Implications + +## Notes + +- Example repositories for reference: + - https://github.com/TimeWarpEngineering/timewarp-fixie + - https://github.com/TimeWarpEngineering/timewarp-state + +## Implementation Notes + +README has been created and updated to follow the structure and format of the TimeWarp.SourceGenerators repository. The README now includes: + +1. All required badges at the top (Dotnet, Stars, Discord, Build Status, NuGet version/downloads, Issues, Forks, License, Twitter) +2. Twitter follow badges for project maintainers +3. Clear project title and description +4. Star request section +5. Features list +6. Getting started section +7. Installation instructions with code example +8. Documentation section with links +9. Development process section with testing instructions +10. Contributing section +11. Unlicense section with badge +12. Contact information with Discord link +13. Acknowledgments section + All content matches the style, tone, and formatting of the example repository. \ No newline at end of file diff --git a/Kanban/Done/005_Create-GitHub-Workflow-for-NuGet-Publish.md b/Kanban/Done/005_Create-GitHub-Workflow-for-NuGet-Publish.md index 4e1b308..37d3f93 100644 --- a/Kanban/Done/005_Create-GitHub-Workflow-for-NuGet-Publish.md +++ b/Kanban/Done/005_Create-GitHub-Workflow-for-NuGet-Publish.md @@ -1,64 +1,64 @@ -# Create GitHub Workflow for NuGet Publish - -## Description - -Create a GitHub Actions workflow to automatically build and publish the TimeWarp.QuickBooks library to NuGet.org. The workflow should follow best practices for .NET library publishing and securely handle secrets. - -## Requirements - -- Create a workflow YAML file in `.github/workflows/` (e.g., `publish-nuget.yml`) -- The workflow must: - - Trigger on push to master or on new release/tag - - Build the library using the correct .NET SDK version (target net9.0) - - Pack the NuGet package - - Publish to NuGet.org using a secure API key stored in GitHub Secrets - - Only publish when version changes or on release -- Reference similar workflows from other TimeWarpEngineering projects for consistency -- Document the workflow in the README or a separate documentation file - -## Checklist - -### Implementation -- [x] Create workflow YAML file -- [x] Configure triggers (push to master, release, or tag) -- [x] Build and pack the library -- [x] Publish to NuGet.org using GitHub Secrets -- [ ] Test the workflow in a dry run (if possible) - -### Documentation -- [x] Update documentation to describe the workflow and required secrets - -### Review -- [ ] Code Review -- [x] Consider Security Implications (secrets, permissions) -- [x] Consider Performance Implications - -## Notes - -- Reference TimeWarpEngineering workflows for best practices -- Use the workflow from https://github.com/TimeWarpEngineering/timewarp-source-generators/ repository as an example - -## Implementation Notes - -1. Created two GitHub workflow files based on the TimeWarpEngineering/timewarp-source-generators repository: - - `.github/workflows/ci-build.yml`: For continuous integration testing on pull requests - - `.github/workflows/release-build.yml`: For building and publishing to NuGet - -2. CI Build Workflow: - - Triggers on pull requests and manual workflow dispatch - - Sets up .NET 9.0 environment - - Builds the TimeWarp.QuickBooks library - - Runs tests - -3. Release Build Workflow: - - Triggers on push to master branch, new releases, and manual workflow dispatch - - Sets up .NET 9.0 environment - - Builds and packs the TimeWarp.QuickBooks library - - Publishes the package to NuGet.org using PUBLISH_TO_NUGET_ORG secret - -4. Updated README.md with: - - GitHub Actions build badge - - Documentation on CI/CD pipeline - - Required GitHub secrets explanation - +# Create GitHub Workflow for NuGet Publish + +## Description + +Create a GitHub Actions workflow to automatically build and publish the TimeWarp.QuickBooks library to NuGet.org. The workflow should follow best practices for .NET library publishing and securely handle secrets. + +## Requirements + +- Create a workflow YAML file in `.github/workflows/` (e.g., `publish-nuget.yml`) +- The workflow must: + - Trigger on push to master or on new release/tag + - Build the library using the correct .NET SDK version (target net9.0) + - Pack the NuGet package + - Publish to NuGet.org using a secure API key stored in GitHub Secrets + - Only publish when version changes or on release +- Reference similar workflows from other TimeWarpEngineering projects for consistency +- Document the workflow in the README or a separate documentation file + +## Checklist + +### Implementation +- [x] Create workflow YAML file +- [x] Configure triggers (push to master, release, or tag) +- [x] Build and pack the library +- [x] Publish to NuGet.org using GitHub Secrets +- [ ] Test the workflow in a dry run (if possible) + +### Documentation +- [x] Update documentation to describe the workflow and required secrets + +### Review +- [ ] Code Review +- [x] Consider Security Implications (secrets, permissions) +- [x] Consider Performance Implications + +## Notes + +- Reference TimeWarpEngineering workflows for best practices +- Use the workflow from https://github.com/TimeWarpEngineering/timewarp-source-generators/ repository as an example + +## Implementation Notes + +1. Created two GitHub workflow files based on the TimeWarpEngineering/timewarp-source-generators repository: + - `.github/workflows/ci-build.yml`: For continuous integration testing on pull requests + - `.github/workflows/release-build.yml`: For building and publishing to NuGet + +2. CI Build Workflow: + - Triggers on pull requests and manual workflow dispatch + - Sets up .NET 9.0 environment + - Builds the TimeWarp.QuickBooks library + - Runs tests + +3. Release Build Workflow: + - Triggers on push to master branch, new releases, and manual workflow dispatch + - Sets up .NET 9.0 environment + - Builds and packs the TimeWarp.QuickBooks library + - Publishes the package to NuGet.org using PUBLISH_TO_NUGET_ORG secret + +4. Updated README.md with: + - GitHub Actions build badge + - Documentation on CI/CD pipeline + - Required GitHub secrets explanation + 5. Note: The GitHub workflow requires the PUBLISH_TO_NUGET_ORG secret to be configured in the repository settings. \ No newline at end of file diff --git a/Kanban/Done/006_Extract-Common-Values-To-Directory-Build-Props.md b/Kanban/Done/006_Extract-Common-Values-To-Directory-Build-Props.md index 2ae82c6..f850cf7 100644 --- a/Kanban/Done/006_Extract-Common-Values-To-Directory-Build-Props.md +++ b/Kanban/Done/006_Extract-Common-Values-To-Directory-Build-Props.md @@ -1,90 +1,90 @@ -# 006 - Extract Common Values to Directory.Build.props and Use Directory.Packages.props - -## Description - -Extract common project values to Directory.Build.props and centralize package references using Directory.Packages.props in the timewarp-quickbooks repository. This follows TimeWarp's standard approach to .NET project organization, using the TimeWarp.SourceGenerators repository as a reference. This task improves maintainability by centralizing common properties and package versions, making updates more efficient and consistent across the entire solution. - -## Requirements - -- Create Directory.Build.props at the root level with common project properties following TimeWarp's conventions -- Create Directory.Packages.props at the root level for centralized NuGet package version management -- Update existing project files to use the centralized properties and package references -- Ensure all projects build successfully after the changes -- Include standard TimeWarp metadata (authors, repository info, etc.) - -## Checklist - -### Implementation -- [x] Create Directory.Build.props with common properties: - - [x] TargetFramework (net9.0) - - [x] ImplicitUsings (enable) - - [x] Nullable (enable) - - [x] Common assembly info (Authors, Product, etc.) - - [x] Package metadata (PackageProjectUrl, RepositoryUrl, PackageTags, etc.) - - [x] Source Link and deterministic build settings - - [x] Documentation generation settings - - [x] Version information and versioning strategy -- [x] Create Directory.Packages.props with: - - [x] All NuGet package references and their versions - - [x] Standard `ManagePackageVersionsCentrally` setting (true) -- [x] Update project files to remove redundant properties now defined in Directory.Build.props -- [x] Update project files to reference packages without explicit versions -- [x] Test building solution to verify changes work correctly - -### Documentation -- [x] Add comments in Directory.Build.props and Directory.Packages.props explaining their purpose -- [x] Update project documentation if necessary - -### Review -- [x] Consider Performance Implications (faster builds due to centralized management) -- [x] Consider Maintenance Implications (easier version upgrades) -- [x] Code Review - -## Notes - -Directory.Build.props and Directory.Packages.props are MSBuild features that allow for centralizing common build properties and package versions: - -- **Directory.Build.props**: Automatically imported by MSBuild before the project file, allowing common properties to be defined once -- **Directory.Packages.props**: Centralizes NuGet package versions when using the `ManagePackageVersionsCentrally` feature - -Based on the TimeWarp.SourceGenerators implementation, the Directory.Build.props should include: -- Common project properties (TargetFramework, ImplicitUsings, Nullable) -- Package metadata (Authors, Product, URLs, etc.) -- Build configuration settings (LangVersion, TreatWarningsAsErrors, etc.) -- Documentation generation settings -- Source Link and deterministic build settings for proper package generation - -Current packages to centralize in Directory.Packages.props: -- TimeWarp.Fixie (3.0.0) -- Fixie.TestAdapter (4.1.0) -- Shouldly (4.3.0) -- IppDotNetSdkForQuickBooksApiV3 (14.7.0) - -See TimeWarp.SourceGenerators repository: https://github.com/TimeWarpEngineering/timewarp-source-generators - -## Implementation Notes - -All required files have been successfully created and configured: - -1. Created `Directory.Build.props` with all common properties: - - Set TargetFramework to net9.0 - - Enabled ImplicitUsings and Nullable - - Added common assembly info with TimeWarp Engineering as author - - Configured package metadata including repository URLs, license, etc. - - Set up Source Link for deterministic builds - - Added documentation generation settings - - Set version information (0.1.0-alpha) - -2. Created `Directory.Packages.props` with: - - Enabled central package version management - - Added all package references with their versions: - - IppDotNetSdkForQuickBooksApiV3 (14.7.0) - - Fixie.TestAdapter (4.1.0) - - Shouldly (4.3.0) - - TimeWarp.Fixie (3.0.0) - -3. Updated project files: - - Removed redundant properties now in Directory.Build.props - - Updated package references to use centralized versions - -The solution has been tested and builds successfully with the new configuration. This implementation follows the TimeWarp standard practices, ensuring consistency across projects and simplifying future maintenance. +# 006 - Extract Common Values to Directory.Build.props and Use Directory.Packages.props + +## Description + +Extract common project values to Directory.Build.props and centralize package references using Directory.Packages.props in the timewarp-quickbooks repository. This follows TimeWarp's standard approach to .NET project organization, using the TimeWarp.SourceGenerators repository as a reference. This task improves maintainability by centralizing common properties and package versions, making updates more efficient and consistent across the entire solution. + +## Requirements + +- Create Directory.Build.props at the root level with common project properties following TimeWarp's conventions +- Create Directory.Packages.props at the root level for centralized NuGet package version management +- Update existing project files to use the centralized properties and package references +- Ensure all projects build successfully after the changes +- Include standard TimeWarp metadata (authors, repository info, etc.) + +## Checklist + +### Implementation +- [x] Create Directory.Build.props with common properties: + - [x] TargetFramework (net9.0) + - [x] ImplicitUsings (enable) + - [x] Nullable (enable) + - [x] Common assembly info (Authors, Product, etc.) + - [x] Package metadata (PackageProjectUrl, RepositoryUrl, PackageTags, etc.) + - [x] Source Link and deterministic build settings + - [x] Documentation generation settings + - [x] Version information and versioning strategy +- [x] Create Directory.Packages.props with: + - [x] All NuGet package references and their versions + - [x] Standard `ManagePackageVersionsCentrally` setting (true) +- [x] Update project files to remove redundant properties now defined in Directory.Build.props +- [x] Update project files to reference packages without explicit versions +- [x] Test building solution to verify changes work correctly + +### Documentation +- [x] Add comments in Directory.Build.props and Directory.Packages.props explaining their purpose +- [x] Update project documentation if necessary + +### Review +- [x] Consider Performance Implications (faster builds due to centralized management) +- [x] Consider Maintenance Implications (easier version upgrades) +- [x] Code Review + +## Notes + +Directory.Build.props and Directory.Packages.props are MSBuild features that allow for centralizing common build properties and package versions: + +- **Directory.Build.props**: Automatically imported by MSBuild before the project file, allowing common properties to be defined once +- **Directory.Packages.props**: Centralizes NuGet package versions when using the `ManagePackageVersionsCentrally` feature + +Based on the TimeWarp.SourceGenerators implementation, the Directory.Build.props should include: +- Common project properties (TargetFramework, ImplicitUsings, Nullable) +- Package metadata (Authors, Product, URLs, etc.) +- Build configuration settings (LangVersion, TreatWarningsAsErrors, etc.) +- Documentation generation settings +- Source Link and deterministic build settings for proper package generation + +Current packages to centralize in Directory.Packages.props: +- TimeWarp.Fixie (3.0.0) +- Fixie.TestAdapter (4.1.0) +- Shouldly (4.3.0) +- IppDotNetSdkForQuickBooksApiV3 (14.7.0) + +See TimeWarp.SourceGenerators repository: https://github.com/TimeWarpEngineering/timewarp-source-generators + +## Implementation Notes + +All required files have been successfully created and configured: + +1. Created `Directory.Build.props` with all common properties: + - Set TargetFramework to net9.0 + - Enabled ImplicitUsings and Nullable + - Added common assembly info with TimeWarp Engineering as author + - Configured package metadata including repository URLs, license, etc. + - Set up Source Link for deterministic builds + - Added documentation generation settings + - Set version information (0.1.0-alpha) + +2. Created `Directory.Packages.props` with: + - Enabled central package version management + - Added all package references with their versions: + - IppDotNetSdkForQuickBooksApiV3 (14.7.0) + - Fixie.TestAdapter (4.1.0) + - Shouldly (4.3.0) + - TimeWarp.Fixie (3.0.0) + +3. Updated project files: + - Removed redundant properties now in Directory.Build.props + - Updated package references to use centralized versions + +The solution has been tested and builds successfully with the new configuration. This implementation follows the TimeWarp standard practices, ensuring consistency across projects and simplifying future maintenance. diff --git a/Kanban/InProgress/003.2_Implement-QuickBooksOAuthService.md b/Kanban/InProgress/003.2_Implement-QuickBooksOAuthService.md index 8c5b221..6fefc6c 100644 --- a/Kanban/InProgress/003.2_Implement-QuickBooksOAuthService.md +++ b/Kanban/InProgress/003.2_Implement-QuickBooksOAuthService.md @@ -1,51 +1,51 @@ -# Task 003.2: Implement QuickBooksOAuthService - -## Description - -Implement the IQuickBooksOAuthService interface to handle OAuth authentication with QuickBooks. This implementation will use the IppDotNetSdkForQuickBooksApiV3 NuGet package to interact with the QuickBooks API for authentication. - -## Parent -003_Create-QuickBooks-Auth-and-Token-Service - -## Requirements - -- Implement the IQuickBooksOAuthService interface defined in Task 003.1 -- Use the IppDotNetSdkForQuickBooksApiV3 NuGet package for OAuth interactions -- Handle the OAuth 2.0 flow including authorization URL generation and callback processing -- Implement proper error handling and logging -- Follow best practices for security when handling authentication - -## Checklist - -### Design -- [x] Review QuickBooks OAuth 2.0 documentation -- [x] Design the implementation class structure - -### Implementation -- [x] Create the QuickBooksOAuthService class -- [x] Implement authorization URL generation -- [x] Implement callback handling -- [x] Implement error handling -- [x] Add logging -- [x] Register the service in DI container - - -## Notes - -The implementation should focus solely on the OAuth flow, not on token management or storage. Token management will be handled in a separate task. - -Key considerations: -- OAuth 2.0 flow requires user consent, so the service should handle redirects and callbacks -- The implementation should be secure and follow best practices -- Error handling should be robust and provide meaningful error messages -- Logging should be comprehensive to aid in debugging - -## Implementation Notes - -This task is dependent on the completion of Task 003.1 which defines the interface to be implemented. - -Testing for the QuickBooksOAuthService is intentionally not included in this task as it will be handled separately: -- Task 003.3 will create a sample application that serves as a testing ground for the OAuth flow -- Task 003.4 is dedicated to creating comprehensive unit tests for the QuickBooksOAuthService - +# Task 003.2: Implement QuickBooksOAuthService + +## Description + +Implement the IQuickBooksOAuthService interface to handle OAuth authentication with QuickBooks. This implementation will use the IppDotNetSdkForQuickBooksApiV3 NuGet package to interact with the QuickBooks API for authentication. + +## Parent +003_Create-QuickBooks-Auth-and-Token-Service + +## Requirements + +- Implement the IQuickBooksOAuthService interface defined in Task 003.1 +- Use the IppDotNetSdkForQuickBooksApiV3 NuGet package for OAuth interactions +- Handle the OAuth 2.0 flow including authorization URL generation and callback processing +- Implement proper error handling and logging +- Follow best practices for security when handling authentication + +## Checklist + +### Design +- [x] Review QuickBooks OAuth 2.0 documentation +- [x] Design the implementation class structure + +### Implementation +- [x] Create the QuickBooksOAuthService class +- [x] Implement authorization URL generation +- [x] Implement callback handling +- [x] Implement error handling +- [x] Add logging +- [x] Register the service in DI container + + +## Notes + +The implementation should focus solely on the OAuth flow, not on token management or storage. Token management will be handled in a separate task. + +Key considerations: +- OAuth 2.0 flow requires user consent, so the service should handle redirects and callbacks +- The implementation should be secure and follow best practices +- Error handling should be robust and provide meaningful error messages +- Logging should be comprehensive to aid in debugging + +## Implementation Notes + +This task is dependent on the completion of Task 003.1 which defines the interface to be implemented. + +Testing for the QuickBooksOAuthService is intentionally not included in this task as it will be handled separately: +- Task 003.3 will create a sample application that serves as a testing ground for the OAuth flow +- Task 003.4 is dedicated to creating comprehensive unit tests for the QuickBooksOAuthService + This separation allows for focused implementation of the service first, followed by proper testing in dedicated tasks. \ No newline at end of file diff --git a/Kanban/InProgress/003_Create-QuickBooks-Auth-and-Token-Service.md b/Kanban/InProgress/003_Create-QuickBooks-Auth-and-Token-Service.md index b18bca7..bf4b429 100644 --- a/Kanban/InProgress/003_Create-QuickBooks-Auth-and-Token-Service.md +++ b/Kanban/InProgress/003_Create-QuickBooks-Auth-and-Token-Service.md @@ -1,56 +1,56 @@ -# Task 003: Build QuickBooks Authentication and Token Management Service - -## Description - -In the TimeWarp.QuickBooks library add QuickBooks Authentication and Token Management service using the IppDotNetSdkForQuickBooksApiV3 NuGet package. This service will handle OAuth authentication with QuickBooks, manage access tokens, refresh tokens, and provide a secure interface for other components to interact with QuickBooks API. - -## Requirements - -- Implement OAuth 2.0 authentication flow for QuickBooks -- Securely store and manage access tokens and refresh tokens -- Handle token expiration and automatic refresh -- Provide a clean API for other services to use when interacting with QuickBooks -- Implement proper error handling and logging -- Follow best practices for security when handling authentication credentials - -## Checklist - -### Design -- [ ] Create service architecture design -- [ ] Define interfaces for the authentication and token management service -- [ ] Plan token storage strategy - -### Implementation -- [x] Add IppDotNetSdkForQuickBooksApiV3 NuGet package dependency -- [ ] Implement OAuth authentication flow -- [ ] Create token storage and retrieval mechanism -- [ ] Implement token refresh logic -- [ ] Create service interfaces for other components to use -- [ ] Add proper error handling and logging - -### Testing -- [ ] Create unit tests for authentication flow -- [ ] Test token refresh mechanism -- [ ] Test error handling scenarios - -### Documentation -- [ ] Document authentication flow -- [ ] Document service API usage -- [ ] Add configuration instructions - -### Review -- [ ] Consider Security Implications of token storage -- [ ] Review error handling and recovery mechanisms -- [ ] Code Review - -## Notes - -The IppDotNetSdkForQuickBooksApiV3 NuGet package provides the foundation for interacting with QuickBooks API. This task focuses on building a service layer that handles authentication and token management, which will be used by other components that need to interact with QuickBooks. - -Key considerations: -- OAuth 2.0 flow requires user consent, so the service should handle redirects and callbacks -- Tokens have limited lifetimes and need to be refreshed -- Secure storage of tokens is critical -- The service should be designed to be reusable across different parts of the application -- For the spike phase, token persistence will use file storage -- In the future, persistence will be migrated to Entity Framework and PostgreSQL +# Task 003: Build QuickBooks Authentication and Token Management Service + +## Description + +In the TimeWarp.QuickBooks library add QuickBooks Authentication and Token Management service using the IppDotNetSdkForQuickBooksApiV3 NuGet package. This service will handle OAuth authentication with QuickBooks, manage access tokens, refresh tokens, and provide a secure interface for other components to interact with QuickBooks API. + +## Requirements + +- Implement OAuth 2.0 authentication flow for QuickBooks +- Securely store and manage access tokens and refresh tokens +- Handle token expiration and automatic refresh +- Provide a clean API for other services to use when interacting with QuickBooks +- Implement proper error handling and logging +- Follow best practices for security when handling authentication credentials + +## Checklist + +### Design +- [ ] Create service architecture design +- [ ] Define interfaces for the authentication and token management service +- [ ] Plan token storage strategy + +### Implementation +- [x] Add IppDotNetSdkForQuickBooksApiV3 NuGet package dependency +- [ ] Implement OAuth authentication flow +- [ ] Create token storage and retrieval mechanism +- [ ] Implement token refresh logic +- [ ] Create service interfaces for other components to use +- [ ] Add proper error handling and logging + +### Testing +- [ ] Create unit tests for authentication flow +- [ ] Test token refresh mechanism +- [ ] Test error handling scenarios + +### Documentation +- [ ] Document authentication flow +- [ ] Document service API usage +- [ ] Add configuration instructions + +### Review +- [ ] Consider Security Implications of token storage +- [ ] Review error handling and recovery mechanisms +- [ ] Code Review + +## Notes + +The IppDotNetSdkForQuickBooksApiV3 NuGet package provides the foundation for interacting with QuickBooks API. This task focuses on building a service layer that handles authentication and token management, which will be used by other components that need to interact with QuickBooks. + +Key considerations: +- OAuth 2.0 flow requires user consent, so the service should handle redirects and callbacks +- Tokens have limited lifetimes and need to be refreshed +- Secure storage of tokens is critical +- The service should be designed to be reusable across different parts of the application +- For the spike phase, token persistence will use file storage +- In the future, persistence will be migrated to Entity Framework and PostgreSQL diff --git a/Kanban/ToDo/003.4_Test-QuickBooksOAuthService.md b/Kanban/ToDo/003.4_Test-QuickBooksOAuthService.md index d08b67b..346bc2e 100644 --- a/Kanban/ToDo/003.4_Test-QuickBooksOAuthService.md +++ b/Kanban/ToDo/003.4_Test-QuickBooksOAuthService.md @@ -1,47 +1,47 @@ -# Task 003.4: Test QuickBooksOAuthService - -## Description - -Create comprehensive unit tests for the QuickBooksOAuthService implementation. This task focuses on testing the OAuth service in isolation to ensure it functions correctly before integration into the sample application. - -## Parent -003_Create-QuickBooks-Auth-and-Token-Service - -## Requirements - -- Create unit tests for all methods in the QuickBooksOAuthService -- Test both success and failure scenarios -- Mock external dependencies for unit tests -- Ensure test coverage for error handling -- Follow TDD principles where appropriate - -## Checklist - -### Design -- [ ] Design test approach for OAuth service -- [ ] Create test fixtures and mocks - -### Implementation -- [ ] Implement unit tests for authorization URL generation -- [ ] Implement unit tests for callback handling -- [ ] Implement unit tests for error scenarios -- [ ] Verify test coverage - -### Review -- [ ] Review test quality and coverage -- [ ] Ensure all edge cases are covered - -## Notes - -Testing OAuth flows can be challenging due to their interactive nature. For unit tests, dependencies should be mocked to isolate the service being tested. - -Key considerations: -- Mock external dependencies for unit tests -- Test both success and error paths -- Verify proper error handling -- Consider using a test framework that supports async testing -- Follow the project's existing testing conventions - -## Implementation Notes - +# Task 003.4: Test QuickBooksOAuthService + +## Description + +Create comprehensive unit tests for the QuickBooksOAuthService implementation. This task focuses on testing the OAuth service in isolation to ensure it functions correctly before integration into the sample application. + +## Parent +003_Create-QuickBooks-Auth-and-Token-Service + +## Requirements + +- Create unit tests for all methods in the QuickBooksOAuthService +- Test both success and failure scenarios +- Mock external dependencies for unit tests +- Ensure test coverage for error handling +- Follow TDD principles where appropriate + +## Checklist + +### Design +- [ ] Design test approach for OAuth service +- [ ] Create test fixtures and mocks + +### Implementation +- [ ] Implement unit tests for authorization URL generation +- [ ] Implement unit tests for callback handling +- [ ] Implement unit tests for error scenarios +- [ ] Verify test coverage + +### Review +- [ ] Review test quality and coverage +- [ ] Ensure all edge cases are covered + +## Notes + +Testing OAuth flows can be challenging due to their interactive nature. For unit tests, dependencies should be mocked to isolate the service being tested. + +Key considerations: +- Mock external dependencies for unit tests +- Test both success and error paths +- Verify proper error handling +- Consider using a test framework that supports async testing +- Follow the project's existing testing conventions + +## Implementation Notes + This task is dependent on the completion of Task 003.2 which implements the QuickBooksOAuthService. The tests created in this task will be used to verify the functionality of the service before it's used in the sample application created in Task 003.3. \ No newline at end of file diff --git a/Kanban/ToDo/003.5_Document-QuickBooksOAuthService.md b/Kanban/ToDo/003.5_Document-QuickBooksOAuthService.md index 6f8147b..4f57526 100644 --- a/Kanban/ToDo/003.5_Document-QuickBooksOAuthService.md +++ b/Kanban/ToDo/003.5_Document-QuickBooksOAuthService.md @@ -1,47 +1,47 @@ -# Task 003.5: Document QuickBooksOAuthService - -## Description - -Create comprehensive documentation for the QuickBooksOAuthService, including usage guides, API documentation, and configuration instructions. This documentation will help developers understand how to use the service effectively. - -## Parent -003_Create-QuickBooks-Auth-and-Token-Service - -## Requirements - -- Create clear and concise API documentation -- Provide usage examples for common scenarios -- Document configuration requirements and options -- Explain the OAuth flow and how it's implemented -- Include security considerations and best practices - -## Checklist - -### Implementation -- [ ] Document the IQuickBooksOAuthService interface -- [ ] Document the QuickBooksOAuthService implementation -- [ ] Create usage examples -- [ ] Document configuration options -- [ ] Add security considerations and best practices - -### Documentation -- [ ] Create markdown documentation in the project repository -- [ ] Add XML documentation comments to code -- [ ] Create diagrams illustrating the OAuth flow -- [ ] Document integration with the sample application - -## Notes - -Good documentation is essential for the adoption and proper use of the QuickBooksOAuthService. The documentation should be clear, concise, and provide enough information for developers to use the service effectively without having to understand its internal implementation details. - -Key considerations: -- Use clear and concise language -- Provide code examples for common scenarios -- Explain the OAuth flow in a way that's easy to understand -- Include diagrams where appropriate -- Document security considerations and best practices -- Ensure documentation is kept up-to-date with the code - -## Implementation Notes - +# Task 003.5: Document QuickBooksOAuthService + +## Description + +Create comprehensive documentation for the QuickBooksOAuthService, including usage guides, API documentation, and configuration instructions. This documentation will help developers understand how to use the service effectively. + +## Parent +003_Create-QuickBooks-Auth-and-Token-Service + +## Requirements + +- Create clear and concise API documentation +- Provide usage examples for common scenarios +- Document configuration requirements and options +- Explain the OAuth flow and how it's implemented +- Include security considerations and best practices + +## Checklist + +### Implementation +- [ ] Document the IQuickBooksOAuthService interface +- [ ] Document the QuickBooksOAuthService implementation +- [ ] Create usage examples +- [ ] Document configuration options +- [ ] Add security considerations and best practices + +### Documentation +- [ ] Create markdown documentation in the project repository +- [ ] Add XML documentation comments to code +- [ ] Create diagrams illustrating the OAuth flow +- [ ] Document integration with the sample application + +## Notes + +Good documentation is essential for the adoption and proper use of the QuickBooksOAuthService. The documentation should be clear, concise, and provide enough information for developers to use the service effectively without having to understand its internal implementation details. + +Key considerations: +- Use clear and concise language +- Provide code examples for common scenarios +- Explain the OAuth flow in a way that's easy to understand +- Include diagrams where appropriate +- Document security considerations and best practices +- Ensure documentation is kept up-to-date with the code + +## Implementation Notes + This task is dependent on the completion of Task 003.2 which implements the QuickBooksOAuthService. The documentation should reflect the actual implementation and be updated if the implementation changes. \ No newline at end of file diff --git a/Samples/TimeWarp.QuickBooks.Sample.Web/Program.cs b/Samples/TimeWarp.QuickBooks.Sample.Web/Program.cs index 1233229..9648e96 100644 --- a/Samples/TimeWarp.QuickBooks.Sample.Web/Program.cs +++ b/Samples/TimeWarp.QuickBooks.Sample.Web/Program.cs @@ -1,119 +1,119 @@ -using TimeWarp.QuickBooks.Authentication; -using TimeWarp.QuickBooks.Authentication.Models; -using Microsoft.AspNetCore.Http; - -var builder = WebApplication.CreateBuilder(args); - -// Add QuickBooks OAuth service -builder.Services.AddQuickBooksOAuth(options => -{ - options.ClientId = builder.Configuration["QuickBooks:ClientId"] ?? string.Empty; - options.ClientSecret = builder.Configuration["QuickBooks:ClientSecret"] ?? string.Empty; - options.RedirectUri = builder.Configuration["QuickBooks:RedirectUri"] ?? string.Empty; - options.Environment = Enum.TryParse(builder.Configuration["QuickBooks:Environment"], out var env) - ? env - : QuickBooksEnvironment.Sandbox; - options.Scopes = builder.Configuration.GetSection("QuickBooks:Scopes").Get>() ?? new List(); -}); - -var app = builder.Build(); - -// Home page with a link to start the OAuth flow -app.MapGet("/", () => Results.Content(@" - - - - QuickBooks OAuth Sample - - - -

QuickBooks OAuth Sample

-

Click the button below to authorize with QuickBooks:

- Authorize with QuickBooks - - -", "text/html")); - -// Start OAuth flow -app.MapGet("/auth", (IQuickBooksOAuthService oauthService, HttpContext context) => -{ - // Generate a random state value to prevent CSRF - string state = Guid.NewGuid().ToString(); - - // Store the state in session or cookie for verification later - context.Response.Cookies.Append("OAuthState", state, new CookieOptions - { - HttpOnly = true, - Secure = true, - SameSite = SameSiteMode.Lax - }); - - string authUrl = oauthService.GenerateAuthorizationUrl(state); - return Results.Redirect(authUrl); -}); - -// OAuth callback - display the access token -app.MapGet("/callback", async (HttpContext context, IQuickBooksOAuthService oauthService) => -{ - string? code = context.Request.Query["code"]; - string? state = context.Request.Query["state"]; - string? realmId = context.Request.Query["realmId"]; - - // Retrieve the stored state - context.Request.Cookies.TryGetValue("OAuthState", out string? expectedState); - - if (string.IsNullOrEmpty(code)) - { - return Results.Content("

Error

Authorization code is missing

", "text/html"); - } - - if (string.IsNullOrEmpty(state) || string.IsNullOrEmpty(expectedState) || state != expectedState) - { - return Results.Content("

Error

Invalid state parameter

", "text/html"); - } - - QuickBooksOAuthCallbackResult result = await oauthService.HandleCallbackAsync( - code, - state, - realmId ?? string.Empty, - expectedState - ); - - // Display the access token and other information - return Results.Content($@" - - - - Authorization Successful - - - -

Authorization Successful!

-

Access Token:

-
{result.Tokens?.AccessToken ?? "N/A"}
- -

Refresh Token:

-
{result.Tokens?.RefreshToken ?? "N/A"}
- -

Realm ID:

-
{result.RealmId ?? "N/A"}
- -

Access Token Expires In:

-
{result.Tokens?.ExpiresIn} seconds ({TimeSpan.FromSeconds(result.Tokens?.ExpiresIn ?? 0).TotalHours:0.##} hours)
- -

Refresh Token Expires In:

-
{result.Tokens?.RefreshTokenExpiresIn} seconds ({TimeSpan.FromSeconds(result.Tokens?.RefreshTokenExpiresIn ?? 0).TotalDays:0.##} days)
- -

Back to Home

- - -", "text/html"); -}); - -app.Run(); +using TimeWarp.QuickBooks.Authentication; +using TimeWarp.QuickBooks.Authentication.Models; +using Microsoft.AspNetCore.Http; + +var builder = WebApplication.CreateBuilder(args); + +// Add QuickBooks OAuth service +builder.Services.AddQuickBooksOAuth(options => +{ + options.ClientId = builder.Configuration["QuickBooks:ClientId"] ?? string.Empty; + options.ClientSecret = builder.Configuration["QuickBooks:ClientSecret"] ?? string.Empty; + options.RedirectUri = builder.Configuration["QuickBooks:RedirectUri"] ?? string.Empty; + options.Environment = Enum.TryParse(builder.Configuration["QuickBooks:Environment"], out var env) + ? env + : QuickBooksEnvironment.Sandbox; + options.Scopes = builder.Configuration.GetSection("QuickBooks:Scopes").Get>() ?? new List(); +}); + +var app = builder.Build(); + +// Home page with a link to start the OAuth flow +app.MapGet("/", () => Results.Content(@" + + + + QuickBooks OAuth Sample + + + +

QuickBooks OAuth Sample

+

Click the button below to authorize with QuickBooks:

+ Authorize with QuickBooks + + +", "text/html")); + +// Start OAuth flow +app.MapGet("/auth", (IQuickBooksOAuthService oauthService, HttpContext context) => +{ + // Generate a random state value to prevent CSRF + string state = Guid.NewGuid().ToString(); + + // Store the state in session or cookie for verification later + context.Response.Cookies.Append("OAuthState", state, new CookieOptions + { + HttpOnly = true, + Secure = true, + SameSite = SameSiteMode.Lax + }); + + string authUrl = oauthService.GenerateAuthorizationUrl(state); + return Results.Redirect(authUrl); +}); + +// OAuth callback - display the access token +app.MapGet("/callback", async (HttpContext context, IQuickBooksOAuthService oauthService) => +{ + string? code = context.Request.Query["code"]; + string? state = context.Request.Query["state"]; + string? realmId = context.Request.Query["realmId"]; + + // Retrieve the stored state + context.Request.Cookies.TryGetValue("OAuthState", out string? expectedState); + + if (string.IsNullOrEmpty(code)) + { + return Results.Content("

Error

Authorization code is missing

", "text/html"); + } + + if (string.IsNullOrEmpty(state) || string.IsNullOrEmpty(expectedState) || state != expectedState) + { + return Results.Content("

Error

Invalid state parameter

", "text/html"); + } + + QuickBooksOAuthCallbackResult result = await oauthService.HandleCallbackAsync( + code, + state, + realmId ?? string.Empty, + expectedState + ); + + // Display the access token and other information + return Results.Content($@" + + + + Authorization Successful + + + +

Authorization Successful!

+

Access Token:

+
{result.Tokens?.AccessToken ?? "N/A"}
+ +

Refresh Token:

+
{result.Tokens?.RefreshToken ?? "N/A"}
+ +

Realm ID:

+
{result.RealmId ?? "N/A"}
+ +

Access Token Expires In:

+
{result.Tokens?.ExpiresIn} seconds ({TimeSpan.FromSeconds(result.Tokens?.ExpiresIn ?? 0).TotalHours:0.##} hours)
+ +

Refresh Token Expires In:

+
{result.Tokens?.RefreshTokenExpiresIn} seconds ({TimeSpan.FromSeconds(result.Tokens?.RefreshTokenExpiresIn ?? 0).TotalDays:0.##} days)
+ +

Back to Home

+ + +", "text/html"); +}); + +app.Run(); diff --git a/Samples/TimeWarp.QuickBooks.Sample.Web/Properties/launchSettings.json b/Samples/TimeWarp.QuickBooks.Sample.Web/Properties/launchSettings.json index c72a80e..4d21476 100644 --- a/Samples/TimeWarp.QuickBooks.Sample.Web/Properties/launchSettings.json +++ b/Samples/TimeWarp.QuickBooks.Sample.Web/Properties/launchSettings.json @@ -1,14 +1,14 @@ -{ - "$schema": "https://json.schemastore.org/launchsettings.json", - "profiles": { - "https": { - "commandName": "Project", - "dotnetRunMessages": true, - "launchBrowser": true, - "applicationUrl": "https://localhost:5024", - "environmentVariables": { - "ASPNETCORE_ENVIRONMENT": "Development" - } - } - } -} +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:5024", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/Samples/TimeWarp.QuickBooks.Sample.Web/README.md b/Samples/TimeWarp.QuickBooks.Sample.Web/README.md index ba12bdb..df3215e 100644 --- a/Samples/TimeWarp.QuickBooks.Sample.Web/README.md +++ b/Samples/TimeWarp.QuickBooks.Sample.Web/README.md @@ -1,113 +1,113 @@ -# TimeWarp.QuickBooks.Sample.Web - -This sample application demonstrates how to use the TimeWarp.QuickBooks library to implement OAuth authentication with QuickBooks Online. - -## Overview - -This minimal ASP.NET Core web application shows the essential steps to implement QuickBooks OAuth authentication: - -1. Configure the QuickBooks OAuth service -2. Initiate the OAuth authorization flow -3. Handle the OAuth callback -4. Retrieve and display access tokens - -## User Experience - -The sample provides a minimal but functional user experience: - -1. **Home Page**: A simple page with a button/link to start the OAuth flow - - Text: "Authorize with QuickBooks" - - When clicked, redirects to the `/auth` endpoint - -2. **Authorization Page**: Redirects to QuickBooks authorization page - - User logs in to their QuickBooks account - - User grants permissions to the application - - QuickBooks redirects back to the application's callback URL - -3. **Callback Page**: Displays the OAuth results - - Shows the access token (proof that the OAuth flow worked) - - Shows the refresh token - - Shows the realm ID (company ID) - - Provides a link back to the home page - -This minimal UX demonstrates the complete OAuth flow while keeping the implementation as simple as possible. - -## Implementation - -The minimal implementation requires: - -1. **Configuration**: - ```json - { - "QuickBooks": { - "ClientId": "your-client-id", - "ClientSecret": "your-client-secret", - "RedirectUri": "https://localhost:5001/callback", - "Environment": "Sandbox", - "Scopes": ["com.intuit.quickbooks.accounting"] - } - } - ``` - -2. **Service Registration**: - ```csharp - builder.Services.AddQuickBooksOAuth(options => - { - options.ClientId = "your-client-id"; - options.ClientSecret = "your-client-secret"; - options.RedirectUri = "https://localhost:5001/callback"; - options.Environment = QuickBooksEnvironment.Sandbox; - options.Scopes = new List { "com.intuit.quickbooks.accounting" }; - }); - ``` - -3. **Home Page Endpoint**: - ```csharp - app.MapGet("/", () => Results.Content(@" -

QuickBooks OAuth Sample

- Authorize with QuickBooks - ", "text/html")); - ``` - -4. **Auth Endpoint**: - ```csharp - app.MapGet("/auth", (IQuickBooksOAuthService oauthService) => - { - string authUrl = oauthService.GetAuthorizationUrl("state123"); - return Results.Redirect(authUrl); - }); - ``` - -5. **Callback Endpoint**: - ```csharp - app.MapGet("/callback", async (HttpContext context, IQuickBooksOAuthService oauthService) => - { - string? code = context.Request.Query["code"]; - string? realmId = context.Request.Query["realmId"]; - - QuickBooksOAuthCallbackResult result = await oauthService.HandleCallbackAsync(code, realmId); - - return Results.Content($@" -

Authorization Successful!

-

Access Token:

-
{result.Tokens.AccessToken}
-

Refresh Token:

-
{result.Tokens.RefreshToken}
-

Realm ID:

-
{result.RealmId}
- ", "text/html"); - }); - ``` - -## Running the Sample - -1. Update the configuration with your QuickBooks app credentials -2. Run the application: `dotnet run` -3. Navigate to `https://localhost:5001/` in your browser -4. Click the "Authorize with QuickBooks" link -5. Complete the QuickBooks authorization process -6. View the access token and other OAuth information on the callback page - -## Next Steps - +# TimeWarp.QuickBooks.Sample.Web + +This sample application demonstrates how to use the TimeWarp.QuickBooks library to implement OAuth authentication with QuickBooks Online. + +## Overview + +This minimal ASP.NET Core web application shows the essential steps to implement QuickBooks OAuth authentication: + +1. Configure the QuickBooks OAuth service +2. Initiate the OAuth authorization flow +3. Handle the OAuth callback +4. Retrieve and display access tokens + +## User Experience + +The sample provides a minimal but functional user experience: + +1. **Home Page**: A simple page with a button/link to start the OAuth flow + - Text: "Authorize with QuickBooks" + - When clicked, redirects to the `/auth` endpoint + +2. **Authorization Page**: Redirects to QuickBooks authorization page + - User logs in to their QuickBooks account + - User grants permissions to the application + - QuickBooks redirects back to the application's callback URL + +3. **Callback Page**: Displays the OAuth results + - Shows the access token (proof that the OAuth flow worked) + - Shows the refresh token + - Shows the realm ID (company ID) + - Provides a link back to the home page + +This minimal UX demonstrates the complete OAuth flow while keeping the implementation as simple as possible. + +## Implementation + +The minimal implementation requires: + +1. **Configuration**: + ```json + { + "QuickBooks": { + "ClientId": "your-client-id", + "ClientSecret": "your-client-secret", + "RedirectUri": "https://localhost:5001/callback", + "Environment": "Sandbox", + "Scopes": ["com.intuit.quickbooks.accounting"] + } + } + ``` + +2. **Service Registration**: + ```csharp + builder.Services.AddQuickBooksOAuth(options => + { + options.ClientId = "your-client-id"; + options.ClientSecret = "your-client-secret"; + options.RedirectUri = "https://localhost:5001/callback"; + options.Environment = QuickBooksEnvironment.Sandbox; + options.Scopes = new List { "com.intuit.quickbooks.accounting" }; + }); + ``` + +3. **Home Page Endpoint**: + ```csharp + app.MapGet("/", () => Results.Content(@" +

QuickBooks OAuth Sample

+ Authorize with QuickBooks + ", "text/html")); + ``` + +4. **Auth Endpoint**: + ```csharp + app.MapGet("/auth", (IQuickBooksOAuthService oauthService) => + { + string authUrl = oauthService.GetAuthorizationUrl("state123"); + return Results.Redirect(authUrl); + }); + ``` + +5. **Callback Endpoint**: + ```csharp + app.MapGet("/callback", async (HttpContext context, IQuickBooksOAuthService oauthService) => + { + string? code = context.Request.Query["code"]; + string? realmId = context.Request.Query["realmId"]; + + QuickBooksOAuthCallbackResult result = await oauthService.HandleCallbackAsync(code, realmId); + + return Results.Content($@" +

Authorization Successful!

+

Access Token:

+
{result.Tokens.AccessToken}
+

Refresh Token:

+
{result.Tokens.RefreshToken}
+

Realm ID:

+
{result.RealmId}
+ ", "text/html"); + }); + ``` + +## Running the Sample + +1. Update the configuration with your QuickBooks app credentials +2. Run the application: `dotnet run` +3. Navigate to `https://localhost:5001/` in your browser +4. Click the "Authorize with QuickBooks" link +5. Complete the QuickBooks authorization process +6. View the access token and other OAuth information on the callback page + +## Next Steps + After obtaining the access token, you can use it to make API calls to QuickBooks Online. The TimeWarp.QuickBooks library provides additional functionality for working with the QuickBooks API. \ No newline at end of file diff --git a/Samples/TimeWarp.QuickBooks.Sample.Web/TimeWarp.QuickBooks.Sample.Web.csproj b/Samples/TimeWarp.QuickBooks.Sample.Web/TimeWarp.QuickBooks.Sample.Web.csproj index 124a7fb..eaf85f1 100644 --- a/Samples/TimeWarp.QuickBooks.Sample.Web/TimeWarp.QuickBooks.Sample.Web.csproj +++ b/Samples/TimeWarp.QuickBooks.Sample.Web/TimeWarp.QuickBooks.Sample.Web.csproj @@ -1,13 +1,13 @@ - - - - net9.0 - enable - enable - 4887abc2-35db-470c-bf79-456c0b4f1a19 - - - - - - + + + + net9.0 + enable + enable + 4887abc2-35db-470c-bf79-456c0b4f1a19 + + + + + + diff --git a/Samples/TimeWarp.QuickBooks.Sample.Web/appsettings.Development.json b/Samples/TimeWarp.QuickBooks.Sample.Web/appsettings.Development.json index ff66ba6..0c208ae 100644 --- a/Samples/TimeWarp.QuickBooks.Sample.Web/appsettings.Development.json +++ b/Samples/TimeWarp.QuickBooks.Sample.Web/appsettings.Development.json @@ -1,8 +1,8 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } -} +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/Samples/TimeWarp.QuickBooks.Sample.Web/appsettings.json b/Samples/TimeWarp.QuickBooks.Sample.Web/appsettings.json index 70a3c3f..7842a29 100644 --- a/Samples/TimeWarp.QuickBooks.Sample.Web/appsettings.json +++ b/Samples/TimeWarp.QuickBooks.Sample.Web/appsettings.json @@ -1,18 +1,18 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*", - "QuickBooks": { - "ClientId": "your-client-id", - "ClientSecret": "your-client-secret", - "RedirectUri": "https://localhost:5024/callback", - "Environment": "Sandbox", - "Scopes": [ - "Accounting" - ] - } -} +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "QuickBooks": { + "ClientId": "your-client-id", + "ClientSecret": "your-client-secret", + "RedirectUri": "https://localhost:5024/callback", + "Environment": "Sandbox", + "Scopes": [ + "Accounting" + ] + } +} diff --git a/Source/TimeWarp.QuickBooks/Authentication/IQuickBooksOAuthService.cs b/Source/TimeWarp.QuickBooks/Authentication/IQuickBooksOAuthService.cs index 0d07ed4..0de26ee 100644 --- a/Source/TimeWarp.QuickBooks/Authentication/IQuickBooksOAuthService.cs +++ b/Source/TimeWarp.QuickBooks/Authentication/IQuickBooksOAuthService.cs @@ -1,72 +1,72 @@ -namespace TimeWarp.QuickBooks.Authentication; - -using TimeWarp.QuickBooks.Authentication.Models; - -/// -/// Interface for a service that handles OAuth authentication with QuickBooks. -/// -public interface IQuickBooksOAuthService -{ - /// - /// Gets the QuickBooks OAuth configuration options. - /// - QuickBooksOAuthOptions Options { get; } - - /// - /// Generates an authorization URL for initiating the OAuth flow. - /// - /// A unique state value to prevent CSRF attacks. - /// The authorization URL to redirect the user to. - string GenerateAuthorizationUrl(string state); - - /// - /// Handles the callback from QuickBooks after user authorization. - /// - /// The authorization code received from QuickBooks. - /// The state parameter that was sent in the authorization request. - /// The Realm ID (Company ID) received from QuickBooks. - /// The expected state value to verify against the received state. - /// A task that represents the asynchronous operation. The task result contains the callback result. - Task HandleCallbackAsync - ( - string code, - string state, - string realmId, - string expectedState - ); - - /// - /// Gets the current tokens for the specified realm. - /// - /// The Realm ID (Company ID) to get tokens for. - /// A task that represents the asynchronous operation. The task result contains the tokens or null if not found. - Task GetTokensAsync(string realmId); - - /// - /// Refreshes the access token if it has expired. - /// - /// The current tokens. - /// A task that represents the asynchronous operation. The task result contains the refreshed tokens. - Task RefreshTokensIfNeededAsync(QuickBooksTokens tokens); - - /// - /// Revokes the access and refresh tokens. - /// - /// The tokens to revoke. - /// A task that represents the asynchronous operation. - Task RevokeTokensAsync(QuickBooksTokens tokens); - - /// - /// Saves the tokens for the specified realm. - /// - /// The Realm ID (Company ID) to save tokens for. - /// The tokens to save. - /// A task that represents the asynchronous operation. - Task SaveTokensAsync(string realmId, QuickBooksTokens tokens); - - /// - /// Validates the current state of the OAuth service. - /// - /// True if the service is properly configured; otherwise, false. - bool ValidateConfiguration(); +namespace TimeWarp.QuickBooks.Authentication; + +using TimeWarp.QuickBooks.Authentication.Models; + +/// +/// Interface for a service that handles OAuth authentication with QuickBooks. +/// +public interface IQuickBooksOAuthService +{ + /// + /// Gets the QuickBooks OAuth configuration options. + /// + QuickBooksOAuthOptions Options { get; } + + /// + /// Generates an authorization URL for initiating the OAuth flow. + /// + /// A unique state value to prevent CSRF attacks. + /// The authorization URL to redirect the user to. + string GenerateAuthorizationUrl(string state); + + /// + /// Handles the callback from QuickBooks after user authorization. + /// + /// The authorization code received from QuickBooks. + /// The state parameter that was sent in the authorization request. + /// The Realm ID (Company ID) received from QuickBooks. + /// The expected state value to verify against the received state. + /// A task that represents the asynchronous operation. The task result contains the callback result. + Task HandleCallbackAsync + ( + string code, + string state, + string realmId, + string expectedState + ); + + /// + /// Gets the current tokens for the specified realm. + /// + /// The Realm ID (Company ID) to get tokens for. + /// A task that represents the asynchronous operation. The task result contains the tokens or null if not found. + Task GetTokensAsync(string realmId); + + /// + /// Refreshes the access token if it has expired. + /// + /// The current tokens. + /// A task that represents the asynchronous operation. The task result contains the refreshed tokens. + Task RefreshTokensIfNeededAsync(QuickBooksTokens tokens); + + /// + /// Revokes the access and refresh tokens. + /// + /// The tokens to revoke. + /// A task that represents the asynchronous operation. + Task RevokeTokensAsync(QuickBooksTokens tokens); + + /// + /// Saves the tokens for the specified realm. + /// + /// The Realm ID (Company ID) to save tokens for. + /// The tokens to save. + /// A task that represents the asynchronous operation. + Task SaveTokensAsync(string realmId, QuickBooksTokens tokens); + + /// + /// Validates the current state of the OAuth service. + /// + /// True if the service is properly configured; otherwise, false. + bool ValidateConfiguration(); } \ No newline at end of file diff --git a/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksOAuthCallbackResult.cs b/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksOAuthCallbackResult.cs index 5392b17..da96ae0 100644 --- a/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksOAuthCallbackResult.cs +++ b/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksOAuthCallbackResult.cs @@ -1,80 +1,80 @@ -namespace TimeWarp.QuickBooks.Authentication.Models; - -/// -/// Represents the result of a QuickBooks OAuth callback. -/// -public class QuickBooksOAuthCallbackResult -{ - /// - /// Gets or sets a value indicating whether the callback was successful. - /// - public bool IsSuccess { get; set; } - - /// - /// Gets or sets the error message if the callback was not successful. - /// - public string? ErrorMessage { get; set; } - - /// - /// Gets or sets the authorization code received from QuickBooks. - /// This code is exchanged for access and refresh tokens. - /// - public string? AuthorizationCode { get; set; } - - /// - /// Gets or sets the state parameter that was sent in the authorization request. - /// This is used to verify that the callback is in response to a request from this application. - /// - public string? State { get; set; } - - /// - /// Gets or sets the Realm ID (Company ID) received from QuickBooks. - /// - public string? RealmId { get; set; } - - /// - /// Gets or sets the tokens obtained after exchanging the authorization code. - /// This will be null if the tokens have not been obtained yet. - /// - public QuickBooksTokens? Tokens { get; set; } - - /// - /// Creates a successful callback result. - /// - /// The authorization code received from QuickBooks. - /// The state parameter that was sent in the authorization request. - /// The Realm ID (Company ID) received from QuickBooks. - /// A successful callback result. - public static QuickBooksOAuthCallbackResult Success - ( - string authorizationCode, - string state, - string realmId - ) - { - return new QuickBooksOAuthCallbackResult - { - IsSuccess = true, - AuthorizationCode = authorizationCode, - State = state, - RealmId = realmId - }; - } - - /// - /// Creates a failed callback result. - /// - /// The error message. - /// A failed callback result. - public static QuickBooksOAuthCallbackResult Failure - ( - string errorMessage - ) - { - return new QuickBooksOAuthCallbackResult - { - IsSuccess = false, - ErrorMessage = errorMessage - }; - } +namespace TimeWarp.QuickBooks.Authentication.Models; + +/// +/// Represents the result of a QuickBooks OAuth callback. +/// +public class QuickBooksOAuthCallbackResult +{ + /// + /// Gets or sets a value indicating whether the callback was successful. + /// + public bool IsSuccess { get; set; } + + /// + /// Gets or sets the error message if the callback was not successful. + /// + public string? ErrorMessage { get; set; } + + /// + /// Gets or sets the authorization code received from QuickBooks. + /// This code is exchanged for access and refresh tokens. + /// + public string? AuthorizationCode { get; set; } + + /// + /// Gets or sets the state parameter that was sent in the authorization request. + /// This is used to verify that the callback is in response to a request from this application. + /// + public string? State { get; set; } + + /// + /// Gets or sets the Realm ID (Company ID) received from QuickBooks. + /// + public string? RealmId { get; set; } + + /// + /// Gets or sets the tokens obtained after exchanging the authorization code. + /// This will be null if the tokens have not been obtained yet. + /// + public QuickBooksTokens? Tokens { get; set; } + + /// + /// Creates a successful callback result. + /// + /// The authorization code received from QuickBooks. + /// The state parameter that was sent in the authorization request. + /// The Realm ID (Company ID) received from QuickBooks. + /// A successful callback result. + public static QuickBooksOAuthCallbackResult Success + ( + string authorizationCode, + string state, + string realmId + ) + { + return new QuickBooksOAuthCallbackResult + { + IsSuccess = true, + AuthorizationCode = authorizationCode, + State = state, + RealmId = realmId + }; + } + + /// + /// Creates a failed callback result. + /// + /// The error message. + /// A failed callback result. + public static QuickBooksOAuthCallbackResult Failure + ( + string errorMessage + ) + { + return new QuickBooksOAuthCallbackResult + { + IsSuccess = false, + ErrorMessage = errorMessage + }; + } } \ No newline at end of file diff --git a/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksOAuthOptions.cs b/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksOAuthOptions.cs index 7a6e7e0..6584158 100644 --- a/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksOAuthOptions.cs +++ b/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksOAuthOptions.cs @@ -1,48 +1,48 @@ -namespace TimeWarp.QuickBooks.Authentication.Models; - -/// -/// Configuration options for QuickBooks OAuth authentication. -/// -public class QuickBooksOAuthOptions -{ - /// - /// Gets or sets the client ID (consumer key) for the QuickBooks application. - /// - public string ClientId { get; set; } = string.Empty; - - /// - /// Gets or sets the client secret (consumer secret) for the QuickBooks application. - /// - public string ClientSecret { get; set; } = string.Empty; - - /// - /// Gets or sets the redirect URI where QuickBooks will send the authorization response. - /// - public string RedirectUri { get; set; } = string.Empty; - - /// - /// Gets or sets the environment (production or sandbox). - /// - public QuickBooksEnvironment Environment { get; set; } = QuickBooksEnvironment.Sandbox; - - /// - /// Gets or sets the scopes requested during authorization. - /// - public List Scopes { get; set; } = new List(); -} - -/// -/// Represents the QuickBooks environment. -/// -public enum QuickBooksEnvironment -{ - /// - /// QuickBooks sandbox environment for development and testing. - /// - Sandbox, - - /// - /// QuickBooks production environment for live applications. - /// - Production +namespace TimeWarp.QuickBooks.Authentication.Models; + +/// +/// Configuration options for QuickBooks OAuth authentication. +/// +public class QuickBooksOAuthOptions +{ + /// + /// Gets or sets the client ID (consumer key) for the QuickBooks application. + /// + public string ClientId { get; set; } = string.Empty; + + /// + /// Gets or sets the client secret (consumer secret) for the QuickBooks application. + /// + public string ClientSecret { get; set; } = string.Empty; + + /// + /// Gets or sets the redirect URI where QuickBooks will send the authorization response. + /// + public string RedirectUri { get; set; } = string.Empty; + + /// + /// Gets or sets the environment (production or sandbox). + /// + public QuickBooksEnvironment Environment { get; set; } = QuickBooksEnvironment.Sandbox; + + /// + /// Gets or sets the scopes requested during authorization. + /// + public List Scopes { get; set; } = new List(); +} + +/// +/// Represents the QuickBooks environment. +/// +public enum QuickBooksEnvironment +{ + /// + /// QuickBooks sandbox environment for development and testing. + /// + Sandbox, + + /// + /// QuickBooks production environment for live applications. + /// + Production } \ No newline at end of file diff --git a/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksTokens.cs b/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksTokens.cs index 1e9d3ac..b7b4ddc 100644 --- a/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksTokens.cs +++ b/Source/TimeWarp.QuickBooks/Authentication/Models/QuickBooksTokens.cs @@ -1,61 +1,61 @@ -namespace TimeWarp.QuickBooks.Authentication.Models; - -/// -/// Represents the OAuth tokens received from QuickBooks API. -/// -public class QuickBooksTokens -{ - /// - /// Gets or sets the access token used for API requests. - /// - public string AccessToken { get; set; } = string.Empty; - - /// - /// Gets or sets the refresh token used to obtain a new access token when it expires. - /// - public string RefreshToken { get; set; } = string.Empty; - - /// - /// Gets or sets the type of the access token (e.g., "Bearer"). - /// - public string TokenType { get; set; } = "Bearer"; - - /// - /// Gets or sets the expiration time of the access token in seconds from the time it was issued. - /// - public long ExpiresIn { get; set; } - - /// - /// Gets or sets the expiration time of the refresh token in seconds from the time it was issued. - /// This corresponds to the x_refresh_token_expires_in field from QuickBooks OAuth response. - /// - public long RefreshTokenExpiresIn { get; set; } - - /// - /// Gets or sets the UTC date and time when the access token was issued. - /// - public DateTime IssuedUtc { get; set; } = DateTime.UtcNow; - - /// - /// Gets or sets the Realm ID (Company ID) associated with these tokens. - /// - public string RealmId { get; set; } = string.Empty; - - /// - /// Determines whether the access token has expired. - /// - /// True if the token has expired; otherwise, false. - public bool IsExpired() - { - return DateTime.UtcNow >= IssuedUtc.AddSeconds(ExpiresIn - 300); // 5-minute buffer - } - - /// - /// Determines whether the refresh token has expired. - /// - /// True if the refresh token has expired; otherwise, false. - public bool IsRefreshTokenExpired() - { - return DateTime.UtcNow >= IssuedUtc.AddSeconds(RefreshTokenExpiresIn - 300); // 5-minute buffer - } +namespace TimeWarp.QuickBooks.Authentication.Models; + +/// +/// Represents the OAuth tokens received from QuickBooks API. +/// +public class QuickBooksTokens +{ + /// + /// Gets or sets the access token used for API requests. + /// + public string AccessToken { get; set; } = string.Empty; + + /// + /// Gets or sets the refresh token used to obtain a new access token when it expires. + /// + public string RefreshToken { get; set; } = string.Empty; + + /// + /// Gets or sets the type of the access token (e.g., "Bearer"). + /// + public string TokenType { get; set; } = "Bearer"; + + /// + /// Gets or sets the expiration time of the access token in seconds from the time it was issued. + /// + public long ExpiresIn { get; set; } + + /// + /// Gets or sets the expiration time of the refresh token in seconds from the time it was issued. + /// This corresponds to the x_refresh_token_expires_in field from QuickBooks OAuth response. + /// + public long RefreshTokenExpiresIn { get; set; } + + /// + /// Gets or sets the UTC date and time when the access token was issued. + /// + public DateTime IssuedUtc { get; set; } = DateTime.UtcNow; + + /// + /// Gets or sets the Realm ID (Company ID) associated with these tokens. + /// + public string RealmId { get; set; } = string.Empty; + + /// + /// Determines whether the access token has expired. + /// + /// True if the token has expired; otherwise, false. + public bool IsExpired() + { + return DateTime.UtcNow >= IssuedUtc.AddSeconds(ExpiresIn - 300); // 5-minute buffer + } + + /// + /// Determines whether the refresh token has expired. + /// + /// True if the refresh token has expired; otherwise, false. + public bool IsRefreshTokenExpired() + { + return DateTime.UtcNow >= IssuedUtc.AddSeconds(RefreshTokenExpiresIn - 300); // 5-minute buffer + } } \ No newline at end of file diff --git a/Source/TimeWarp.QuickBooks/Authentication/QuickBooksOAuthService.cs b/Source/TimeWarp.QuickBooks/Authentication/QuickBooksOAuthService.cs index fe0dc9c..a75f238 100644 --- a/Source/TimeWarp.QuickBooks/Authentication/QuickBooksOAuthService.cs +++ b/Source/TimeWarp.QuickBooks/Authentication/QuickBooksOAuthService.cs @@ -1,282 +1,282 @@ -namespace TimeWarp.QuickBooks.Authentication; - -using TimeWarp.QuickBooks.Authentication.Models; - -/// -/// Service that handles OAuth authentication with QuickBooks. -/// -public class QuickBooksOAuthService : IQuickBooksOAuthService -{ - private readonly ILogger Logger; - private readonly OAuth2Client OAuth2Client; - private readonly Dictionary TokenStore; - - /// - /// Gets the QuickBooks OAuth configuration options. - /// - public QuickBooksOAuthOptions Options { get; } - - /// - /// Initializes a new instance of the class. - /// - /// The QuickBooks OAuth options. - /// The logger. - public QuickBooksOAuthService - ( - IOptions options, - ILogger logger - ) - { - Options = options.Value; - Logger = logger; - TokenStore = []; - - // Create the OAuth2 client using the Intuit SDK - OAuth2Client = new OAuth2Client - ( - Options.ClientId, - Options.ClientSecret, - Options.RedirectUri, - Options.Environment == QuickBooksEnvironment.Production ? - nameof(QuickBooksEnvironment.Production) : - nameof(QuickBooksEnvironment.Sandbox) - ); - } - - /// - /// Generates an authorization URL for initiating the OAuth flow. - /// - /// A unique state value to prevent CSRF attacks. - /// The authorization URL to redirect the user to. - public string GenerateAuthorizationUrl(string state) - { - Logger.LogInformation("Generating authorization URL with state: {State}", state); - - List scopes = []; - - // Convert string scopes to OidcScopes enum values - foreach (string scope in Options.Scopes) - { - if (Enum.TryParse(scope, true, out OidcScopes oidcScope)) - scopes.Add(oidcScope); - else - Logger.LogWarning("Invalid scope: {Scope}", scope); - } - - // Generate the authorization URL - string authUrl = OAuth2Client.GetAuthorizationURL(scopes, state); - - Logger.LogInformation("Generated authorization URL: {AuthUrl}", authUrl); - - return authUrl; - } - - /// - /// Handles the callback from QuickBooks after user authorization. - /// - /// The authorization code received from QuickBooks. - /// The state parameter that was sent in the authorization request. - /// The Realm ID (Company ID) received from QuickBooks. - /// The expected state value to verify against the received state. - /// A task that represents the asynchronous operation. The task result contains the callback result. - public async Task HandleCallbackAsync - ( - string code, - string state, - string realmId, - string expectedState - ) - { - Logger.LogInformation("Handling OAuth callback for realm: {RealmId}", realmId); - - // Verify state to prevent CSRF attacks - if (state != expectedState) - { - string errorMessage = "State mismatch. Possible CSRF attack."; - Logger.LogWarning(errorMessage); - return QuickBooksOAuthCallbackResult.Failure(errorMessage); - } - - try - { - // Exchange authorization code for tokens - TokenResponse tokenResponse = await OAuth2Client.GetBearerTokenAsync(code); - - // Create and store tokens - QuickBooksTokens tokens = new() - { - AccessToken = tokenResponse.AccessToken, - RefreshToken = tokenResponse.RefreshToken, - TokenType = tokenResponse.TokenType, - ExpiresIn = tokenResponse.AccessTokenExpiresIn, - RefreshTokenExpiresIn = tokenResponse.RefreshTokenExpiresIn, - IssuedUtc = DateTime.UtcNow, - RealmId = realmId - }; - - await SaveTokensAsync(realmId, tokens); - - // Create successful result with tokens - QuickBooksOAuthCallbackResult result = QuickBooksOAuthCallbackResult.Success(code, state, realmId); - result.Tokens = tokens; - - Logger.LogInformation("Successfully processed OAuth callback for realm: {RealmId}", realmId); - - return result; - } - catch (Exception ex) - { - string errorMessage = $"Error handling OAuth callback: {ex.Message}"; - Logger.LogError(ex, errorMessage); - return QuickBooksOAuthCallbackResult.Failure(errorMessage); - } - } - - /// - /// Gets the current tokens for the specified realm. - /// - /// The Realm ID (Company ID) to get tokens for. - /// A task that represents the asynchronous operation. The task result contains the tokens or null if not found. - public Task GetTokensAsync(string realmId) - { - Logger.LogInformation("Getting tokens for realm: {RealmId}", realmId); - - if (TokenStore.TryGetValue(realmId, out QuickBooksTokens? tokens)) - { - return Task.FromResult(tokens); - } - - Logger.LogWarning("No tokens found for realm: {RealmId}", realmId); - return Task.FromResult(null); - } - - /// - /// Refreshes the access token if it has expired. - /// - /// The current tokens. - /// A task that represents the asynchronous operation. The task result contains the refreshed tokens. - public async Task RefreshTokensIfNeededAsync(QuickBooksTokens tokens) - { - if (!tokens.IsExpired()) - { - Logger.LogInformation("Tokens for realm {RealmId} are still valid", tokens.RealmId); - return tokens; - } - - Logger.LogInformation("Refreshing tokens for realm: {RealmId}", tokens.RealmId); - - try - { - // Use the refresh token to get a new access token - TokenResponse tokenResponse = await OAuth2Client.RefreshTokenAsync(tokens.RefreshToken); - - // Update tokens - QuickBooksTokens refreshedTokens = new() - { - AccessToken = tokenResponse.AccessToken, - RefreshToken = tokenResponse.RefreshToken, - TokenType = tokenResponse.TokenType, - ExpiresIn = tokenResponse.AccessTokenExpiresIn, - RefreshTokenExpiresIn = tokenResponse.RefreshTokenExpiresIn, - IssuedUtc = DateTime.UtcNow, - RealmId = tokens.RealmId - }; - - // Save the refreshed tokens - await SaveTokensAsync(tokens.RealmId, refreshedTokens); - - Logger.LogInformation("Successfully refreshed tokens for realm: {RealmId}", tokens.RealmId); - - return refreshedTokens; - } - catch (Exception ex) - { - Logger.LogError(ex, "Error refreshing tokens for realm: {RealmId}", tokens.RealmId); - throw; - } - } - - /// - /// Revokes the access and refresh tokens. - /// - /// The tokens to revoke. - /// A task that represents the asynchronous operation. - public async Task RevokeTokensAsync(QuickBooksTokens tokens) - { - Logger.LogInformation("Revoking tokens for realm: {RealmId}", tokens.RealmId); - - try - { - // Revoke the access token - await OAuth2Client.RevokeTokenAsync(tokens.AccessToken, "access_token"); - - // Revoke the refresh token - await OAuth2Client.RevokeTokenAsync(tokens.RefreshToken, "refresh_token"); - - // Remove from token store - if (TokenStore.ContainsKey(tokens.RealmId)) - { - TokenStore.Remove(tokens.RealmId); - } - - Logger.LogInformation("Successfully revoked tokens for realm: {RealmId}", tokens.RealmId); - } - catch (Exception ex) - { - Logger.LogError(ex, "Error revoking tokens for realm: {RealmId}", tokens.RealmId); - throw; - } - } - - /// - /// Saves the tokens for the specified realm. - /// - /// The Realm ID (Company ID) to save tokens for. - /// The tokens to save. - /// A task that represents the asynchronous operation. - public Task SaveTokensAsync(string realmId, QuickBooksTokens tokens) - { - Logger.LogInformation("Saving tokens for realm: {RealmId}", realmId); - - // In a real implementation, tokens should be stored securely in a database or secure storage - // For this implementation, we're using an in-memory dictionary - TokenStore[realmId] = tokens; - - return Task.CompletedTask; - } - - /// - /// Validates the current state of the OAuth service. - /// - /// True if the service is properly configured; otherwise, false. - public bool ValidateConfiguration() - { - bool isValid = true; - - if (string.IsNullOrEmpty(Options.ClientId)) - { - Logger.LogError("ClientId is not configured"); - isValid = false; - } - - if (string.IsNullOrEmpty(Options.ClientSecret)) - { - Logger.LogError("ClientSecret is not configured"); - isValid = false; - } - - if (string.IsNullOrEmpty(Options.RedirectUri)) - { - Logger.LogError("RedirectUri is not configured"); - isValid = false; - } - - if (Options.Scopes.Count == 0) - { - Logger.LogError("No scopes are configured"); - isValid = false; - } - - return isValid; - } +namespace TimeWarp.QuickBooks.Authentication; + +using TimeWarp.QuickBooks.Authentication.Models; + +/// +/// Service that handles OAuth authentication with QuickBooks. +/// +public class QuickBooksOAuthService : IQuickBooksOAuthService +{ + private readonly ILogger Logger; + private readonly OAuth2Client OAuth2Client; + private readonly Dictionary TokenStore; + + /// + /// Gets the QuickBooks OAuth configuration options. + /// + public QuickBooksOAuthOptions Options { get; } + + /// + /// Initializes a new instance of the class. + /// + /// The QuickBooks OAuth options. + /// The logger. + public QuickBooksOAuthService + ( + IOptions options, + ILogger logger + ) + { + Options = options.Value; + Logger = logger; + TokenStore = []; + + // Create the OAuth2 client using the Intuit SDK + OAuth2Client = new OAuth2Client + ( + Options.ClientId, + Options.ClientSecret, + Options.RedirectUri, + Options.Environment == QuickBooksEnvironment.Production ? + nameof(QuickBooksEnvironment.Production) : + nameof(QuickBooksEnvironment.Sandbox) + ); + } + + /// + /// Generates an authorization URL for initiating the OAuth flow. + /// + /// A unique state value to prevent CSRF attacks. + /// The authorization URL to redirect the user to. + public string GenerateAuthorizationUrl(string state) + { + Logger.LogInformation("Generating authorization URL with state: {State}", state); + + List scopes = []; + + // Convert string scopes to OidcScopes enum values + foreach (string scope in Options.Scopes) + { + if (Enum.TryParse(scope, true, out OidcScopes oidcScope)) + scopes.Add(oidcScope); + else + Logger.LogWarning("Invalid scope: {Scope}", scope); + } + + // Generate the authorization URL + string authUrl = OAuth2Client.GetAuthorizationURL(scopes, state); + + Logger.LogInformation("Generated authorization URL: {AuthUrl}", authUrl); + + return authUrl; + } + + /// + /// Handles the callback from QuickBooks after user authorization. + /// + /// The authorization code received from QuickBooks. + /// The state parameter that was sent in the authorization request. + /// The Realm ID (Company ID) received from QuickBooks. + /// The expected state value to verify against the received state. + /// A task that represents the asynchronous operation. The task result contains the callback result. + public async Task HandleCallbackAsync + ( + string code, + string state, + string realmId, + string expectedState + ) + { + Logger.LogInformation("Handling OAuth callback for realm: {RealmId}", realmId); + + // Verify state to prevent CSRF attacks + if (state != expectedState) + { + string errorMessage = "State mismatch. Possible CSRF attack."; + Logger.LogWarning(errorMessage); + return QuickBooksOAuthCallbackResult.Failure(errorMessage); + } + + try + { + // Exchange authorization code for tokens + TokenResponse tokenResponse = await OAuth2Client.GetBearerTokenAsync(code); + + // Create and store tokens + QuickBooksTokens tokens = new() + { + AccessToken = tokenResponse.AccessToken, + RefreshToken = tokenResponse.RefreshToken, + TokenType = tokenResponse.TokenType, + ExpiresIn = tokenResponse.AccessTokenExpiresIn, + RefreshTokenExpiresIn = tokenResponse.RefreshTokenExpiresIn, + IssuedUtc = DateTime.UtcNow, + RealmId = realmId + }; + + await SaveTokensAsync(realmId, tokens); + + // Create successful result with tokens + QuickBooksOAuthCallbackResult result = QuickBooksOAuthCallbackResult.Success(code, state, realmId); + result.Tokens = tokens; + + Logger.LogInformation("Successfully processed OAuth callback for realm: {RealmId}", realmId); + + return result; + } + catch (Exception ex) + { + string errorMessage = $"Error handling OAuth callback: {ex.Message}"; + Logger.LogError(ex, errorMessage); + return QuickBooksOAuthCallbackResult.Failure(errorMessage); + } + } + + /// + /// Gets the current tokens for the specified realm. + /// + /// The Realm ID (Company ID) to get tokens for. + /// A task that represents the asynchronous operation. The task result contains the tokens or null if not found. + public Task GetTokensAsync(string realmId) + { + Logger.LogInformation("Getting tokens for realm: {RealmId}", realmId); + + if (TokenStore.TryGetValue(realmId, out QuickBooksTokens? tokens)) + { + return Task.FromResult(tokens); + } + + Logger.LogWarning("No tokens found for realm: {RealmId}", realmId); + return Task.FromResult(null); + } + + /// + /// Refreshes the access token if it has expired. + /// + /// The current tokens. + /// A task that represents the asynchronous operation. The task result contains the refreshed tokens. + public async Task RefreshTokensIfNeededAsync(QuickBooksTokens tokens) + { + if (!tokens.IsExpired()) + { + Logger.LogInformation("Tokens for realm {RealmId} are still valid", tokens.RealmId); + return tokens; + } + + Logger.LogInformation("Refreshing tokens for realm: {RealmId}", tokens.RealmId); + + try + { + // Use the refresh token to get a new access token + TokenResponse tokenResponse = await OAuth2Client.RefreshTokenAsync(tokens.RefreshToken); + + // Update tokens + QuickBooksTokens refreshedTokens = new() + { + AccessToken = tokenResponse.AccessToken, + RefreshToken = tokenResponse.RefreshToken, + TokenType = tokenResponse.TokenType, + ExpiresIn = tokenResponse.AccessTokenExpiresIn, + RefreshTokenExpiresIn = tokenResponse.RefreshTokenExpiresIn, + IssuedUtc = DateTime.UtcNow, + RealmId = tokens.RealmId + }; + + // Save the refreshed tokens + await SaveTokensAsync(tokens.RealmId, refreshedTokens); + + Logger.LogInformation("Successfully refreshed tokens for realm: {RealmId}", tokens.RealmId); + + return refreshedTokens; + } + catch (Exception ex) + { + Logger.LogError(ex, "Error refreshing tokens for realm: {RealmId}", tokens.RealmId); + throw; + } + } + + /// + /// Revokes the access and refresh tokens. + /// + /// The tokens to revoke. + /// A task that represents the asynchronous operation. + public async Task RevokeTokensAsync(QuickBooksTokens tokens) + { + Logger.LogInformation("Revoking tokens for realm: {RealmId}", tokens.RealmId); + + try + { + // Revoke the access token + await OAuth2Client.RevokeTokenAsync(tokens.AccessToken, "access_token"); + + // Revoke the refresh token + await OAuth2Client.RevokeTokenAsync(tokens.RefreshToken, "refresh_token"); + + // Remove from token store + if (TokenStore.ContainsKey(tokens.RealmId)) + { + TokenStore.Remove(tokens.RealmId); + } + + Logger.LogInformation("Successfully revoked tokens for realm: {RealmId}", tokens.RealmId); + } + catch (Exception ex) + { + Logger.LogError(ex, "Error revoking tokens for realm: {RealmId}", tokens.RealmId); + throw; + } + } + + /// + /// Saves the tokens for the specified realm. + /// + /// The Realm ID (Company ID) to save tokens for. + /// The tokens to save. + /// A task that represents the asynchronous operation. + public Task SaveTokensAsync(string realmId, QuickBooksTokens tokens) + { + Logger.LogInformation("Saving tokens for realm: {RealmId}", realmId); + + // In a real implementation, tokens should be stored securely in a database or secure storage + // For this implementation, we're using an in-memory dictionary + TokenStore[realmId] = tokens; + + return Task.CompletedTask; + } + + /// + /// Validates the current state of the OAuth service. + /// + /// True if the service is properly configured; otherwise, false. + public bool ValidateConfiguration() + { + bool isValid = true; + + if (string.IsNullOrEmpty(Options.ClientId)) + { + Logger.LogError("ClientId is not configured"); + isValid = false; + } + + if (string.IsNullOrEmpty(Options.ClientSecret)) + { + Logger.LogError("ClientSecret is not configured"); + isValid = false; + } + + if (string.IsNullOrEmpty(Options.RedirectUri)) + { + Logger.LogError("RedirectUri is not configured"); + isValid = false; + } + + if (Options.Scopes.Count == 0) + { + Logger.LogError("No scopes are configured"); + isValid = false; + } + + return isValid; + } } \ No newline at end of file diff --git a/Source/TimeWarp.QuickBooks/Authentication/ServiceCollectionExtensions.cs b/Source/TimeWarp.QuickBooks/Authentication/ServiceCollectionExtensions.cs index 28ee690..3282fe4 100644 --- a/Source/TimeWarp.QuickBooks/Authentication/ServiceCollectionExtensions.cs +++ b/Source/TimeWarp.QuickBooks/Authentication/ServiceCollectionExtensions.cs @@ -1,51 +1,51 @@ -namespace TimeWarp.QuickBooks.Authentication; - -using TimeWarp.QuickBooks.Authentication.Models; - -/// -/// Extension methods for registering QuickBooks authentication services. -/// -public static class ServiceCollectionExtensions -{ - /// - /// Adds QuickBooks OAuth services to the service collection. - /// - /// The service collection. - /// The action to configure the QuickBooks OAuth options. - /// The service collection. - public static IServiceCollection AddQuickBooksOAuth - ( - this IServiceCollection services, - Action configureOptions - ) - { - // Register options - services.Configure(configureOptions); - - // Register the OAuth service - services.AddScoped(); - - return services; - } - - /// - /// Adds QuickBooks OAuth services to the service collection. - /// - /// The service collection. - /// The configuration section containing QuickBooks OAuth settings. - /// The service collection. - public static IServiceCollection AddQuickBooksOAuth - ( - this IServiceCollection services, - IConfiguration configuration - ) - { - // Register options from configuration - services.Configure(configuration); - - // Register the OAuth service - services.AddScoped(); - - return services; - } +namespace TimeWarp.QuickBooks.Authentication; + +using TimeWarp.QuickBooks.Authentication.Models; + +/// +/// Extension methods for registering QuickBooks authentication services. +/// +public static class ServiceCollectionExtensions +{ + /// + /// Adds QuickBooks OAuth services to the service collection. + /// + /// The service collection. + /// The action to configure the QuickBooks OAuth options. + /// The service collection. + public static IServiceCollection AddQuickBooksOAuth + ( + this IServiceCollection services, + Action configureOptions + ) + { + // Register options + services.Configure(configureOptions); + + // Register the OAuth service + services.AddScoped(); + + return services; + } + + /// + /// Adds QuickBooks OAuth services to the service collection. + /// + /// The service collection. + /// The configuration section containing QuickBooks OAuth settings. + /// The service collection. + public static IServiceCollection AddQuickBooksOAuth + ( + this IServiceCollection services, + IConfiguration configuration + ) + { + // Register options from configuration + services.Configure(configuration); + + // Register the OAuth service + services.AddScoped(); + + return services; + } } \ No newline at end of file diff --git a/Source/TimeWarp.QuickBooks/Class1.cs b/Source/TimeWarp.QuickBooks/Class1.cs index 4e690e8..ce97382 100644 --- a/Source/TimeWarp.QuickBooks/Class1.cs +++ b/Source/TimeWarp.QuickBooks/Class1.cs @@ -1,6 +1,6 @@ -namespace TimeWarp.QuickBooks; - -public class Class1 -{ - -} +namespace TimeWarp.QuickBooks; + +public class Class1 +{ + +} diff --git a/Source/TimeWarp.QuickBooks/GlobalUsings.cs b/Source/TimeWarp.QuickBooks/GlobalUsings.cs index 67d9b3c..aa2abeb 100644 --- a/Source/TimeWarp.QuickBooks/GlobalUsings.cs +++ b/Source/TimeWarp.QuickBooks/GlobalUsings.cs @@ -1,13 +1,13 @@ -global using System; -global using System.Collections.Generic; -global using System.Net.Http; -global using System.Text; -global using System.Text.Json; -global using System.Threading.Tasks; - -global using Intuit.Ipp.OAuth2PlatformClient; - -global using Microsoft.Extensions.Configuration; -global using Microsoft.Extensions.DependencyInjection; -global using Microsoft.Extensions.Logging; +global using System; +global using System.Collections.Generic; +global using System.Net.Http; +global using System.Text; +global using System.Text.Json; +global using System.Threading.Tasks; + +global using Intuit.Ipp.OAuth2PlatformClient; + +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Options; \ No newline at end of file diff --git a/Source/TimeWarp.QuickBooks/TimeWarp.QuickBooks.csproj b/Source/TimeWarp.QuickBooks/TimeWarp.QuickBooks.csproj index 7cf5906..9343907 100644 --- a/Source/TimeWarp.QuickBooks/TimeWarp.QuickBooks.csproj +++ b/Source/TimeWarp.QuickBooks/TimeWarp.QuickBooks.csproj @@ -1,12 +1,12 @@ - - - - - - - - - - - - + + + + + + + + + + + + diff --git a/Tests/TimeWarp.QuickBooks.Tests/.config/dotnet-tools.json b/Tests/TimeWarp.QuickBooks.Tests/.config/dotnet-tools.json index 6b4224a..c22f07d 100644 --- a/Tests/TimeWarp.QuickBooks.Tests/.config/dotnet-tools.json +++ b/Tests/TimeWarp.QuickBooks.Tests/.config/dotnet-tools.json @@ -1,13 +1,13 @@ -{ - "version": 1, - "isRoot": true, - "tools": { - "fixie.console": { - "version": "4.1.0", - "commands": [ - "fixie" - ], - "rollForward": false - } - } +{ + "version": 1, + "isRoot": true, + "tools": { + "fixie.console": { + "version": "4.1.0", + "commands": [ + "fixie" + ], + "rollForward": false + } + } } \ No newline at end of file diff --git a/Tests/TimeWarp.QuickBooks.Tests/ConventionTests.cs b/Tests/TimeWarp.QuickBooks.Tests/ConventionTests.cs index d431775..45025d9 100644 --- a/Tests/TimeWarp.QuickBooks.Tests/ConventionTests.cs +++ b/Tests/TimeWarp.QuickBooks.Tests/ConventionTests.cs @@ -1,24 +1,24 @@ -namespace TimeWarp.QuickBooks.Tests; - -using Shouldly; -using TimeWarp.Fixie; - -[TestTag(TestTags.Fast)] -public class SimpleNoApplicationTest_Should_ -{ - public static void AlwaysPass() => true.ShouldBeTrue(); - - [Skip("Demonstrates skip attribute")] - public static void SkipExample() => true.ShouldBeFalse(); - - [TestTag(TestTags.Fast)] - public static void TagExample() => true.ShouldBeTrue(); - - [Input(5, 3, 2)] - [Input(8, 5, 3)] - public static void Subtract(int x, int y, int expectedDifference) - { - int result = x - y; - result.ShouldBe(expectedDifference); - } +namespace TimeWarp.QuickBooks.Tests; + +using Shouldly; +using TimeWarp.Fixie; + +[TestTag(TestTags.Fast)] +public class SimpleNoApplicationTest_Should_ +{ + public static void AlwaysPass() => true.ShouldBeTrue(); + + [Skip("Demonstrates skip attribute")] + public static void SkipExample() => true.ShouldBeFalse(); + + [TestTag(TestTags.Fast)] + public static void TagExample() => true.ShouldBeTrue(); + + [Input(5, 3, 2)] + [Input(8, 5, 3)] + public static void Subtract(int x, int y, int expectedDifference) + { + int result = x - y; + result.ShouldBe(expectedDifference); + } } \ No newline at end of file diff --git a/Tests/TimeWarp.QuickBooks.Tests/TestingConvention.cs b/Tests/TimeWarp.QuickBooks.Tests/TestingConvention.cs index 8d9b9e0..e94123f 100644 --- a/Tests/TimeWarp.QuickBooks.Tests/TestingConvention.cs +++ b/Tests/TimeWarp.QuickBooks.Tests/TestingConvention.cs @@ -1,3 +1,3 @@ -namespace TimeWarp.QuickBooks.Tests; - +namespace TimeWarp.QuickBooks.Tests; + public class TestingConvention : TimeWarp.Fixie.TestingConvention { } \ No newline at end of file diff --git a/Tests/TimeWarp.QuickBooks.Tests/TimeWarp.QuickBooks.Tests.csproj b/Tests/TimeWarp.QuickBooks.Tests/TimeWarp.QuickBooks.Tests.csproj index 0bac010..f63c6c9 100644 --- a/Tests/TimeWarp.QuickBooks.Tests/TimeWarp.QuickBooks.Tests.csproj +++ b/Tests/TimeWarp.QuickBooks.Tests/TimeWarp.QuickBooks.Tests.csproj @@ -1,14 +1,14 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/copy-workflows.ps1 b/copy-workflows.ps1 index 3d29497..f879dc1 100644 --- a/copy-workflows.ps1 +++ b/copy-workflows.ps1 @@ -1,168 +1,168 @@ -#!/usr/bin/env pwsh - -<# -.SYNOPSIS - Copies workflow files from workflows-temp to their proper locations. - -.DESCRIPTION - This script performs the manual step of the two-step sync workflow deployment process. - It copies files from workflows-temp/ to .github/workflows/ and .github/scripts/ - directories due to GitHub Apps limitations that prevent direct workflow file updates. - -.PARAMETER WhatIf - Shows what would be copied without actually performing the operations. - -.EXAMPLE - ./copy-workflows.ps1 - -.EXAMPLE - ./copy-workflows.ps1 -WhatIf -#> - -[CmdletBinding()] -param( - [switch]$WhatIf -) - -# Set error handling -$ErrorActionPreference = 'Stop' - -function Write-Info { - param([string]$Message) - Write-Host "ℹ️ $Message" -ForegroundColor Blue -} - -function Write-Success { - param([string]$Message) - Write-Host "✅ $Message" -ForegroundColor Green -} - -function Write-Warning { - param([string]$Message) - Write-Host "⚠️ $Message" -ForegroundColor Yellow -} - -function Write-Error { - param([string]$Message) - Write-Host "❌ $Message" -ForegroundColor Red -} - -function Ensure-Directory { - param([string]$Path) - - if (-not (Test-Path -Path $Path)) { - if ($WhatIf) { - Write-Info "[WHAT-IF] Would create directory: $Path" - } else { - New-Item -Path $Path -ItemType Directory -Force | Out-Null - Write-Success "Created directory: $Path" - } - } -} - -function Copy-WorkflowFile { - param( - [string]$Source, - [string]$Destination, - [string]$Description - ) - - if (-not (Test-Path -Path $Source)) { - Write-Warning "Source file not found: $Source" - return $false - } - - if ($WhatIf) { - Write-Info "[WHAT-IF] Would copy $Description`: $Source -> $Destination" - return $true - } - - try { - Copy-Item -Path $Source -Destination $Destination -Force - Write-Success "Copied $Description`: $Destination" - return $true - } - catch { - Write-Error "Failed to copy $Description`: $($_.Exception.Message)" - return $false - } -} - -# === Main Script Execution === - -try { - Write-Info "Starting workflow files deployment $(if ($WhatIf) { '(WHAT-IF MODE)' })" - - # Check if workflows-temp directory exists - if (-not (Test-Path -Path "workflows-temp")) { - throw "workflows-temp directory not found. Please run the sync workflow generation first." - } - - # Create necessary directories - Ensure-Directory -Path ".github" - Ensure-Directory -Path ".github/workflows" - Ensure-Directory -Path ".github/scripts" - - # Define file mappings - $fileMappings = @( - @{ - Source = "workflows-temp/sync-configurable-files.yml" - Destination = ".github/workflows/sync-configurable-files.yml" - Description = "Sync workflow" - }, - @{ - Source = "workflows-temp/sync-config.yml" - Destination = ".github/scripts/sync-config.yml" - Description = "Sync configuration" - }, - @{ - Source = "workflows-temp/sync-configurable-files.ps1" - Destination = ".github/scripts/sync-configurable-files.ps1" - Description = "Sync PowerShell script" - } - ) - - # Copy files - $successCount = 0 - $totalCount = $fileMappings.Count - - foreach ($mapping in $fileMappings) { - if (Copy-WorkflowFile -Source $mapping.Source -Destination $mapping.Destination -Description $mapping.Description) { - $successCount++ - } - } - - # Summary - if ($WhatIf) { - Write-Info "What-if analysis complete" - Write-Info "Files that would be copied: $successCount/$totalCount" - } else { - if ($successCount -eq $totalCount) { - Write-Success "All workflow files deployed successfully! ($successCount/$totalCount)" - Write-Info "" - Write-Info "Next steps:" - Write-Info "1. Commit the new .github/ directory files" - Write-Info "2. Test the workflow with: gh workflow run sync-configurable-files.yml --ref $(git branch --show-current)" - Write-Info "3. Or trigger manually in GitHub Actions web interface" - Write-Info "" - Write-Info "The workflow will:" - Write-Info "- Sync configuration files from TimeWarpEngineering/timewarp-architecture" - Write-Info "- Create a pull request with the changes" - Write-Info "- Preserve local customizations where configured" - } else { - Write-Warning "Deployment completed with some failures ($successCount/$totalCount files copied)" - Write-Info "Please review the errors above and try again" - } - } - -} catch { - Write-Error "Deployment failed: $($_.Exception.Message)" - exit 1 -} - -# Instructions for cleanup (optional) -if (-not $WhatIf -and $successCount -eq $totalCount) { - Write-Info "" - Write-Info "Optional: You can now remove the workflows-temp directory:" - Write-Info " Remove-Item -Path workflows-temp -Recurse -Force" +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Copies workflow files from workflows-temp to their proper locations. + +.DESCRIPTION + This script performs the manual step of the two-step sync workflow deployment process. + It copies files from workflows-temp/ to .github/workflows/ and .github/scripts/ + directories due to GitHub Apps limitations that prevent direct workflow file updates. + +.PARAMETER WhatIf + Shows what would be copied without actually performing the operations. + +.EXAMPLE + ./copy-workflows.ps1 + +.EXAMPLE + ./copy-workflows.ps1 -WhatIf +#> + +[CmdletBinding()] +param( + [switch]$WhatIf +) + +# Set error handling +$ErrorActionPreference = 'Stop' + +function Write-Info { + param([string]$Message) + Write-Host "ℹ️ $Message" -ForegroundColor Blue +} + +function Write-Success { + param([string]$Message) + Write-Host "✅ $Message" -ForegroundColor Green +} + +function Write-Warning { + param([string]$Message) + Write-Host "⚠️ $Message" -ForegroundColor Yellow +} + +function Write-Error { + param([string]$Message) + Write-Host "❌ $Message" -ForegroundColor Red +} + +function Ensure-Directory { + param([string]$Path) + + if (-not (Test-Path -Path $Path)) { + if ($WhatIf) { + Write-Info "[WHAT-IF] Would create directory: $Path" + } else { + New-Item -Path $Path -ItemType Directory -Force | Out-Null + Write-Success "Created directory: $Path" + } + } +} + +function Copy-WorkflowFile { + param( + [string]$Source, + [string]$Destination, + [string]$Description + ) + + if (-not (Test-Path -Path $Source)) { + Write-Warning "Source file not found: $Source" + return $false + } + + if ($WhatIf) { + Write-Info "[WHAT-IF] Would copy $Description`: $Source -> $Destination" + return $true + } + + try { + Copy-Item -Path $Source -Destination $Destination -Force + Write-Success "Copied $Description`: $Destination" + return $true + } + catch { + Write-Error "Failed to copy $Description`: $($_.Exception.Message)" + return $false + } +} + +# === Main Script Execution === + +try { + Write-Info "Starting workflow files deployment $(if ($WhatIf) { '(WHAT-IF MODE)' })" + + # Check if workflows-temp directory exists + if (-not (Test-Path -Path "workflows-temp")) { + throw "workflows-temp directory not found. Please run the sync workflow generation first." + } + + # Create necessary directories + Ensure-Directory -Path ".github" + Ensure-Directory -Path ".github/workflows" + Ensure-Directory -Path ".github/scripts" + + # Define file mappings + $fileMappings = @( + @{ + Source = "workflows-temp/sync-configurable-files.yml" + Destination = ".github/workflows/sync-configurable-files.yml" + Description = "Sync workflow" + }, + @{ + Source = "workflows-temp/sync-config.yml" + Destination = ".github/scripts/sync-config.yml" + Description = "Sync configuration" + }, + @{ + Source = "workflows-temp/sync-configurable-files.ps1" + Destination = ".github/scripts/sync-configurable-files.ps1" + Description = "Sync PowerShell script" + } + ) + + # Copy files + $successCount = 0 + $totalCount = $fileMappings.Count + + foreach ($mapping in $fileMappings) { + if (Copy-WorkflowFile -Source $mapping.Source -Destination $mapping.Destination -Description $mapping.Description) { + $successCount++ + } + } + + # Summary + if ($WhatIf) { + Write-Info "What-if analysis complete" + Write-Info "Files that would be copied: $successCount/$totalCount" + } else { + if ($successCount -eq $totalCount) { + Write-Success "All workflow files deployed successfully! ($successCount/$totalCount)" + Write-Info "" + Write-Info "Next steps:" + Write-Info "1. Commit the new .github/ directory files" + Write-Info "2. Test the workflow with: gh workflow run sync-configurable-files.yml --ref $(git branch --show-current)" + Write-Info "3. Or trigger manually in GitHub Actions web interface" + Write-Info "" + Write-Info "The workflow will:" + Write-Info "- Sync configuration files from TimeWarpEngineering/timewarp-architecture" + Write-Info "- Create a pull request with the changes" + Write-Info "- Preserve local customizations where configured" + } else { + Write-Warning "Deployment completed with some failures ($successCount/$totalCount files copied)" + Write-Info "Please review the errors above and try again" + } + } + +} catch { + Write-Error "Deployment failed: $($_.Exception.Message)" + exit 1 +} + +# Instructions for cleanup (optional) +if (-not $WhatIf -and $successCount -eq $totalCount) { + Write-Info "" + Write-Info "Optional: You can now remove the workflows-temp directory:" + Write-Info " Remove-Item -Path workflows-temp -Recurse -Force" } \ No newline at end of file diff --git a/insert-variables.ps1 b/insert-variables.ps1 index d33c89a..60d72cf 100644 --- a/insert-variables.ps1 +++ b/insert-variables.ps1 @@ -1,61 +1,61 @@ -#requires -Version 7.0 -# PowerShell script with Push-Location, Pop-Location, PSScriptRoot, and $HOME - -# Push the current directory and switch to the script's root directory -Push-Location -Path $PSScriptRoot - -try { - # Get environment variables - $OS = (Get-CimInstance Win32_OperatingSystem).Caption - $SHELL = "cmd" # Hardcoded as in the original script - $WORKSPACE = Get-Location | Select-Object -ExpandProperty Path - - # Construct paths - $GLOBAL_SETTINGS = "$env:APPDATA\Code\User\globalStorage\rooveterinaryinc.roo-cline\settings\cline_custom_modes.json" - $MCP_LOCATION = "$HOME\.local\share\Roo-Code\MCP" - $MCP_SETTINGS = "$env:APPDATA\Code\User\globalStorage\rooveterinaryinc.roo-cline\settings\cline_mcp_settings.json" - - # Directory setup - $ROO_DIR = "$WORKSPACE\.roo" - - # Check if the .roo directory exists - if (-not (Test-Path -Path $ROO_DIR)) { - Write-Error ".roo directory not found in $WORKSPACE" - exit 1 - } - - # Process files in .roo directory - Write-Host "Working Directory: $WORKSPACE" - $ErrorActionPreference = 'Stop' - - $files = Get-ChildItem -Path $ROO_DIR -Filter 'system-prompt-*' - foreach ($file in $files) { - Write-Host "Processing: $($file.FullName)" - - # Read file content - $content = Get-Content -Path $file.FullName -Raw - - # Replace placeholders - $content = $content -replace 'OS_PLACEHOLDER', $OS ` - -replace 'SHELL_PLACEHOLDER', $SHELL ` - -replace 'HOME_PLACEHOLDER', $HOME ` - -replace 'WORKSPACE_PLACEHOLDER', $WORKSPACE ` - -replace 'GLOBAL_SETTINGS_PLACEHOLDER', $GLOBAL_SETTINGS ` - -replace 'MCP_LOCATION_PLACEHOLDER', $MCP_LOCATION ` - -replace 'MCP_SETTINGS_PLACEHOLDER', $MCP_SETTINGS - - # Write updated content back to file - Set-Content -Path $file.FullName -Value $content -NoNewline - Write-Host "Completed: $($file.FullName)" - } -} -catch { - Write-Error "Error processing files: $_" - exit 1 -} -finally { - # Restore the original directory - Pop-Location -} - +#requires -Version 7.0 +# PowerShell script with Push-Location, Pop-Location, PSScriptRoot, and $HOME + +# Push the current directory and switch to the script's root directory +Push-Location -Path $PSScriptRoot + +try { + # Get environment variables + $OS = (Get-CimInstance Win32_OperatingSystem).Caption + $SHELL = "cmd" # Hardcoded as in the original script + $WORKSPACE = Get-Location | Select-Object -ExpandProperty Path + + # Construct paths + $GLOBAL_SETTINGS = "$env:APPDATA\Code\User\globalStorage\rooveterinaryinc.roo-cline\settings\cline_custom_modes.json" + $MCP_LOCATION = "$HOME\.local\share\Roo-Code\MCP" + $MCP_SETTINGS = "$env:APPDATA\Code\User\globalStorage\rooveterinaryinc.roo-cline\settings\cline_mcp_settings.json" + + # Directory setup + $ROO_DIR = "$WORKSPACE\.roo" + + # Check if the .roo directory exists + if (-not (Test-Path -Path $ROO_DIR)) { + Write-Error ".roo directory not found in $WORKSPACE" + exit 1 + } + + # Process files in .roo directory + Write-Host "Working Directory: $WORKSPACE" + $ErrorActionPreference = 'Stop' + + $files = Get-ChildItem -Path $ROO_DIR -Filter 'system-prompt-*' + foreach ($file in $files) { + Write-Host "Processing: $($file.FullName)" + + # Read file content + $content = Get-Content -Path $file.FullName -Raw + + # Replace placeholders + $content = $content -replace 'OS_PLACEHOLDER', $OS ` + -replace 'SHELL_PLACEHOLDER', $SHELL ` + -replace 'HOME_PLACEHOLDER', $HOME ` + -replace 'WORKSPACE_PLACEHOLDER', $WORKSPACE ` + -replace 'GLOBAL_SETTINGS_PLACEHOLDER', $GLOBAL_SETTINGS ` + -replace 'MCP_LOCATION_PLACEHOLDER', $MCP_LOCATION ` + -replace 'MCP_SETTINGS_PLACEHOLDER', $MCP_SETTINGS + + # Write updated content back to file + Set-Content -Path $file.FullName -Value $content -NoNewline + Write-Host "Completed: $($file.FullName)" + } +} +catch { + Write-Error "Error processing files: $_" + exit 1 +} +finally { + # Restore the original directory + Pop-Location +} + Write-Host "Done." \ No newline at end of file diff --git a/workflows-temp/README.md b/workflows-temp/README.md index 397382e..f6c1c75 100644 --- a/workflows-temp/README.md +++ b/workflows-temp/README.md @@ -1,133 +1,133 @@ -# Sync Configurable Files Workflow - -This directory contains the sync workflow system for maintaining configuration file consistency with the TimeWarp Architecture repository. - -## Overview - -The sync workflow automatically downloads and updates configuration files from the parent `TimeWarpEngineering/timewarp-architecture` repository to ensure organizational consistency across all TimeWarp projects. - -## Two-Step Deployment Process - -Due to GitHub Apps security limitations that prevent direct workflow file updates, this system uses a two-step deployment: - -### Step 1: Automated Generation (Completed) -✅ Workflow files have been generated in `workflows-temp/` directory: -- `sync-configurable-files.yml` - GitHub Actions workflow -- `sync-config.yml` - Configuration defining which files to sync -- `sync-configurable-files.ps1` - PowerShell script handling the sync logic - -### Step 2: Manual Deployment (Required) -🔄 **Next Action Required:** Run the deployment script to move files to their final locations: - -```powershell -# Preview what will be copied (recommended first) -./copy-workflows.ps1 -WhatIf - -# Deploy the workflow files -./copy-workflows.ps1 -``` - -This will copy: -- `workflows-temp/sync-configurable-files.yml` → `.github/workflows/sync-configurable-files.yml` -- `workflows-temp/sync-config.yml` → `.github/scripts/sync-config.yml` -- `workflows-temp/sync-configurable-files.ps1` → `.github/scripts/sync-configurable-files.ps1` - -## Configuration for .NET Repository - -The sync configuration has been tailored for this .NET 9.0 QuickBooks integration library: - -### Files That Will Be Synced -- ✅ `.editorconfig` - Code formatting standards -- ✅ `global.json` - .NET SDK version pinning -- ✅ `.gitignore` - Standard ignore patterns (merged with local additions) -- ⚠️ `Directory.Build.props` - Build configuration (local customizations preserved) -- ⚠️ `Directory.Build.targets` - Build targets (if available in parent repo) - -### Files Excluded from Sync -- Local project metadata (version info, package details) -- Environment-specific configurations -- Repository-specific customizations - -### GitIgnore Merge Strategy -The `.gitignore` file uses a special merge strategy: -- Global patterns are synced from the parent repository -- Local repository-specific patterns are preserved -- Section markers separate global vs. local content - -## Workflow Features - -### Scheduling -- Runs automatically every Sunday at 2 AM UTC -- Can be triggered manually via GitHub Actions interface -- Supports dry-run mode for testing - -### Pull Request Creation -- Automatically creates PRs when changes are detected -- Includes detailed change summaries -- Uses branch naming: `sync/configurable-files-{run-number}` - -### Local Preservation -- Preserves repository-specific package metadata -- Maintains local version information -- Keeps local .gitignore additions - -## Testing the Workflow - -After deployment, test the workflow: - -```bash -# Using GitHub CLI -gh workflow run sync-configurable-files.yml --ref $(git branch --show-current) - -# Or trigger manually in GitHub Actions web interface -``` - -For testing, use the dry-run option: -1. Go to Actions → Sync Configurable Files -2. Click "Run workflow" -3. Set "Perform a dry run" to `true` -4. Click "Run workflow" - -## Monitoring - -The workflow will: -1. Download files from `TimeWarpEngineering/timewarp-architecture` -2. Apply appropriate merge strategies -3. Create backups of existing files -4. Generate a pull request if changes are detected -5. Include detailed logs of all operations - -## Troubleshooting - -### Common Issues - -**Workflow fails with "Configuration file not found":** -- Ensure `copy-workflows.ps1` was run successfully -- Check that `.github/scripts/sync-config.yml` exists - -**No changes detected when changes are expected:** -- Check the parent repository for recent updates -- Verify file paths in `sync-config.yml` are correct -- Review workflow logs for download errors - -**Local customizations lost:** -- Check preserve_sections configuration in `sync-config.yml` -- File an issue to update preservation rules - -### Getting Help - -1. Check workflow run logs in GitHub Actions -2. Review the sync configuration in `.github/scripts/sync-config.yml` -3. Test locally with dry-run mode -4. File issues in this repository for sync-specific problems - -## Security Notes - -- Workflow runs with `contents: write` and `pull-requests: write` permissions -- No secrets are synced - only public configuration files -- All changes go through pull request review process -- Backups are created before any file modifications - ---- - +# Sync Configurable Files Workflow + +This directory contains the sync workflow system for maintaining configuration file consistency with the TimeWarp Architecture repository. + +## Overview + +The sync workflow automatically downloads and updates configuration files from the parent `TimeWarpEngineering/timewarp-architecture` repository to ensure organizational consistency across all TimeWarp projects. + +## Two-Step Deployment Process + +Due to GitHub Apps security limitations that prevent direct workflow file updates, this system uses a two-step deployment: + +### Step 1: Automated Generation (Completed) +✅ Workflow files have been generated in `workflows-temp/` directory: +- `sync-configurable-files.yml` - GitHub Actions workflow +- `sync-config.yml` - Configuration defining which files to sync +- `sync-configurable-files.ps1` - PowerShell script handling the sync logic + +### Step 2: Manual Deployment (Required) +🔄 **Next Action Required:** Run the deployment script to move files to their final locations: + +```powershell +# Preview what will be copied (recommended first) +./copy-workflows.ps1 -WhatIf + +# Deploy the workflow files +./copy-workflows.ps1 +``` + +This will copy: +- `workflows-temp/sync-configurable-files.yml` → `.github/workflows/sync-configurable-files.yml` +- `workflows-temp/sync-config.yml` → `.github/scripts/sync-config.yml` +- `workflows-temp/sync-configurable-files.ps1` → `.github/scripts/sync-configurable-files.ps1` + +## Configuration for .NET Repository + +The sync configuration has been tailored for this .NET 9.0 QuickBooks integration library: + +### Files That Will Be Synced +- ✅ `.editorconfig` - Code formatting standards +- ✅ `global.json` - .NET SDK version pinning +- ✅ `.gitignore` - Standard ignore patterns (merged with local additions) +- ⚠️ `Directory.Build.props` - Build configuration (local customizations preserved) +- ⚠️ `Directory.Build.targets` - Build targets (if available in parent repo) + +### Files Excluded from Sync +- Local project metadata (version info, package details) +- Environment-specific configurations +- Repository-specific customizations + +### GitIgnore Merge Strategy +The `.gitignore` file uses a special merge strategy: +- Global patterns are synced from the parent repository +- Local repository-specific patterns are preserved +- Section markers separate global vs. local content + +## Workflow Features + +### Scheduling +- Runs automatically every Sunday at 2 AM UTC +- Can be triggered manually via GitHub Actions interface +- Supports dry-run mode for testing + +### Pull Request Creation +- Automatically creates PRs when changes are detected +- Includes detailed change summaries +- Uses branch naming: `sync/configurable-files-{run-number}` + +### Local Preservation +- Preserves repository-specific package metadata +- Maintains local version information +- Keeps local .gitignore additions + +## Testing the Workflow + +After deployment, test the workflow: + +```bash +# Using GitHub CLI +gh workflow run sync-configurable-files.yml --ref $(git branch --show-current) + +# Or trigger manually in GitHub Actions web interface +``` + +For testing, use the dry-run option: +1. Go to Actions → Sync Configurable Files +2. Click "Run workflow" +3. Set "Perform a dry run" to `true` +4. Click "Run workflow" + +## Monitoring + +The workflow will: +1. Download files from `TimeWarpEngineering/timewarp-architecture` +2. Apply appropriate merge strategies +3. Create backups of existing files +4. Generate a pull request if changes are detected +5. Include detailed logs of all operations + +## Troubleshooting + +### Common Issues + +**Workflow fails with "Configuration file not found":** +- Ensure `copy-workflows.ps1` was run successfully +- Check that `.github/scripts/sync-config.yml` exists + +**No changes detected when changes are expected:** +- Check the parent repository for recent updates +- Verify file paths in `sync-config.yml` are correct +- Review workflow logs for download errors + +**Local customizations lost:** +- Check preserve_sections configuration in `sync-config.yml` +- File an issue to update preservation rules + +### Getting Help + +1. Check workflow run logs in GitHub Actions +2. Review the sync configuration in `.github/scripts/sync-config.yml` +3. Test locally with dry-run mode +4. File issues in this repository for sync-specific problems + +## Security Notes + +- Workflow runs with `contents: write` and `pull-requests: write` permissions +- No secrets are synced - only public configuration files +- All changes go through pull request review process +- Backups are created before any file modifications + +--- + **Generated with [Claude Code](https://claude.ai/code)** \ No newline at end of file diff --git a/workflows-temp/sync-config.yml b/workflows-temp/sync-config.yml index 3b81f0b..08fe0c7 100644 --- a/workflows-temp/sync-config.yml +++ b/workflows-temp/sync-config.yml @@ -1,101 +1,101 @@ -# Configuration for sync-configurable-files workflow -# This file defines which files should be synced from the parent TimeWarp Architecture repository - -# Source repository configuration -source_repo: TimeWarpEngineering/timewarp-architecture -source_branch: master - -# Repository type - determines which file sets to sync -repository_type: dotnet - -# Files to sync from the parent repository -# Each entry specifies the source path and destination path -sync_files: - # .NET Build Configuration Files - - source_path: TimeWarp.Architecture/Directory.Build.props - destination_path: Directory.Build.props - merge_strategy: replace_with_local_preservation - preserve_sections: - - "" - - "" - - "" - - - source_path: TimeWarp.Architecture/Directory.Build.targets - destination_path: Directory.Build.targets - merge_strategy: replace - create_if_missing: true - - - source_path: TimeWarp.Architecture/global.json - destination_path: global.json - merge_strategy: replace - create_if_missing: true - - # Development Configuration Files - - source_path: TimeWarp.Architecture/.editorconfig - destination_path: .editorconfig - merge_strategy: replace - create_if_missing: true - - # GitIgnore with special merge handling - - source_path: TimeWarp.Architecture/.gitignore - destination_path: .gitignore - merge_strategy: merge_sections - section_markers: - global_start: "# ----- Global .gitignore (synced from TimeWarp Architecture) -----" - global_end: "# ----- Repository-specific .gitignore -----" - local_start: "# ----- Repository-specific .gitignore -----" - - # GitHub Workflow Templates (optional - only if they exist in parent) - - source_path: TimeWarp.Architecture/.github/workflows/build.yml - destination_path: .github/workflows/build.yml - merge_strategy: replace - create_if_missing: false - optional: true - - - source_path: TimeWarp.Architecture/.github/workflows/codeql.yml - destination_path: .github/workflows/codeql.yml - merge_strategy: replace - create_if_missing: false - optional: true - -# Exclusion patterns - files that should never be synced -exclude_patterns: - - "*.user" - - "*.suo" - - "**/bin/**" - - "**/obj/**" - - "appsettings.*.json" - - "launchSettings.json" - -# Local preservation rules -preserve_local: - # Preserve local package metadata and version info in Directory.Build.props - - file: Directory.Build.props - sections: - - xpath: "//PropertyGroup[Description or PackageProjectUrl or RepositoryUrl]" - - xpath: "//PropertyGroup[VersionPrefix or VersionSuffix or AssemblyVersion]" - - xpath: "//ItemGroup[None[@Include and contains(@Include, 'Logo')]]" - - # Preserve local .gitignore additions - - file: .gitignore - preserve_after_marker: "# ----- Repository-specific .gitignore -----" - -# Post-sync validation -validation: - # Ensure these files compile/parse correctly after sync - build_check: true - powershell_syntax_check: true - - # Files that must exist after sync - required_files: - - Directory.Build.props - - .gitignore - -# Notification settings -notifications: - create_pr: true - pr_reviewers: [] # Add default reviewers if needed - pr_labels: - - "automation" - - "configuration" +# Configuration for sync-configurable-files workflow +# This file defines which files should be synced from the parent TimeWarp Architecture repository + +# Source repository configuration +source_repo: TimeWarpEngineering/timewarp-architecture +source_branch: master + +# Repository type - determines which file sets to sync +repository_type: dotnet + +# Files to sync from the parent repository +# Each entry specifies the source path and destination path +sync_files: + # .NET Build Configuration Files + - source_path: TimeWarp.Architecture/Directory.Build.props + destination_path: Directory.Build.props + merge_strategy: replace_with_local_preservation + preserve_sections: + - "" + - "" + - "" + + - source_path: TimeWarp.Architecture/Directory.Build.targets + destination_path: Directory.Build.targets + merge_strategy: replace + create_if_missing: true + + - source_path: TimeWarp.Architecture/global.json + destination_path: global.json + merge_strategy: replace + create_if_missing: true + + # Development Configuration Files + - source_path: TimeWarp.Architecture/.editorconfig + destination_path: .editorconfig + merge_strategy: replace + create_if_missing: true + + # GitIgnore with special merge handling + - source_path: TimeWarp.Architecture/.gitignore + destination_path: .gitignore + merge_strategy: merge_sections + section_markers: + global_start: "# ----- Global .gitignore (synced from TimeWarp Architecture) -----" + global_end: "# ----- Repository-specific .gitignore -----" + local_start: "# ----- Repository-specific .gitignore -----" + + # GitHub Workflow Templates (optional - only if they exist in parent) + - source_path: TimeWarp.Architecture/.github/workflows/build.yml + destination_path: .github/workflows/build.yml + merge_strategy: replace + create_if_missing: false + optional: true + + - source_path: TimeWarp.Architecture/.github/workflows/codeql.yml + destination_path: .github/workflows/codeql.yml + merge_strategy: replace + create_if_missing: false + optional: true + +# Exclusion patterns - files that should never be synced +exclude_patterns: + - "*.user" + - "*.suo" + - "**/bin/**" + - "**/obj/**" + - "appsettings.*.json" + - "launchSettings.json" + +# Local preservation rules +preserve_local: + # Preserve local package metadata and version info in Directory.Build.props + - file: Directory.Build.props + sections: + - xpath: "//PropertyGroup[Description or PackageProjectUrl or RepositoryUrl]" + - xpath: "//PropertyGroup[VersionPrefix or VersionSuffix or AssemblyVersion]" + - xpath: "//ItemGroup[None[@Include and contains(@Include, 'Logo')]]" + + # Preserve local .gitignore additions + - file: .gitignore + preserve_after_marker: "# ----- Repository-specific .gitignore -----" + +# Post-sync validation +validation: + # Ensure these files compile/parse correctly after sync + build_check: true + powershell_syntax_check: true + + # Files that must exist after sync + required_files: + - Directory.Build.props + - .gitignore + +# Notification settings +notifications: + create_pr: true + pr_reviewers: [] # Add default reviewers if needed + pr_labels: + - "automation" + - "configuration" - "chore" \ No newline at end of file diff --git a/workflows-temp/sync-configurable-files.ps1 b/workflows-temp/sync-configurable-files.ps1 index 5324897..dea4f3c 100644 --- a/workflows-temp/sync-configurable-files.ps1 +++ b/workflows-temp/sync-configurable-files.ps1 @@ -1,371 +1,371 @@ -#!/usr/bin/env pwsh - -<# -.SYNOPSIS - Syncs configurable files from the parent TimeWarp Architecture repository. - -.DESCRIPTION - This script downloads and syncs configuration files from the TimeWarpEngineering/timewarp-architecture - repository to maintain consistency with organizational standards. It supports different merge strategies - and preserves local customizations where appropriate. - -.PARAMETER DryRun - If specified, performs a dry run without making any changes. - -.PARAMETER ConfigPath - Path to the sync configuration file. Defaults to .github/scripts/sync-config.yml - -.EXAMPLE - ./sync-configurable-files.ps1 - -.EXAMPLE - ./sync-configurable-files.ps1 -DryRun -#> - -[CmdletBinding()] -param( - [switch]$DryRun, - [string]$ConfigPath = ".github/scripts/sync-config.yml" -) - -# Set error handling -$ErrorActionPreference = 'Stop' -$ProgressPreference = 'SilentlyContinue' - -# Initialize variables -$script:ChangesMade = $false -$script:SyncedFiles = @() - -function Write-Info { - param([string]$Message) - Write-Host "ℹ️ $Message" -ForegroundColor Blue -} - -function Write-Success { - param([string]$Message) - Write-Host "✅ $Message" -ForegroundColor Green -} - -function Write-Warning { - param([string]$Message) - Write-Host "⚠️ $Message" -ForegroundColor Yellow -} - -function Write-Error { - param([string]$Message) - Write-Host "❌ $Message" -ForegroundColor Red -} - -function Get-GitHubContent { - param( - [string]$Repository, - [string]$Path, - [string]$Branch = "master" - ) - - $uri = "https://api.github.com/repos/$Repository/contents/$Path" - $headers = @{ - 'Accept' = 'application/vnd.github.v3.raw' - 'User-Agent' = 'TimeWarp-Sync-Script' - } - - if ($env:GITHUB_TOKEN) { - $headers['Authorization'] = "Bearer $env:GITHUB_TOKEN" - } - - try { - $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get - return $response - } - catch { - if ($_.Exception.Response.StatusCode -eq 404) { - return $null - } - throw - } -} - -function Test-FileExists { - param([string]$Path) - return Test-Path -Path $Path -PathType Leaf -} - -function Backup-File { - param([string]$FilePath) - - if (Test-FileExists -Path $FilePath) { - $backupPath = "$FilePath.backup.$(Get-Date -Format 'yyyyMMdd-HHmmss')" - Copy-Item -Path $FilePath -Destination $backupPath -Force - Write-Info "Created backup: $backupPath" - return $backupPath - } - return $null -} - -function Merge-GitIgnoreFile { - param( - [string]$SourceContent, - [string]$DestinationPath, - [hashtable]$SectionMarkers - ) - - $globalStart = $SectionMarkers.global_start - $globalEnd = $SectionMarkers.global_end - $localStart = $SectionMarkers.local_start - - $localContent = "" - $hasLocalSection = $false - - # Read existing local content if file exists - if (Test-FileExists -Path $DestinationPath) { - $existingContent = Get-Content -Path $DestinationPath -Raw - - # Extract local section if it exists - if ($existingContent -match "(?s)$([regex]::Escape($localStart))(.*)$") { - $localContent = $matches[1].Trim() - $hasLocalSection = $true - Write-Info "Preserving existing local .gitignore section" - } - } - - # Build new content - $newContent = @( - $globalStart, - "# This section is automatically synced - do not edit manually", - $SourceContent.Trim(), - "", - $globalEnd, - "# Add repository-specific ignore patterns below this line" - ) - - if ($hasLocalSection -and $localContent) { - $newContent += $localContent - } - - return ($newContent -join "`n") -} - -function Merge-FileWithLocalPreservation { - param( - [string]$SourceContent, - [string]$DestinationPath, - [array]$PreserveSections - ) - - # For now, implement simple replacement - # In a full implementation, this would parse XML/YAML and preserve specific sections - Write-Warning "Local preservation merge not fully implemented - using replace strategy" - return $SourceContent -} - -function Sync-File { - param( - [hashtable]$FileConfig, - [hashtable]$Config - ) - - $sourcePath = $FileConfig.source_path - $destPath = $FileConfig.destination_path - $mergeStrategy = $FileConfig.merge_strategy -or "replace" - $isOptional = $FileConfig.optional -eq $true - $createIfMissing = $FileConfig.create_if_missing -ne $false - - Write-Info "Syncing: $sourcePath -> $destPath" - - # Download source content - try { - $sourceContent = Get-GitHubContent -Repository $Config.source_repo -Path $sourcePath -Branch $Config.source_branch - - if (-not $sourceContent) { - if ($isOptional) { - Write-Info "Optional file not found in source repository: $sourcePath" - return $false - } else { - throw "Required file not found in source repository: $sourcePath" - } - } - } - catch { - if ($isOptional) { - Write-Warning "Could not download optional file: $sourcePath - $($_.Exception.Message)" - return $false - } else { - throw "Failed to download required file: $sourcePath - $($_.Exception.Message)" - } - } - - # Ensure destination directory exists - $destDir = Split-Path -Path $destPath -Parent - if ($destDir -and -not (Test-Path -Path $destDir)) { - if (-not $DryRun) { - New-Item -Path $destDir -ItemType Directory -Force | Out-Null - } - Write-Info "Created directory: $destDir" - } - - # Handle different merge strategies - $finalContent = switch ($mergeStrategy) { - "replace" { - $sourceContent - } - "merge_sections" { - if ($FileConfig.section_markers) { - Merge-GitIgnoreFile -SourceContent $sourceContent -DestinationPath $destPath -SectionMarkers $FileConfig.section_markers - } else { - $sourceContent - } - } - "replace_with_local_preservation" { - if ($FileConfig.preserve_sections) { - Merge-FileWithLocalPreservation -SourceContent $sourceContent -DestinationPath $destPath -PreserveSections $FileConfig.preserve_sections - } else { - $sourceContent - } - } - default { - $sourceContent - } - } - - # Check if content has changed - $hasChanged = $true - if (Test-FileExists -Path $destPath) { - $existingContent = Get-Content -Path $destPath -Raw - $hasChanged = $existingContent -ne $finalContent - } - - if ($hasChanged) { - if (-not $DryRun) { - # Create backup if file exists - Backup-File -FilePath $destPath - - # Write new content - $finalContent | Out-File -FilePath $destPath -Encoding UTF8 -NoNewline - } - - Write-Success "$(if ($DryRun) { '[DRY RUN] ' })Updated: $destPath" - $script:ChangesMade = $true - $script:SyncedFiles += $destPath - return $true - } else { - Write-Info "No changes needed: $destPath" - return $false - } -} - -function Test-Configuration { - param([hashtable]$Config) - - Write-Info "Validating configuration..." - - if (-not $Config.source_repo) { - throw "Configuration missing required field: source_repo" - } - - if (-not $Config.sync_files -or $Config.sync_files.Count -eq 0) { - throw "Configuration missing required field: sync_files" - } - - Write-Success "Configuration validation passed" -} - -function Import-SyncConfiguration { - param([string]$ConfigPath) - - if (-not (Test-Path -Path $ConfigPath)) { - throw "Configuration file not found: $ConfigPath" - } - - try { - # Simple YAML parsing - in production, use a proper YAML parser - $content = Get-Content -Path $ConfigPath -Raw - - # For this implementation, we'll use a simplified approach - # In production, you'd want to use PowerShell-Yaml module - Write-Warning "Using simplified YAML parsing - consider using PowerShell-Yaml module for production" - - # Return a hardcoded configuration based on the created config file - return @{ - source_repo = "TimeWarpEngineering/timewarp-architecture" - source_branch = "master" - repository_type = "dotnet" - sync_files = @( - @{ - source_path = "TimeWarp.Architecture/.editorconfig" - destination_path = ".editorconfig" - merge_strategy = "replace" - create_if_missing = $true - }, - @{ - source_path = "TimeWarp.Architecture/global.json" - destination_path = "global.json" - merge_strategy = "replace" - create_if_missing = $true - }, - @{ - source_path = "TimeWarp.Architecture/.gitignore" - destination_path = ".gitignore" - merge_strategy = "merge_sections" - section_markers = @{ - global_start = "# ----- Global .gitignore (synced from TimeWarp Architecture) -----" - global_end = "# ----- Repository-specific .gitignore -----" - local_start = "# ----- Repository-specific .gitignore -----" - } - } - ) - } - } - catch { - throw "Failed to parse configuration file: $($_.Exception.Message)" - } -} - -# === Main Script Execution === - -try { - Write-Info "Starting configurable files sync $(if ($DryRun) { '(DRY RUN)' })" - Write-Info "PowerShell Version: $($PSVersionTable.PSVersion)" - Write-Info "Configuration: $ConfigPath" - - # Load configuration - $config = Import-SyncConfiguration -ConfigPath $ConfigPath - Test-Configuration -Config $config - - Write-Info "Source Repository: $($config.source_repo)" - Write-Info "Repository Type: $($config.repository_type)" - Write-Info "Files to sync: $($config.sync_files.Count)" - - # Process each file - foreach ($fileConfig in $config.sync_files) { - try { - Sync-File -FileConfig $fileConfig -Config $config - } - catch { - Write-Error "Failed to sync $($fileConfig.source_path): $($_.Exception.Message)" - # Continue with other files rather than failing completely - } - } - - # Set environment variable for GitHub Actions - if ($script:ChangesMade) { - Write-Success "Sync completed with changes" - if ($env:GITHUB_ACTIONS) { - "SYNC_CHANGES_MADE=true" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding UTF8 - } - - Write-Info "Files synced:" - foreach ($file in $script:SyncedFiles) { - Write-Info " - $file" - } - } else { - Write-Info "Sync completed - no changes needed" - if ($env:GITHUB_ACTIONS) { - "SYNC_CHANGES_MADE=false" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding UTF8 - } - } - -} catch { - Write-Error "Sync failed: $($_.Exception.Message)" - Write-Error $_.ScriptStackTrace - exit 1 +#!/usr/bin/env pwsh + +<# +.SYNOPSIS + Syncs configurable files from the parent TimeWarp Architecture repository. + +.DESCRIPTION + This script downloads and syncs configuration files from the TimeWarpEngineering/timewarp-architecture + repository to maintain consistency with organizational standards. It supports different merge strategies + and preserves local customizations where appropriate. + +.PARAMETER DryRun + If specified, performs a dry run without making any changes. + +.PARAMETER ConfigPath + Path to the sync configuration file. Defaults to .github/scripts/sync-config.yml + +.EXAMPLE + ./sync-configurable-files.ps1 + +.EXAMPLE + ./sync-configurable-files.ps1 -DryRun +#> + +[CmdletBinding()] +param( + [switch]$DryRun, + [string]$ConfigPath = ".github/scripts/sync-config.yml" +) + +# Set error handling +$ErrorActionPreference = 'Stop' +$ProgressPreference = 'SilentlyContinue' + +# Initialize variables +$script:ChangesMade = $false +$script:SyncedFiles = @() + +function Write-Info { + param([string]$Message) + Write-Host "ℹ️ $Message" -ForegroundColor Blue +} + +function Write-Success { + param([string]$Message) + Write-Host "✅ $Message" -ForegroundColor Green +} + +function Write-Warning { + param([string]$Message) + Write-Host "⚠️ $Message" -ForegroundColor Yellow +} + +function Write-Error { + param([string]$Message) + Write-Host "❌ $Message" -ForegroundColor Red +} + +function Get-GitHubContent { + param( + [string]$Repository, + [string]$Path, + [string]$Branch = "master" + ) + + $uri = "https://api.github.com/repos/$Repository/contents/$Path" + $headers = @{ + 'Accept' = 'application/vnd.github.v3.raw' + 'User-Agent' = 'TimeWarp-Sync-Script' + } + + if ($env:GITHUB_TOKEN) { + $headers['Authorization'] = "Bearer $env:GITHUB_TOKEN" + } + + try { + $response = Invoke-RestMethod -Uri $uri -Headers $headers -Method Get + return $response + } + catch { + if ($_.Exception.Response.StatusCode -eq 404) { + return $null + } + throw + } +} + +function Test-FileExists { + param([string]$Path) + return Test-Path -Path $Path -PathType Leaf +} + +function Backup-File { + param([string]$FilePath) + + if (Test-FileExists -Path $FilePath) { + $backupPath = "$FilePath.backup.$(Get-Date -Format 'yyyyMMdd-HHmmss')" + Copy-Item -Path $FilePath -Destination $backupPath -Force + Write-Info "Created backup: $backupPath" + return $backupPath + } + return $null +} + +function Merge-GitIgnoreFile { + param( + [string]$SourceContent, + [string]$DestinationPath, + [hashtable]$SectionMarkers + ) + + $globalStart = $SectionMarkers.global_start + $globalEnd = $SectionMarkers.global_end + $localStart = $SectionMarkers.local_start + + $localContent = "" + $hasLocalSection = $false + + # Read existing local content if file exists + if (Test-FileExists -Path $DestinationPath) { + $existingContent = Get-Content -Path $DestinationPath -Raw + + # Extract local section if it exists + if ($existingContent -match "(?s)$([regex]::Escape($localStart))(.*)$") { + $localContent = $matches[1].Trim() + $hasLocalSection = $true + Write-Info "Preserving existing local .gitignore section" + } + } + + # Build new content + $newContent = @( + $globalStart, + "# This section is automatically synced - do not edit manually", + $SourceContent.Trim(), + "", + $globalEnd, + "# Add repository-specific ignore patterns below this line" + ) + + if ($hasLocalSection -and $localContent) { + $newContent += $localContent + } + + return ($newContent -join "`n") +} + +function Merge-FileWithLocalPreservation { + param( + [string]$SourceContent, + [string]$DestinationPath, + [array]$PreserveSections + ) + + # For now, implement simple replacement + # In a full implementation, this would parse XML/YAML and preserve specific sections + Write-Warning "Local preservation merge not fully implemented - using replace strategy" + return $SourceContent +} + +function Sync-File { + param( + [hashtable]$FileConfig, + [hashtable]$Config + ) + + $sourcePath = $FileConfig.source_path + $destPath = $FileConfig.destination_path + $mergeStrategy = $FileConfig.merge_strategy -or "replace" + $isOptional = $FileConfig.optional -eq $true + $createIfMissing = $FileConfig.create_if_missing -ne $false + + Write-Info "Syncing: $sourcePath -> $destPath" + + # Download source content + try { + $sourceContent = Get-GitHubContent -Repository $Config.source_repo -Path $sourcePath -Branch $Config.source_branch + + if (-not $sourceContent) { + if ($isOptional) { + Write-Info "Optional file not found in source repository: $sourcePath" + return $false + } else { + throw "Required file not found in source repository: $sourcePath" + } + } + } + catch { + if ($isOptional) { + Write-Warning "Could not download optional file: $sourcePath - $($_.Exception.Message)" + return $false + } else { + throw "Failed to download required file: $sourcePath - $($_.Exception.Message)" + } + } + + # Ensure destination directory exists + $destDir = Split-Path -Path $destPath -Parent + if ($destDir -and -not (Test-Path -Path $destDir)) { + if (-not $DryRun) { + New-Item -Path $destDir -ItemType Directory -Force | Out-Null + } + Write-Info "Created directory: $destDir" + } + + # Handle different merge strategies + $finalContent = switch ($mergeStrategy) { + "replace" { + $sourceContent + } + "merge_sections" { + if ($FileConfig.section_markers) { + Merge-GitIgnoreFile -SourceContent $sourceContent -DestinationPath $destPath -SectionMarkers $FileConfig.section_markers + } else { + $sourceContent + } + } + "replace_with_local_preservation" { + if ($FileConfig.preserve_sections) { + Merge-FileWithLocalPreservation -SourceContent $sourceContent -DestinationPath $destPath -PreserveSections $FileConfig.preserve_sections + } else { + $sourceContent + } + } + default { + $sourceContent + } + } + + # Check if content has changed + $hasChanged = $true + if (Test-FileExists -Path $destPath) { + $existingContent = Get-Content -Path $destPath -Raw + $hasChanged = $existingContent -ne $finalContent + } + + if ($hasChanged) { + if (-not $DryRun) { + # Create backup if file exists + Backup-File -FilePath $destPath + + # Write new content + $finalContent | Out-File -FilePath $destPath -Encoding UTF8 -NoNewline + } + + Write-Success "$(if ($DryRun) { '[DRY RUN] ' })Updated: $destPath" + $script:ChangesMade = $true + $script:SyncedFiles += $destPath + return $true + } else { + Write-Info "No changes needed: $destPath" + return $false + } +} + +function Test-Configuration { + param([hashtable]$Config) + + Write-Info "Validating configuration..." + + if (-not $Config.source_repo) { + throw "Configuration missing required field: source_repo" + } + + if (-not $Config.sync_files -or $Config.sync_files.Count -eq 0) { + throw "Configuration missing required field: sync_files" + } + + Write-Success "Configuration validation passed" +} + +function Import-SyncConfiguration { + param([string]$ConfigPath) + + if (-not (Test-Path -Path $ConfigPath)) { + throw "Configuration file not found: $ConfigPath" + } + + try { + # Simple YAML parsing - in production, use a proper YAML parser + $content = Get-Content -Path $ConfigPath -Raw + + # For this implementation, we'll use a simplified approach + # In production, you'd want to use PowerShell-Yaml module + Write-Warning "Using simplified YAML parsing - consider using PowerShell-Yaml module for production" + + # Return a hardcoded configuration based on the created config file + return @{ + source_repo = "TimeWarpEngineering/timewarp-architecture" + source_branch = "master" + repository_type = "dotnet" + sync_files = @( + @{ + source_path = "TimeWarp.Architecture/.editorconfig" + destination_path = ".editorconfig" + merge_strategy = "replace" + create_if_missing = $true + }, + @{ + source_path = "TimeWarp.Architecture/global.json" + destination_path = "global.json" + merge_strategy = "replace" + create_if_missing = $true + }, + @{ + source_path = "TimeWarp.Architecture/.gitignore" + destination_path = ".gitignore" + merge_strategy = "merge_sections" + section_markers = @{ + global_start = "# ----- Global .gitignore (synced from TimeWarp Architecture) -----" + global_end = "# ----- Repository-specific .gitignore -----" + local_start = "# ----- Repository-specific .gitignore -----" + } + } + ) + } + } + catch { + throw "Failed to parse configuration file: $($_.Exception.Message)" + } +} + +# === Main Script Execution === + +try { + Write-Info "Starting configurable files sync $(if ($DryRun) { '(DRY RUN)' })" + Write-Info "PowerShell Version: $($PSVersionTable.PSVersion)" + Write-Info "Configuration: $ConfigPath" + + # Load configuration + $config = Import-SyncConfiguration -ConfigPath $ConfigPath + Test-Configuration -Config $config + + Write-Info "Source Repository: $($config.source_repo)" + Write-Info "Repository Type: $($config.repository_type)" + Write-Info "Files to sync: $($config.sync_files.Count)" + + # Process each file + foreach ($fileConfig in $config.sync_files) { + try { + Sync-File -FileConfig $fileConfig -Config $config + } + catch { + Write-Error "Failed to sync $($fileConfig.source_path): $($_.Exception.Message)" + # Continue with other files rather than failing completely + } + } + + # Set environment variable for GitHub Actions + if ($script:ChangesMade) { + Write-Success "Sync completed with changes" + if ($env:GITHUB_ACTIONS) { + "SYNC_CHANGES_MADE=true" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding UTF8 + } + + Write-Info "Files synced:" + foreach ($file in $script:SyncedFiles) { + Write-Info " - $file" + } + } else { + Write-Info "Sync completed - no changes needed" + if ($env:GITHUB_ACTIONS) { + "SYNC_CHANGES_MADE=false" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding UTF8 + } + } + +} catch { + Write-Error "Sync failed: $($_.Exception.Message)" + Write-Error $_.ScriptStackTrace + exit 1 } \ No newline at end of file diff --git a/workflows-temp/sync-configurable-files.yml b/workflows-temp/sync-configurable-files.yml index 26a8e88..46c8c75 100644 --- a/workflows-temp/sync-configurable-files.yml +++ b/workflows-temp/sync-configurable-files.yml @@ -1,75 +1,75 @@ -name: Sync Configurable Files - -on: - schedule: - # Run weekly on Sundays at 2 AM UTC - - cron: '0 2 * * 0' - workflow_dispatch: - inputs: - dry_run: - description: 'Perform a dry run (no changes will be made)' - required: false - default: 'false' - type: boolean - -permissions: - contents: write - pull-requests: write - -jobs: - sync-files: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - with: - token: ${{ secrets.GITHUB_TOKEN }} - fetch-depth: 0 - - - name: Setup PowerShell - shell: pwsh - run: | - Write-Host "PowerShell version: $($PSVersionTable.PSVersion)" - - - name: Run sync script - shell: pwsh - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - DRY_RUN: ${{ github.event.inputs.dry_run }} - run: | - ./.github/scripts/sync-configurable-files.ps1 - - - name: Create Pull Request - if: success() && env.SYNC_CHANGES_MADE == 'true' - uses: peter-evans/create-pull-request@v6 - with: - token: ${{ secrets.GITHUB_TOKEN }} - commit-message: | - chore: sync configurable files from timewarp-architecture - - Automatically synced configuration files to maintain consistency - with organizational standards. - title: 'chore: sync configurable files from timewarp-architecture' - body: | - ## Summary - This PR automatically syncs configurable files from the parent TimeWarp Architecture repository to maintain consistency with organizational standards. - - ### Files Synced - - Configuration files for .NET development - - Common development tools configuration - - Standard ignore patterns (merged with local additions) - - ### Review Notes - - This is an automated sync from `TimeWarpEngineering/timewarp-architecture` - - Local customizations in synced files may have been preserved or updated - - Please review changes before merging - - ## Test Plan - - [ ] Verify build still works after sync - - [ ] Check that local customizations are preserved - - [ ] Ensure no sensitive information was inadvertently synced - - 🤖 Generated with [Claude Code](https://claude.ai/code) - branch: sync/configurable-files-${{ github.run_number }} - delete-branch: true +name: Sync Configurable Files + +on: + schedule: + # Run weekly on Sundays at 2 AM UTC + - cron: '0 2 * * 0' + workflow_dispatch: + inputs: + dry_run: + description: 'Perform a dry run (no changes will be made)' + required: false + default: 'false' + type: boolean + +permissions: + contents: write + pull-requests: write + +jobs: + sync-files: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + token: ${{ secrets.GITHUB_TOKEN }} + fetch-depth: 0 + + - name: Setup PowerShell + shell: pwsh + run: | + Write-Host "PowerShell version: $($PSVersionTable.PSVersion)" + + - name: Run sync script + shell: pwsh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + DRY_RUN: ${{ github.event.inputs.dry_run }} + run: | + ./.github/scripts/sync-configurable-files.ps1 + + - name: Create Pull Request + if: success() && env.SYNC_CHANGES_MADE == 'true' + uses: peter-evans/create-pull-request@v6 + with: + token: ${{ secrets.GITHUB_TOKEN }} + commit-message: | + chore: sync configurable files from timewarp-architecture + + Automatically synced configuration files to maintain consistency + with organizational standards. + title: 'chore: sync configurable files from timewarp-architecture' + body: | + ## Summary + This PR automatically syncs configurable files from the parent TimeWarp Architecture repository to maintain consistency with organizational standards. + + ### Files Synced + - Configuration files for .NET development + - Common development tools configuration + - Standard ignore patterns (merged with local additions) + + ### Review Notes + - This is an automated sync from `TimeWarpEngineering/timewarp-architecture` + - Local customizations in synced files may have been preserved or updated + - Please review changes before merging + + ## Test Plan + - [ ] Verify build still works after sync + - [ ] Check that local customizations are preserved + - [ ] Ensure no sensitive information was inadvertently synced + + 🤖 Generated with [Claude Code](https://claude.ai/code) + branch: sync/configurable-files-${{ github.run_number }} + delete-branch: true draft: false \ No newline at end of file