diff --git a/.cspell.json b/.cspell.json
new file mode 100644
index 0000000..88532b0
--- /dev/null
+++ b/.cspell.json
@@ -0,0 +1,30 @@
+{
+ "$schema": "https://raw.githubusercontent.com/streetsidesoftware/cspell/main/cspell.schema.json",
+ "caseSensitive": false,
+ "language": "en-GB",
+ "useGitignore": true,
+ "dictionaries": [
+ "project-dictionary"
+ ],
+ "ignorePaths": [
+ "LICENSE",
+ ".gitignore",
+ ".editorconfig",
+ "./.githooks/**",
+ "./.github/dependabot.yml",
+ "./.github/workflows/**",
+ "./.vscode/extensions.json",
+ "./dictionary.dic",
+ "./Directory.*.props",
+ "./Directory.*.targets",
+ "**/*.csproj"
+ ],
+ "dictionaryDefinitions": [
+ {
+ "name": "project-dictionary",
+ "path": "./dictionary.dic",
+ "description": "Words used in this project",
+ "addWords": true
+ }
+ ],
+}
\ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..a0f398b
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,168 @@
+# Editor configuration, see http://editorconfig.org
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+indent_style = space
+indent_size = 2
+insert_final_newline = true
+trim_trailing_whitespace = true
+max_line_length = 120
+spelling_languages = en-GB
+spelling_error_severity = warning
+spelling_exclusion_path = .\dictionary.dic
+
+[*.json]
+insert_final_newline = false
+
+[*.md]
+max_line_length = off
+trim_trailing_whitespace = false
+
+[*.{yml,yaml}]
+max_line_length = off
+
+[*.sln, *.csproj, *.ruleset, *.props, *.targets]
+max_line_length = off
+
+[*.cs]
+indent_size = 4
+
+################# .NET Coding Conventions #################
+# Organize usings
+dotnet_separate_import_directive_groups = false
+dotnet_sort_system_directives_first = true
+file_header_template = unset
+
+# 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
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+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_compound_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_return = true:suggestion
+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:warning
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:suggestion
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+
+################# C# Coding Conventions #################
+
+# var preferences
+csharp_style_var_elsewhere = true:silent
+csharp_style_var_for_built_in_types = true:silent
+csharp_style_var_when_type_is_apparent = true:silent
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:suggestion
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+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_not_pattern = true:suggestion
+csharp_style_prefer_pattern_matching = true:silent
+csharp_style_prefer_switch_expression = true:suggestion
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Modifier preferences
+csharp_prefer_static_local_function = true:warning
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_pattern_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_throw_expression = true:suggestion
+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 = outside_namespace:silent
+
+# namespace preferences
+csharp_style_namespace_declarations = file_scoped
+
+################## 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
diff --git a/.githooks/commit-msg b/.githooks/commit-msg
new file mode 100755
index 0000000..74ee9d4
--- /dev/null
+++ b/.githooks/commit-msg
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+rootDirectory="$(dirname -- "$0")/.."
+source "$rootDirectory/scripts/colours.sh"
+export ROOT_DIR="$rootDirectory"
+
+echo "${CYAN}Running commit-msg hooks${NC}"
+
+# Read the commit message from the file
+commit_message_file=$1
+commit_message=$(cat "$commit_message_file")
+
+"$rootDirectory/scripts/validate-commit-message.sh" "$commit_message"
+
+# Check the exit code of the script
+if [ $? -ne 0 ]; then
+ echo "${RED}Error: Script failed, aborting commit.${NC}"
+ exit 1
+fi
+
+echo ""
+exit 0
diff --git a/.githooks/pre-commit b/.githooks/pre-commit
new file mode 100755
index 0000000..90e02d5
--- /dev/null
+++ b/.githooks/pre-commit
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+rootDirectory="$(dirname -- "$0")/.."
+source "$rootDirectory/scripts/colours.sh"
+export ROOT_DIR="$rootDirectory"
+
+echo "${CYAN}Running pre-commit hooks${NC}"
+
+staged_files=$(git diff --cached --name-only --diff-filter=ACMR)
+
+"$rootDirectory/scripts/validate-branch-name.sh"
+"$rootDirectory/scripts/lint-dotnet.sh" "$staged_files"
+
+# Check the exit code of the script
+if [ $? -ne 0 ]; then
+ echo "${RED}Error: Script failed, aborting commit.${NC}"
+ exit 1
+fi
+
+# Add any modified files to the staging area
+git add $staged_files
+
+echo ""
+exit 0
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..2fe9996
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,46 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+### Describe the bug
+
+A clear and concise description of what the bug is.
+
+### Actual behaviour
+
+A clear and concise description of what is currently happening.
+
+##### Screenshots
+
+If applicable, add screenshots to help explain your problem.
+
+### Expected behaviour
+A clear and concise description of what you expected to happen.
+
+### How to reproduce
+Steps to reproduce the behaviour:
+1. Go to '...'
+2. Click on '....'
+3. Scroll down to '....'
+4. See error
+
+### Environment
+
+##### Desktop (please complete the following information):
+ - OS: [e.g. Mac, Windows]
+ - Browser [e.g. chrome, safari]
+ - Version [e.g. 22]
+
+##### Smartphone (please complete the following information):
+ - Device: [e.g. iPhone6]
+ - OS: [e.g. iOS8.1]
+ - Browser [e.g. stock browser, safari]
+ - Version [e.g. 22]
+
+### Additional context
+Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000..dfcbce4
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+### What problem are you trying to solve?
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+### Describe the solution you'd like
+A clear and concise description of what you want to happen.
+
+### Describe alternatives you've considered
+A clear and concise description of any alternative solutions or features you've considered.
+
+### Additional context
+Add any other context, diagrams or screenshots about the feature request here.
diff --git a/.github/dependabot.yml b/.github/dependabot.yml
new file mode 100644
index 0000000..f96529c
--- /dev/null
+++ b/.github/dependabot.yml
@@ -0,0 +1,35 @@
+---
+version: 2
+updates:
+ - package-ecosystem: "github-actions"
+ directory: "/"
+ schedule:
+ interval: daily
+ day: friday
+ time: "05:00"
+ timezone: "Australia/Perth"
+ commit-message:
+ prefix: "dependencies(github-actions): "
+
+ - package-ecosystem: nuget
+ directory: "/"
+ groups:
+ microsoft:
+ patterns:
+ - Microsoft*
+ update-types: ["minor", "patch"]
+ testing:
+ patterns:
+ - AutoFixture*
+ - coverlet*
+ - FluentAssertions*
+ - GitHubActionsTestLogger
+ - xunit*
+ update-types: ["minor", "patch"]
+ schedule:
+ interval: weekly
+ day: friday
+ time: "05:00"
+ timezone: "Australia/Perth"
+ commit-message:
+ prefix: "dependencies(nuget): "
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..b665c2c
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,13 @@
+### :spiral_notepad: Description
+
+
+
+### :white_check_mark: Checklist
+
+
+
+- [ ] Linked to any related issues in the PR description
+- [ ] Added or updated documentation
+- [ ] Tests for new functionality and regression tests for bug fixes
+- [ ] Breaking changes were made
diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml
new file mode 100644
index 0000000..2e67fb6
--- /dev/null
+++ b/.github/workflows/build-and-test.yml
@@ -0,0 +1,61 @@
+name: build-and-test
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+ - releases/*
+ paths:
+ - "src/**"
+
+permissions:
+ contents: read
+
+jobs:
+ build-and-test:
+ runs-on: ubuntu-latest
+ strategy:
+ fail-fast: false
+
+ permissions:
+ checks: write
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+
+ - name: Install .NET Core
+ uses: actions/setup-dotnet@6bd8b7f7774af54e05809fcc5431931b3eb1ddee # v4.0.1
+ with:
+ dotnet-version: 8.0
+
+ - name: Restore packages
+ run: dotnet restore
+
+ - name: Build
+ run: dotnet build --no-restore --configuration Release
+
+ - name: Run unit tests
+ run: >
+ dotnet test
+ --no-restore
+ --configuration Release
+ --logger "GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true;annotations.titleFormat=@test;annotations.messageFormat=@error\n@trace"
+ /p:CollectCoverage=true
+ --
+ RunConfiguration.CollectSourceInformation=true
+
+ - name: Upload coverage reports to Codecov
+ uses: codecov/codecov-action@e28ff129e5465c2c0dcc6f003fc735cb6ae0c673 # v4.5.0
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ directory: ./reports/coverage/
+ fail_ci_if_error: false
+ flags: unittests
+ slug: saan800/saansoft-correlationId
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..556f3c3
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,88 @@
+# For most projects, this workflow file will not need changing; you simply need
+# to commit it to your repository.
+#
+# You may wish to alter this file to override the set of languages analyzed,
+# or to provide custom queries or build logic.
+#
+# ******** NOTE ********
+# We have attempted to detect the languages in your repository. Please check
+# the `language` matrix defined below to confirm you have the correct set of
+# supported CodeQL languages.
+#
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+ schedule:
+ - cron: '36 3 * * 1'
+
+permissions:
+ contents: read
+
+jobs:
+ analyze:
+ name: Analyze (${{ matrix.language }})
+ # Runner size impacts CodeQL analysis time. To learn more, please see:
+ # - https://gh.io/recommended-hardware-resources-for-running-codeql
+ # - https://gh.io/supported-runners-and-hardware-resources
+ # - https://gh.io/using-larger-runners
+ # Consider using larger runners for possible analysis time improvements.
+ runs-on: ${{ matrix.os }}
+ timeout-minutes: ${{ matrix.timeout-minutes }}
+ permissions:
+ # required for all workflows
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - language: csharp
+ build-mode: autobuild
+ os: ubuntu-latest
+ timeout-minutes: 360
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout repository
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13
+ with:
+ languages: ${{ matrix.language }}
+ build-mode: ${{ matrix.build-mode }}
+ # If you wish to specify custom queries, you can do so here or in a config file.
+ # By default, queries listed here will override any specified in a config file.
+ # Prefix the list here with "+" to use these queries and those in the config file.
+
+ # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs
+ # queries: security-extended,security-and-quality
+
+ # If the analyze step fails for one of the languages you are analyzing with
+ # "We were unable to automatically build your code", modify the matrix above
+ # to set the build mode to "manual" for that language. Then modify this step
+ # to build your code.
+ # ℹ️ Command-line programs to run using the OS shell.
+ # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
+ - if: matrix.build-mode == 'manual'
+ run: |
+ echo 'If you are using a "manual" build mode for one or more of the' \
+ 'languages you are analyzing, replace this with the commands to build' \
+ 'your code, for example:'
+ echo ' make bootstrap'
+ echo ' make release'
+ exit 1
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13
+ with:
+ category: "/language:${{matrix.language}}"
diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml
new file mode 100644
index 0000000..e919c6c
--- /dev/null
+++ b/.github/workflows/dependabot-auto-merge.yml
@@ -0,0 +1,56 @@
+name: dependabot-auto-approve-and-merge
+
+on: pull_request
+
+permissions:
+ contents: read
+ pull-requests: read
+
+jobs:
+ dependabot-auto-approve-and-merge:
+ runs-on: ubuntu-latest
+
+ permissions:
+ contents: write
+ pull-requests: write
+
+ # Checking the author and fork will prevent your Action run failing on non-Dependabot PRs
+ if: |
+ github.event.repository.fork == false &&
+ github.event.pull_request.user.login == 'dependabot[bot]'
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+
+ - name: Dependabot metadata
+ uses: dependabot/fetch-metadata@dbb049abf0d677abbd7f7eee0375145b417fdd34 # v2.2.0
+ id: dependabot-metadata
+
+ - name: Check for merge conflicts
+ uses: sv-tools/block-merge-conflicts@2fb5852d0efb87ecceed7a3d24ad657dccf47a64 # v1.1.0
+ with:
+ token: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Approve a PR if not already approved
+ if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}
+ run: |
+ gh pr checkout "$PR_URL" # sets the upstream metadata for `gh pr status`
+ if [ "$(gh pr status --json reviewDecision -q .currentBranch.reviewDecision)" != "APPROVED" ];
+ then gh pr review --approve "$PR_URL" && gh pr merge --auto --squash "${PR_URL}"
+ else echo "PR already approved, skipping additional approvals to minimize emails/notification noise.";
+ fi
+ env:
+ PR_URL: ${{github.event.pull_request.html_url}}
+ GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
+
+ - name: Auto-merge
+ if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }}
+ run: gh pr merge --auto --squash "$PR_URL"
+ env:
+ PR_URL: ${{github.event.pull_request.html_url}}
+ GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
diff --git a/.github/workflows/dependabot-validate.yml b/.github/workflows/dependabot-validate.yml
new file mode 100644
index 0000000..b97898d
--- /dev/null
+++ b/.github/workflows/dependabot-validate.yml
@@ -0,0 +1,27 @@
+name: dependabot-validate
+
+on:
+ pull_request:
+ paths:
+ - ".github/dependabot.yml"
+ - ".github/workflows/dependabot-validate.yml"
+
+permissions:
+ contents: read
+
+jobs:
+ dependabot-validate:
+ runs-on: ubuntu-latest
+ steps:
+
+ - name: Harden Runner
+ uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+
+ - name: validate dependabot
+ uses: marocchino/validate-dependabot@d8ae5c0d03dd75fbd0ad5f8ab4ba8101ebbd4b37 # v3.0.0
+
diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml
new file mode 100644
index 0000000..6fe9acc
--- /dev/null
+++ b/.github/workflows/dependency-review.yml
@@ -0,0 +1,21 @@
+name: dependency-review
+
+on: pull_request
+
+permissions:
+ contents: read
+
+jobs:
+ dependency-review:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+
+ - name: Run dependency review
+ uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
new file mode 100644
index 0000000..c2db7ab
--- /dev/null
+++ b/.github/workflows/lint.yml
@@ -0,0 +1,81 @@
+name: lint
+
+on: pull_request
+
+permissions:
+ contents: read
+
+jobs:
+ super-linter:
+ runs-on: ubuntu-latest
+
+ permissions:
+ packages: read
+ # To report GitHub Actions status checks
+ statuses: write
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ with:
+ # super-linter needs the full git history to get the
+ # list of files that changed across commits
+ fetch-depth: 0
+
+ - name: Get changed c# files
+ id: changed-csharp-files
+ uses: tj-actions/changed-files@6b2903bdce6310cfbddd87c418f253cf29b2dec9 # v44.5.6
+ with:
+ # Avoid using single or double quotes for multiline patterns
+ files: |
+ **.cs
+
+ - name: Setup linting config
+ id: config
+ env:
+ hasChangedCSharpFiles: ${{ steps.changed-csharp-files.outputs.any_changed == 'true' }}
+ run: |
+ if [ "${hasChangedCSharpFiles}" == "true" ]; then
+ echo "VALIDATE_ALL_CODEBASE=false" >> "$GITHUB_OUTPUT"
+ else
+ echo "VALIDATE_ALL_CODEBASE=true" >> "$GITHUB_OUTPUT"
+ fi
+
+ - name: Run super-linter
+ uses: super-linter/super-linter@3fe03abab2eafb293ace16d4a3b07aeabcb3f1a0 # v6.7.0
+ env:
+ # To report GitHub Actions status checks
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ FILTER_REGEX_EXCLUDE: (./\.editorconfig|\.idea/*|\.vscode/*)
+ IGNORE_GENERATED_FILES: true
+ # false -> only checks changed files
+ VALIDATE_ALL_CODEBASE: ${{ steps.config.outputs.VALIDATE_ALL_CODEBASE == 'true'}}
+ VALIDATE_CSHARP: true
+ VALIDATE_GITHUB_ACTIONS: true
+ VALIDATE_YAML: true
+
+ spellcheck:
+ permissions:
+ contents: read # for streetsidesoftware/cspell-action to fetch files for commit
+ pull-requests: read # for streetsidesoftware/cspell-action to fetch commits for PR
+ runs-on: ubuntu-latest
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0
+ with:
+ egress-policy: audit
+
+ - name: Checkout code
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+
+ - name: Spellcheck
+ uses: streetsidesoftware/cspell-action@245201e3f58019204d99920deeb78aade6724230 # v6.6.0
+ with:
+ config: .cspell.json
+ check_dot_files: false
+ incremental_files_only: true
diff --git a/.github/workflows/ossf-scorecard.yml b/.github/workflows/ossf-scorecard.yml
new file mode 100644
index 0000000..8a625dc
--- /dev/null
+++ b/.github/workflows/ossf-scorecard.yml
@@ -0,0 +1,78 @@
+# This workflow uses actions that are not certified by GitHub. They are provided
+# by a third-party and are governed by separate terms of service, privacy
+# policy, and support documentation.
+
+name: Scorecard supply-chain security
+on:
+ # For Branch-Protection check. Only the default branch is supported. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
+ branch_protection_rule:
+ # To guarantee Maintained check is occasionally updated. See
+ # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
+ schedule:
+ - cron: '39 19 * * 6'
+ push:
+ branches: [ "main" ]
+
+# Declare default permissions as read only.
+permissions: read-all
+
+jobs:
+ analysis:
+ name: Scorecard analysis
+ runs-on: ubuntu-latest
+ permissions:
+ # Needed to upload the results to code-scanning dashboard.
+ security-events: write
+ # Needed to publish results and get a badge (see publish_results below).
+ id-token: write
+ # Uncomment the permissions below if installing in a private repository.
+ # contents: read
+ # actions: read
+
+ steps:
+ - name: Harden Runner
+ uses: step-security/harden-runner@0d381219ddf674d61a7572ddd19d7941e271515c # v2.9.0
+ with:
+ egress-policy: audit
+
+ - name: "Checkout code"
+ uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
+ with:
+ persist-credentials: false
+
+ - name: "Run analysis"
+ uses: ossf/scorecard-action@dc50aa9510b46c811795eb24b2f1ba02a914e534 # v2.3.3
+ with:
+ results_file: results.sarif
+ results_format: sarif
+ # (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
+ # - you want to enable the Branch-Protection check on a *public* repository, or
+ # - you are installing Scorecard on a *private* repository
+ # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional.
+ # repo_token: ${{ secrets.SCORECARD_TOKEN }}
+
+ # Public repositories:
+ # - Publish results to OpenSSF REST API for easy access by consumers
+ # - Allows the repository to include the Scorecard badge.
+ # - See https://github.com/ossf/scorecard-action#publishing-results.
+ # For private repositories:
+ # - `publish_results` will always be set to `false`, regardless
+ # of the value entered here.
+ publish_results: true
+
+ # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
+ # format to the repository Actions tab.
+ - name: "Upload artifact"
+ uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
+ with:
+ name: SARIF file
+ path: results.sarif
+ retention-days: 5
+
+ # Upload the results to GitHub's code scanning dashboard (optional).
+ # Commenting out will disable upload of results to your repo's Code Scanning dashboard
+ - name: "Upload to code-scanning"
+ uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # v3.25.13
+ with:
+ sarif_file: results.sarif
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0821451
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,56 @@
+# ********************************************** #
+# ******************** IDEs ******************** #
+# ********************************************** #
+
+############### Visual Studio Code ###############
+.vscode/*
+!.vscode/settings.json
+!.vscode/tasks.json
+!.vscode/launch.json
+!.vscode/extensions.json
+
+################## Visual Studio #################
+# For more VS gitignore rules: https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
+
+.vs/
+
+*.DotSettings
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+################# JetBrains Rider ################
+# For more Rider gitignore rules: https://github.com/JetBrains/resharper-rider-samples/blob/master/.gitignore
+#.idea/
+
+################## Visual Studio #################
+.mono/
+
+
+# ********************************************** #
+# ******************** CODE ******************** #
+# ********************************************** #
+
+# compiled / generated output
+dist
+tmp
+/out-tsc
+
+# dependencies
+node_modules
+packages
+
+# .NET
+bin
+obj
+
+
+# LOGS, TESTS AND COVERAGE
+
+/coverage
+/reports
+npm-debug.log
+yarn-error.log
+testem.log
+bundle-stats.html
diff --git a/.idea/.idea.SaanSoft.CorrelationId/.idea/.gitignore b/.idea/.idea.SaanSoft.CorrelationId/.idea/.gitignore
new file mode 100644
index 0000000..731c243
--- /dev/null
+++ b/.idea/.idea.SaanSoft.CorrelationId/.idea/.gitignore
@@ -0,0 +1,14 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Rider ignored files
+/projectSettingsUpdater.xml
+/modules.xml
+/.idea.SaanSoft.CorrelationId.iml
+/contentModel.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+/dictionaries/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/.idea.SaanSoft.CorrelationId/.idea/.name b/.idea/.idea.SaanSoft.CorrelationId/.idea/.name
new file mode 100644
index 0000000..551e836
--- /dev/null
+++ b/.idea/.idea.SaanSoft.CorrelationId/.idea/.name
@@ -0,0 +1 @@
+SaanSoft.CorrelationId
\ No newline at end of file
diff --git a/.idea/.idea.SaanSoft.CorrelationId/.idea/indexLayout.xml b/.idea/.idea.SaanSoft.CorrelationId/.idea/indexLayout.xml
new file mode 100644
index 0000000..7b08163
--- /dev/null
+++ b/.idea/.idea.SaanSoft.CorrelationId/.idea/indexLayout.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/.idea.SaanSoft.CorrelationId/.idea/vcs.xml b/.idea/.idea.SaanSoft.CorrelationId/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/.idea.SaanSoft.CorrelationId/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..cdb4074
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,7 @@
+{
+ "recommendations": [
+ "EditorConfig.EditorConfig",
+ "ms-dotnettools.csdevkit",
+ "streetsidesoftware.code-spell-checker",
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..6af2d4c
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,4 @@
+{
+ "editor.formatOnPaste": true,
+ "editor.formatOnSave": true,
+}
\ No newline at end of file
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..1636f5e
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,133 @@
+
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+We as members, contributors, and leaders pledge to make participation in our
+community a harassment-free experience for everyone, regardless of age, body
+size, visible or invisible disability, ethnicity, sex characteristics, gender
+identity and expression, level of experience, education, socio-economic status,
+nationality, personal appearance, race, caste, color, religion, or sexual
+identity and orientation.
+
+We pledge to act and interact in ways that contribute to an open, welcoming,
+diverse, inclusive, and healthy community.
+
+## Our Standards
+
+Examples of behaviour that contributes to a positive environment for our
+community include:
+
+* Demonstrating empathy and kindness toward other people
+* Being respectful of differing opinions, viewpoints, and experiences
+* Giving and gracefully accepting constructive feedback
+* Accepting responsibility and apologizing to those affected by our mistakes,
+ and learning from the experience
+* Focusing on what is best not just for us as individuals, but for the overall
+ community
+
+Examples of unacceptable behaviour include:
+
+* The use of sexualized language or imagery, and sexual attention or advances of
+ any kind
+* Trolling, insulting or derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or email address,
+ without their explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Enforcement Responsibilities
+
+Community leaders are responsible for clarifying and enforcing our standards of
+acceptable behaviour and will take appropriate and fair corrective action in
+response to any behaviour that they deem inappropriate, threatening, offensive,
+or harmful.
+
+Community leaders have the right and responsibility to remove, edit, or reject
+comments, commits, code, wiki edits, issues, and other contributions that are
+not aligned to this Code of Conduct, and will communicate reasons for moderation
+decisions when appropriate.
+
+## Scope
+
+This Code of Conduct applies within all community spaces, and also applies when
+an individual is officially representing the community in public spaces.
+Examples of representing our community include using an official email address,
+posting via an official social media account, or acting as an appointed
+representative at an online or offline event.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behaviour may be
+reported to the community leaders responsible for enforcement via raising a
+[GitHub issue](https://github.com/saan800/saansoft-correlationid/issues/new/choose).
+All complaints will be reviewed and investigated promptly and fairly.
+
+All community leaders are obligated to respect the privacy and security of the
+reporter of any incident.
+
+## Enforcement Guidelines
+
+Community leaders will follow these Community Impact Guidelines in determining
+the consequences for any action they deem in violation of this Code of Conduct:
+
+### 1. Correction
+
+**Community Impact**: Use of inappropriate language or other behaviour deemed
+unprofessional or unwelcome in the community.
+
+**Consequence**: A private, written warning from community leaders, providing
+clarity around the nature of the violation and an explanation of why the
+behaviour was inappropriate. A public apology may be requested.
+
+### 2. Warning
+
+**Community Impact**: A violation through a single incident or series of
+actions.
+
+**Consequence**: A warning with consequences for continued behaviour. No
+interaction with the people involved, including unsolicited interaction with
+those enforcing the Code of Conduct, for a specified period of time. This
+includes avoiding interactions in community spaces as well as external channels
+like social media. Violating these terms may lead to a temporary or permanent
+ban.
+
+### 3. Temporary Ban
+
+**Community Impact**: A serious violation of community standards, including
+sustained inappropriate behaviour.
+
+**Consequence**: A temporary ban from any sort of interaction or public
+communication with the community for a specified period of time. No public or
+private interaction with the people involved, including unsolicited interaction
+with those enforcing the Code of Conduct, is allowed during this period.
+Violating these terms may lead to a permanent ban.
+
+### 4. Permanent Ban
+
+**Community Impact**: Demonstrating a pattern of violation of community
+standards, including sustained inappropriate behaviour, harassment of an
+individual, or aggression toward or disparagement of classes of individuals.
+
+**Consequence**: A permanent ban from any sort of public interaction within the
+community.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage],
+version 2.1, available at
+[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].
+
+Community Impact Guidelines were inspired by
+[Mozilla's code of conduct enforcement ladder][Mozilla CoC].
+
+For answers to common questions about this code of conduct, see the FAQ at
+[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
+[https://www.contributor-covenant.org/translations][translations].
+
+[homepage]: https://www.contributor-covenant.org
+[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
+[Mozilla CoC]: https://github.com/mozilla/diversity
+[FAQ]: https://www.contributor-covenant.org/faq
+[translations]: https://www.contributor-covenant.org/translations
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..fea0994
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,67 @@
+# Contributing to SaanSoft.CorrelationId
+
+## Recommended Git Setup
+
+### Git Hooks
+
+All PRs will be verified against linting scripts to ensure styling consistency.
+For a faster feedback cycle, we recommend configuring git hooks.
+
+Run the following to enable git hooks for this repository.
+
+```shell
+git config core.hooksPath .githooks
+```
+
+### Signed commits
+
+So we can attribute changes to the people involved we like to have signed commits
+
+```shell
+git config user.name "John Doe"
+git config user.email johndoe@example.com
+
+# Or to configure globally for all repositories
+git config --global user.name "John Doe"
+git config --global user.email johndoe@example.com
+```
+
+Then use the `-s` or `--signoff` flag when committing.
+
+
+## Spelling
+
+SaanSoft.CorrelationId uses the `en-GB` dictionary for spell checking.
+
+The `dictionary.dic` file at the root level of the repository contains project specific words.
+
+### VS Code
+
+Add the [Code Spell Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) plugin and enable it.
+
+The `.cspell.json` file in the root directory is configured to use and add any unknown words to the dictionary.
+
+The `en-GB` dictionary comes packaged with the plugin, so no further setup is required.
+
+### JetBrains Rider
+
+#### Configure project to use `dictionary.dic
+
+Open the project then go to:
+
+* `Settings` > `Editor` > `Spelling`
+* Check `Use single dictionary for saving words` and select `application-level`
+* Under `Custom dictionaries (plain text word lists, hunspell)`
+ * Click the `+` button
+ * In the root directory of the repository, select `dictionary.dic`
+ * Save the changes
+
+
+### Visual Studio
+
+Read more [here](https://learn.microsoft.com/en-us/visualstudio/ide/text-spell-checker)
+
+## Coding guidelines
+
+A consistent coding style is included via [EditorConfig](https://editorconfig.org/) with the file [.editorconfig](./.editorconfig) at the root of the repo. Depending on your editor of choice, it will either support it out of the box or you can [download a plugin](https://editorconfig.org/#download) for the config to be applied.
+
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..ca9f9d3
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,20 @@
+
+
+ enable
+ default
+ true
+ enable
+
+
+ true
+ true
+ CS1591
+
+
+ true
+ true
+
+
+
+
+
diff --git a/Directory.Build.targets b/Directory.Build.targets
new file mode 100644
index 0000000..d8a3bf9
--- /dev/null
+++ b/Directory.Build.targets
@@ -0,0 +1,9 @@
+
+
+ true
+ $([System.IO.Path]::Combine($(MSBuildProjectDirectory), "../../", 'reports', 'coverage'))/
+ opencover,json
+ $(CoverletOutput)/coverage.json
+ [SaanSoft.Tests.*]*,[xunit.*]*
+
+
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 0000000..5f689d3
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/README.md b/README.md
index 2311a51..5ff7e57 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,9 @@
-# saansoft-correlationid
-Generate correlation Ids
+# SaanSoft.CorrelationId
+
+In a distributed system it can be a challenge to trace HTTP requests and messages through multiple microservices.
+
+A `CorrelationId` bundles each logical transaction as it moves through multiple processors.
+
+With this system, your client's requests are collected under one value for easier tracking and troubleshooting.
+
+Depending on the `ICorrelationIdProvider` configured you can extract the value from HTTP request headers or OpenTelemetry tracing for greater ease of tracking the transaction, or simply default to a generated GUID string.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..a6be393
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,5 @@
+# Reporting Security Issues
+
+To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/saan800/saansoft-correlationid/security/advisories/new) tab.
+
+More information can be found in the [GitHub Documentation](https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability)
diff --git a/SaanSoft.CorrelationId.sln b/SaanSoft.CorrelationId.sln
new file mode 100644
index 0000000..2e84f7e
--- /dev/null
+++ b/SaanSoft.CorrelationId.sln
@@ -0,0 +1,36 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_solution", "_solution", "{63401EBF-A03B-47CD-B5C2-99E334B312E1}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaanSoft.CorrelationId", "src\SaanSoft.CorrelationId\SaanSoft.CorrelationId.csproj", "{82101F7B-641B-4F7B-9446-57DDFA66A694}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{A1F9D39E-7FAB-4D65-8DE7-C29511D4C024}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaanSoft.Tests.CorrelationId", "test\SaanSoft.Tests.CorrelationId\SaanSoft.Tests.CorrelationId.csproj", "{31889625-3724-44C6-B426-FF0C031C2FC7}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SaanSoft.Tests.CorrelationId.Common", "test\SaanSoft.Tests.CorrelationId.Common\SaanSoft.Tests.CorrelationId.Common.csproj", "{ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {82101F7B-641B-4F7B-9446-57DDFA66A694}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {82101F7B-641B-4F7B-9446-57DDFA66A694}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {82101F7B-641B-4F7B-9446-57DDFA66A694}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {82101F7B-641B-4F7B-9446-57DDFA66A694}.Release|Any CPU.Build.0 = Release|Any CPU
+ {31889625-3724-44C6-B426-FF0C031C2FC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {31889625-3724-44C6-B426-FF0C031C2FC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {31889625-3724-44C6-B426-FF0C031C2FC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {31889625-3724-44C6-B426-FF0C031C2FC7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {31889625-3724-44C6-B426-FF0C031C2FC7} = {A1F9D39E-7FAB-4D65-8DE7-C29511D4C024}
+ {ACBF3961-E6E1-4EC2-948E-04C47D2A8A1B} = {A1F9D39E-7FAB-4D65-8DE7-C29511D4C024}
+ EndGlobalSection
+EndGlobal
diff --git a/dictionary.dic b/dictionary.dic
new file mode 100644
index 0000000..dd4df45
--- /dev/null
+++ b/dictionary.dic
@@ -0,0 +1,12 @@
+abbrev
+bson
+codecov
+color
+nojournal
+opencover
+Saan
+SaanSoft
+Specflow
+signoff
+unittests
+Xunit
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 0000000..38ac8e7
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/scripts/colours.sh b/scripts/colours.sh
new file mode 100755
index 0000000..92aeb6b
--- /dev/null
+++ b/scripts/colours.sh
@@ -0,0 +1,12 @@
+# More colours in top answer on
+# - https://stackoverflow.com/questions/5947742/how-to-change-the-output-color-of-echo-in-linux
+# Add colours to echo console statements.
+# Ensure you reset console colours after using them with ${NC}
+# > echo ${BLUE}Something is happening...${NC}
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+YELLOW='\033[1;33m'
+BLUE='\033[0;34m'
+PURPLE='\033[0;35m'
+CYAN='\033[0;36m'
+NC='\033[0m' # No Colour
diff --git a/scripts/lint-dotnet.sh b/scripts/lint-dotnet.sh
new file mode 100755
index 0000000..4bdd381
--- /dev/null
+++ b/scripts/lint-dotnet.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+source "$ROOT_DIR/scripts/colours.sh"
+echo "${YELLOW}Running lint-dotnet...${NC}"
+
+# Check if the correct number of arguments are provided
+if [ $# -lt 1 ]; then
+ echo "${RED}Usage: $0 [options]${NC}"
+ echo " - options: see ${PURPLE}dotnet format --help${NC} for details"
+ echo " eg ${PURPLE}--verify-no-changes${NC} for verification in CI pipeline"
+ exit 1
+fi
+
+# Assign arguments to variables
+files="$1"
+staged_cs_files=$(echo "$files" | grep -E ".cs$")
+# Get additional arguments if provided
+if [ $# -gt 1 ]; then
+ shift # Shift the arguments to remove the first one
+ additional_args="$@"
+ echo "Additional arguments: $additional_args"
+fi
+
+if [ -n "$staged_cs_files" ]; then
+ dotnet format --verbosity normal --no-restore --include $staged_cs_files $additional_args
+fi
diff --git a/scripts/validate-branch-name.sh b/scripts/validate-branch-name.sh
new file mode 100755
index 0000000..f8985c0
--- /dev/null
+++ b/scripts/validate-branch-name.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+# A simple regex linter for branch naming strategy
+# based off https://www.devwithimagination.com/2020/04/13/git-commit-hooks-for-branch-naming-using-husky/
+
+source "$ROOT_DIR/scripts/colours.sh"
+echo "${YELLOW}Running validate-branch-name...${NC}"
+
+local_branch_name="$(git rev-parse --abbrev-ref HEAD)"
+
+validTypes="bugfix|chore|docs|feature|hotfix"
+valid_branch_regex="^(($validTypes)\/[a-zA-Z0-9_\-]{4,})$"
+
+if [[ ! $local_branch_name =~ $valid_branch_regex ]]; then
+ commaSeparatedList=$(echo "$validTypes" | tr '|' ', ')
+
+ echo "${RED}Invalid branch name: $local_branch_name${NC}"
+ echo " Branch names must be in the format:"
+ echo " /"
+ echo " /-"
+ echo " Where:"
+ echo " - : ${commaSeparatedList}"
+ echo " - : (optional) usually used for a story or issue number"
+ echo " - : at least 4 characters long"
+ echo " Examples:"
+ echo " - feature/ABC-123_subject"
+ echo " - hotfix/something_is_wrong"
+ exit 1
+fi
+
+exit 0
diff --git a/scripts/validate-commit-message.sh b/scripts/validate-commit-message.sh
new file mode 100755
index 0000000..0943997
--- /dev/null
+++ b/scripts/validate-commit-message.sh
@@ -0,0 +1,49 @@
+#!/bin/sh
+
+# A simple regex commit linter example
+# https://www.conventionalcommits.org/en/v1.0.0/
+# https://github.com/angular/angular/blob/22b96b9/CONTRIBUTING.md#type
+
+source "$ROOT_DIR/scripts/colours.sh"
+echo "${YELLOW}Running validate-commit-message...${NC}"
+
+# Check if the correct number of arguments are provided
+if [ $# -ne 1 ]; then
+ echo "${RED}Usage: $0 ${NC}"
+ exit 1
+fi
+
+# Read the commit message from the arguments
+commit_message="$1"
+
+
+validTypes="breaking change|bugfix|build|ci|chore|docs|feat|feature|fix|hotfix|perf|refactor|revert|style|test"
+system_msg_regex="^(Merge remote-tracking branch|Revert) .*"
+valid_msg_regex="^($validTypes)(\(.{3,}\))?!?: .{4,}"
+
+if [[ ! $commit_message =~ $valid_msg_regex ]] && [[ ! $commit_message =~ $system_msg_regex ]]; then
+ commaSeparatedList=$(echo "$validTypes" | tr '|' ', ')
+
+ echo "${RED}Invalid commit message: $commit_message${NC}"
+ echo " Commit messages must be in the Conventional Commits format:"
+ echo " : "
+ echo " (): "
+ echo " Where:"
+ echo " - : $commaSeparatedList"
+ echo " - : (optional) usually used for story or issue number"
+ echo " - : at least 4 characters long"
+ echo " Examples:"
+ echo " - feature(ABC-123): subject"
+ echo " - fix: subject"
+ echo " More info: https://www.conventionalcommits.org/en/v1.0.0/"
+ echo ""
+ echo " ${GREEN}HELP:${NC}"
+ echo " - If you are using zhs command line, and the '!' character for breaking changes. You may get the error:"
+ echo " > zsh: illegal modifier:"
+ echo " In your commit message use single quotes around the commit message."
+
+
+ exit 1
+fi
+
+exit 0
diff --git a/src/SaanSoft.CorrelationId/ICorrelationIdProvider.cs b/src/SaanSoft.CorrelationId/ICorrelationIdProvider.cs
new file mode 100644
index 0000000..857d9a8
--- /dev/null
+++ b/src/SaanSoft.CorrelationId/ICorrelationIdProvider.cs
@@ -0,0 +1,16 @@
+namespace SaanSoft.CorrelationId;
+
+public interface ICorrelationIdProvider
+{
+ ///
+ /// Set the correlationId to a specific value
+ ///
+ ///
+ void Set(string correlationId);
+
+ ///
+ /// Get the correlationId
+ ///
+ ///
+ string? Get();
+}
diff --git a/src/SaanSoft.CorrelationId/SaanSoft.CorrelationId.csproj b/src/SaanSoft.CorrelationId/SaanSoft.CorrelationId.csproj
new file mode 100644
index 0000000..c513b8a
--- /dev/null
+++ b/src/SaanSoft.CorrelationId/SaanSoft.CorrelationId.csproj
@@ -0,0 +1,8 @@
+
+
+
+ netstandard2.1;net7.0;net8.0
+ true
+
+
+
diff --git a/src/SaanSoft.CorrelationId/SimpleCorrelationIdProvider.cs b/src/SaanSoft.CorrelationId/SimpleCorrelationIdProvider.cs
new file mode 100644
index 0000000..bd59d2b
--- /dev/null
+++ b/src/SaanSoft.CorrelationId/SimpleCorrelationIdProvider.cs
@@ -0,0 +1,20 @@
+namespace SaanSoft.CorrelationId;
+
+///
+/// If no correlationId is set, it will default to `Guid.NewGuid().ToString()` to ensure a value is always provided.
+///
+public class SimpleCorrelationIdProvider : ICorrelationIdProvider
+{
+ private string? _correlationId;
+
+ public void Set(string correlationId)
+ {
+ if (!string.IsNullOrWhiteSpace(correlationId)) _correlationId = correlationId.Trim();
+ }
+
+ public string? Get()
+ {
+ if (string.IsNullOrWhiteSpace(_correlationId)) _correlationId = Guid.NewGuid().ToString();
+ return _correlationId;
+ }
+}
diff --git a/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFakeDataAttribute.cs b/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFakeDataAttribute.cs
new file mode 100644
index 0000000..620ad4d
--- /dev/null
+++ b/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFakeDataAttribute.cs
@@ -0,0 +1,6 @@
+using AutoFixture.Xunit2;
+
+namespace SaanSoft.Tests.CorrelationId.Common.AutoFixture;
+
+[AttributeUsage(AttributeTargets.Method)]
+public class AutoFakeDataAttribute() : AutoDataAttribute(AutoFixtureExtensions.Create);
diff --git a/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFixtureExtensions.cs b/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFixtureExtensions.cs
new file mode 100644
index 0000000..0fd80d1
--- /dev/null
+++ b/test/SaanSoft.Tests.CorrelationId.Common/AutoFixture/AutoFixtureExtensions.cs
@@ -0,0 +1,36 @@
+using AutoFixture;
+using AutoFixture.AutoFakeItEasy;
+
+namespace SaanSoft.Tests.CorrelationId.Common.AutoFixture;
+
+public static class AutoFixtureExtensions
+{
+ public static Fixture Create()
+ {
+ var fixture = new Fixture();
+
+ fixture.Customize(new AutoFakeItEasyCustomization
+ {
+ ConfigureMembers = true,
+ GenerateDelegates = true
+ }
+ );
+
+ fixture.Register(A.Fake);
+ fixture.Freeze();
+
+ fixture.Register(() =>
+ {
+ var httpMessageHandler = fixture.Freeze();
+ return new HttpClient(httpMessageHandler)
+ {
+ // inject a default baseUri for httpClient, this is required for dependencies that expect
+ // HttpClientFactory to configure their defaults correctly, in that case even if you mock out the
+ // message handler things will fail when generating the target uri
+ BaseAddress = new Uri("http://localhost")
+ };
+ });
+
+ return fixture;
+ }
+}
diff --git a/test/SaanSoft.Tests.CorrelationId.Common/FakeTest.cs b/test/SaanSoft.Tests.CorrelationId.Common/FakeTest.cs
new file mode 100644
index 0000000..93f8377
--- /dev/null
+++ b/test/SaanSoft.Tests.CorrelationId.Common/FakeTest.cs
@@ -0,0 +1,13 @@
+namespace SaanSoft.Tests.CorrelationId.Common;
+
+///
+/// Here just to make this project have a test file, and ignore in coverage
+///
+public class FakeTest
+{
+ [Fact]
+ public void Run()
+ {
+ Assert.True(true);
+ }
+}
diff --git a/test/SaanSoft.Tests.CorrelationId.Common/GlobalUsings.cs b/test/SaanSoft.Tests.CorrelationId.Common/GlobalUsings.cs
new file mode 100644
index 0000000..257b210
--- /dev/null
+++ b/test/SaanSoft.Tests.CorrelationId.Common/GlobalUsings.cs
@@ -0,0 +1 @@
+global using FakeItEasy;
diff --git a/test/SaanSoft.Tests.CorrelationId.Common/SaanSoft.Tests.CorrelationId.Common.csproj b/test/SaanSoft.Tests.CorrelationId.Common/SaanSoft.Tests.CorrelationId.Common.csproj
new file mode 100644
index 0000000..c55b5c3
--- /dev/null
+++ b/test/SaanSoft.Tests.CorrelationId.Common/SaanSoft.Tests.CorrelationId.Common.csproj
@@ -0,0 +1,19 @@
+
+
+
+ net7.0;net8.0
+ false
+ true
+ CS1591;CS8602;CS8625;CS8618
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
diff --git a/test/SaanSoft.Tests.CorrelationId/GlobalUsings.cs b/test/SaanSoft.Tests.CorrelationId/GlobalUsings.cs
new file mode 100644
index 0000000..9f9f398
--- /dev/null
+++ b/test/SaanSoft.Tests.CorrelationId/GlobalUsings.cs
@@ -0,0 +1,4 @@
+global using FakeItEasy;
+global using FluentAssertions;
+global using SaanSoft.CorrelationId;
+
diff --git a/test/SaanSoft.Tests.CorrelationId/SaanSoft.Tests.CorrelationId.csproj b/test/SaanSoft.Tests.CorrelationId/SaanSoft.Tests.CorrelationId.csproj
new file mode 100644
index 0000000..9261795
--- /dev/null
+++ b/test/SaanSoft.Tests.CorrelationId/SaanSoft.Tests.CorrelationId.csproj
@@ -0,0 +1,23 @@
+
+
+ net7.0;net8.0
+ false
+ true
+ CS1591;CS8602;CS8625;CS8618
+
+
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+
+
+
diff --git a/test/SaanSoft.Tests.CorrelationId/SimpleCorrelationIdProviderTests.cs b/test/SaanSoft.Tests.CorrelationId/SimpleCorrelationIdProviderTests.cs
new file mode 100644
index 0000000..51df717
--- /dev/null
+++ b/test/SaanSoft.Tests.CorrelationId/SimpleCorrelationIdProviderTests.cs
@@ -0,0 +1,27 @@
+using AutoFixture.Xunit2;
+
+namespace SaanSoft.Tests.CorrelationId;
+
+public class SimpleCorrelationIdProviderTests
+{
+ private readonly SimpleCorrelationIdProvider _provider = new();
+
+ [Fact]
+ public void Get_returns_non_empty_string()
+ {
+ var result = _provider.Get();
+
+ result.Should().NotBeNullOrWhiteSpace();
+ result.Length.Should().Be(36); // length of a guid
+ }
+
+ [Theory]
+ [InlineAutoData]
+ public void Set_and_Get_returns_given_value(string val)
+ {
+ _provider.Set(val);
+
+ var result = _provider.Get();
+ result.Should().Be(val);
+ }
+}