mirror of
https://github.com/meysamhadeli/booking-microservices.git
synced 2026-04-16 07:26:25 +08:00
Compare commits
165 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d99d1a9a9a | ||
|
|
6e1fe61a65 | ||
|
|
a882b3cfe2 | ||
|
|
14cc9e7c96 | ||
|
|
0f66c14299 | ||
|
|
7f9cf8b922 | ||
|
|
23d4babd52 | ||
|
|
8d4819624e | ||
|
|
043c20002c | ||
|
|
94a22dfc23 | ||
|
|
3ce312891a | ||
|
|
a62177a6c4 | ||
|
|
b67d000580 | ||
|
|
2a5909bdbd | ||
|
|
786fbb121f | ||
|
|
b9b3c26edc | ||
|
|
9dcd6625b2 | ||
|
|
43666a7dd3 | ||
|
|
23b14eabe7 | ||
|
|
9164c770b5 | ||
|
|
3bdcc6341f | ||
|
|
20e49770b2 | ||
|
|
e259b64476 | ||
|
|
7476502e50 | ||
|
|
64dfee4224 | ||
|
|
bd94742d18 | ||
|
|
d5e9f75dfc | ||
|
|
eef8aead7e | ||
|
|
38c339c1aa | ||
|
|
20bd2ac0bc | ||
|
|
3b86cf7917 | ||
|
|
d1dbe6209c | ||
|
|
475fa90e1c | ||
|
|
0dd7941136 | ||
|
|
29a1e47a2d | ||
|
|
68f768d687 | ||
|
|
e2fbd27222 | ||
|
|
d6e313a560 | ||
|
|
57000c5802 | ||
|
|
b3ce2889b2 | ||
|
|
a3c6f670e1 | ||
|
|
3bd8cb1db3 | ||
|
|
b9e1a1b949 | ||
|
|
e76353c2df | ||
|
|
44e408258f | ||
|
|
10627f8de6 | ||
|
|
61d90da20e | ||
|
|
be13a0ac27 | ||
|
|
aba06b9675 | ||
|
|
a887b0406e | ||
|
|
da1a3df324 | ||
|
|
756f166711 | ||
|
|
0809701fe7 | ||
|
|
ab512476d0 | ||
|
|
b2e6d4c834 | ||
|
|
a67909d109 | ||
|
|
11e3bb3904 | ||
|
|
e3154c23bc | ||
|
|
89589c2c72 | ||
|
|
406d3e16e7 | ||
|
|
aeb19e2f4b | ||
|
|
2e02f1b3bf | ||
|
|
c33eaf4d07 | ||
|
|
afbe8ffeff | ||
|
|
05fcc24bc8 | ||
|
|
768178b153 | ||
|
|
ca7ee3833b | ||
|
|
b0da80bfff | ||
|
|
93850aaf2f | ||
|
|
d26115ccc5 | ||
|
|
1bf20a8334 | ||
|
|
157d9e24d0 | ||
|
|
a6b9d1c948 | ||
|
|
dedf6086fc | ||
|
|
eb5bf1da61 | ||
|
|
879fde8d80 | ||
|
|
5115e0daec | ||
|
|
774866e9ad | ||
|
|
444b96bc73 | ||
|
|
ba0bcc120e | ||
|
|
fadd128d1f | ||
|
|
f3b96dab73 | ||
|
|
68d9db7849 | ||
|
|
1fb9558227 | ||
|
|
c91538493b | ||
|
|
660cac9ee5 | ||
|
|
628257ba6c | ||
|
|
a1f12f7129 | ||
|
|
c7a92d7391 | ||
|
|
4b4eca5a8f | ||
|
|
d04169e6c2 | ||
|
|
92fac775ba | ||
|
|
5dde8aab42 | ||
|
|
c9c2478bbf | ||
|
|
566f9bd8b7 | ||
|
|
56f3a1cc94 | ||
|
|
24b1f08901 | ||
|
|
c8faa3097f | ||
|
|
33c2f9cf81 | ||
|
|
ab579347c6 | ||
|
|
c9b1767b41 | ||
|
|
5e2c92fda6 | ||
|
|
d705ff12f2 | ||
|
|
d469663559 | ||
|
|
4417096bae | ||
|
|
e2ae2c237b | ||
|
|
8ad716f850 | ||
|
|
16109bf052 | ||
|
|
20a8363103 | ||
|
|
d5fa86cdaf | ||
|
|
d5312430ac | ||
|
|
ff8badcd4a | ||
|
|
95123ee6b2 | ||
|
|
d278e36fd2 | ||
|
|
27d25aa47d | ||
|
|
0d4dfb3459 | ||
|
|
871620d18c | ||
|
|
f8041f4d12 | ||
|
|
ecfcccf36e | ||
|
|
e783f726e5 | ||
|
|
c3c20710a2 | ||
|
|
c503186b69 | ||
|
|
105826202e | ||
|
|
97c121a51e | ||
|
|
da39702e33 | ||
|
|
33553ad15b | ||
|
|
1d7eb5057e | ||
|
|
b9e76e7183 | ||
|
|
28651fe9b1 | ||
|
|
8abd53e02f | ||
|
|
19ed23482e | ||
|
|
adb47d25a9 | ||
|
|
5841d8c331 | ||
|
|
12658f6b0c | ||
|
|
97637ac9dd | ||
|
|
e8b1c2783e | ||
|
|
3d5326a22f | ||
|
|
ccce0faef1 | ||
|
|
76551a21f2 | ||
|
|
7eb8c73002 | ||
|
|
28ff69a8e2 | ||
|
|
abe4860a1c | ||
|
|
3bf8733f5e | ||
|
|
9f284b3604 | ||
|
|
6ed7ee7409 | ||
|
|
c3471f8229 | ||
|
|
3844e42cc8 | ||
|
|
3efe4c0261 | ||
|
|
1cfb9fb8cc | ||
|
|
05a4416da1 | ||
|
|
8ac5a0a536 | ||
|
|
668869dbef | ||
|
|
2e050fb328 | ||
|
|
1e9391ad1f | ||
|
|
29d734427f | ||
|
|
b7fdbc22fa | ||
|
|
6c9d183970 | ||
|
|
68a9185070 | ||
|
|
321b7ce901 | ||
|
|
b9aa18a043 | ||
|
|
cc8989a6b3 | ||
|
|
bd345ae949 | ||
|
|
e871a37217 | ||
|
|
671816cd63 | ||
|
|
4785a680da |
3
.aspire/settings.json
Normal file
3
.aspire/settings.json
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"appHostPath": "../src/Aspire/src/AppHost/AppHost.csproj"
|
||||||
|
}
|
||||||
@ -3,18 +3,28 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"dotnet-outdated-tool": {
|
"dotnet-outdated-tool": {
|
||||||
"version": "4.6.4",
|
"version": "4.6.9",
|
||||||
"commands": [
|
"commands": [
|
||||||
"dotnet-outdated"
|
"dotnet-outdated"
|
||||||
],
|
]
|
||||||
"rollForward": false
|
|
||||||
},
|
},
|
||||||
"dotnet-ef": {
|
"dotnet-ef": {
|
||||||
"version": "8.0.8",
|
"version": "10.0.3",
|
||||||
"commands": [
|
"commands": [
|
||||||
"dotnet-ef"
|
"dotnet-ef"
|
||||||
],
|
]
|
||||||
"rollForward": false
|
},
|
||||||
|
"aspire.cli": {
|
||||||
|
"version": "13.1.1",
|
||||||
|
"commands": [
|
||||||
|
"aspire"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"csharpier": {
|
||||||
|
"version": "0.30.6",
|
||||||
|
"commands": [
|
||||||
|
"dotnet-csharpier"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
651
.editorconfig
651
.editorconfig
@ -9,127 +9,167 @@
|
|||||||
## https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/
|
## https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/
|
||||||
## https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
|
## https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
|
||||||
## Microsoft Rules
|
## Microsoft Rules
|
||||||
##
|
|
||||||
|
|
||||||
root = true
|
root = true
|
||||||
# EditorConfig is awesome: http://EditorConfig.org
|
|
||||||
|
|
||||||
# top-most EditorConfig file
|
# All files
|
||||||
|
|
||||||
# Global settings
|
|
||||||
[*]
|
[*]
|
||||||
charset = utf-8
|
|
||||||
end_of_line = lf
|
|
||||||
insert_final_newline = true
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
|
|
||||||
# Xml project files
|
|
||||||
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
|
|
||||||
indent_style = space
|
indent_style = space
|
||||||
|
|
||||||
|
# Xml files
|
||||||
|
[*.xml]
|
||||||
indent_size = 2
|
indent_size = 2
|
||||||
|
|
||||||
# Xml config files
|
# C# files
|
||||||
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 2
|
|
||||||
|
|
||||||
[*.{md,json}]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
|
|
||||||
[*.cs]
|
[*.cs]
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
max_line_length = 100
|
|
||||||
|
|
||||||
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options
|
#### Core EditorConfig Options ####
|
||||||
|
|
||||||
|
# Indentation and spacing
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
max_line_length = 120
|
||||||
|
|
||||||
|
# New line preferences
|
||||||
|
insert_final_newline = false
|
||||||
|
|
||||||
|
#### .NET Coding Conventions ####
|
||||||
|
[*.{cs,vb}]
|
||||||
|
|
||||||
|
# Organize usings
|
||||||
|
dotnet_separate_import_directive_groups = false
|
||||||
|
dotnet_sort_system_directives_first = true
|
||||||
|
file_header_template = unset
|
||||||
|
|
||||||
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/overview?#enable-on-build
|
||||||
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/language-rules#option-format
|
||||||
|
# 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:silent
|
||||||
|
dotnet_style_predefined_type_for_member_access = true:silent
|
||||||
|
|
||||||
|
# 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
|
||||||
|
dotnet_style_coalesce_expression = true:suggestion
|
||||||
|
dotnet_style_collection_initializer = true:suggestion
|
||||||
|
dotnet_style_explicit_tuple_names = true:suggestion
|
||||||
|
dotnet_style_namespace_match_folder = 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_collection_expression = when_types_loosely_match: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_foreach_explicit_cast_in_source = when_strongly_typed: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 ####
|
||||||
|
[*.cs]
|
||||||
|
|
||||||
|
# var preferences
|
||||||
|
csharp_style_var_elsewhere = false:silent
|
||||||
|
csharp_style_var_for_built_in_types = false:silent
|
||||||
|
csharp_style_var_when_type_is_apparent = false: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_extended_property_pattern = 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_anonymous_function = true:suggestion
|
||||||
|
csharp_prefer_static_local_function = true:warning
|
||||||
|
csharp_preferred_modifier_order = public,private,protected,internal,file,const,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async:suggestion
|
||||||
|
csharp_style_prefer_readonly_struct = true:suggestion
|
||||||
|
csharp_style_prefer_readonly_struct_member = true:suggestion
|
||||||
|
|
||||||
|
# Code-block preferences
|
||||||
|
csharp_prefer_braces = true:silent
|
||||||
|
csharp_prefer_simple_using_statement = true:suggestion
|
||||||
|
csharp_style_namespace_declarations = file_scoped:suggestion
|
||||||
|
csharp_style_prefer_method_group_conversion = true:silent
|
||||||
|
csharp_style_prefer_primary_constructors = true:suggestion
|
||||||
|
csharp_style_prefer_top_level_statements = true:silent
|
||||||
|
|
||||||
|
# Expression-level preferences
|
||||||
|
csharp_prefer_simple_default_expression = true:suggestion
|
||||||
|
csharp_style_deconstructed_variable_declaration = true:suggestion
|
||||||
|
csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion
|
||||||
|
csharp_style_inlined_variable_declaration = true:suggestion
|
||||||
|
csharp_style_prefer_index_operator = true:suggestion
|
||||||
|
csharp_style_prefer_local_over_anonymous_function = true:suggestion
|
||||||
|
csharp_style_prefer_null_check_over_type_check = true:suggestion
|
||||||
|
csharp_style_prefer_range_operator = true:suggestion
|
||||||
|
csharp_style_prefer_tuple_swap = true:suggestion
|
||||||
|
csharp_style_prefer_utf8_string_literals = 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
|
||||||
|
|
||||||
|
#### C# Formatting Rules ####
|
||||||
|
|
||||||
# New line preferences
|
# New line preferences
|
||||||
csharp_new_line_before_open_brace = all
|
|
||||||
csharp_new_line_before_else = true
|
|
||||||
csharp_new_line_before_catch = true
|
csharp_new_line_before_catch = true
|
||||||
|
csharp_new_line_before_else = true
|
||||||
csharp_new_line_before_finally = true
|
csharp_new_line_before_finally = true
|
||||||
csharp_new_line_before_members_in_object_initializers = true
|
|
||||||
csharp_new_line_before_members_in_anonymous_types = 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
|
csharp_new_line_between_query_expression_clauses = true
|
||||||
|
|
||||||
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options#indentation-options
|
|
||||||
# Indentation preferences
|
# Indentation preferences
|
||||||
csharp_indent_block_contents = true
|
csharp_indent_block_contents = true
|
||||||
csharp_indent_braces = false
|
csharp_indent_braces = false
|
||||||
csharp_indent_case_contents = true
|
csharp_indent_case_contents = true
|
||||||
csharp_indent_switch_labels = true
|
csharp_indent_case_contents_when_block = true
|
||||||
csharp_indent_labels = one_less_than_current
|
csharp_indent_labels = one_less_than_current
|
||||||
|
csharp_indent_switch_labels = true
|
||||||
# avoid this. unless absolutely necessary
|
|
||||||
dotnet_style_qualification_for_field = false:suggestion
|
|
||||||
dotnet_style_qualification_for_property = false:suggestion
|
|
||||||
dotnet_style_qualification_for_method = false:suggestion
|
|
||||||
dotnet_style_qualification_for_event = false:suggestion
|
|
||||||
|
|
||||||
# only use var when it's obvious what the variable type is
|
|
||||||
# csharp_style_var_for_built_in_types = false:none
|
|
||||||
# csharp_style_var_when_type_is_apparent = false:none
|
|
||||||
# csharp_style_var_elsewhere = false:suggestion
|
|
||||||
|
|
||||||
# use language keywords instead of BCL types
|
|
||||||
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion
|
|
||||||
dotnet_style_predefined_type_for_member_access = true:suggestion
|
|
||||||
|
|
||||||
# name all constant fields using PascalCase
|
|
||||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion
|
|
||||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields
|
|
||||||
dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style
|
|
||||||
|
|
||||||
dotnet_naming_symbols.constant_fields.applicable_kinds = field
|
|
||||||
dotnet_naming_symbols.constant_fields.required_modifiers = const
|
|
||||||
|
|
||||||
dotnet_naming_style.pascal_case_style.capitalization = pascal_case
|
|
||||||
|
|
||||||
dotnet_naming_symbols.static_fields.applicable_kinds = field
|
|
||||||
dotnet_naming_symbols.static_fields.required_modifiers = static
|
|
||||||
|
|
||||||
dotnet_naming_style.static_prefix_style.required_prefix = s_
|
|
||||||
dotnet_naming_style.static_prefix_style.capitalization = camel_case
|
|
||||||
|
|
||||||
# internal and private fields should be _camelCase
|
|
||||||
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
|
|
||||||
dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields
|
|
||||||
dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style
|
|
||||||
|
|
||||||
dotnet_naming_symbols.private_internal_fields.applicable_kinds = field
|
|
||||||
dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal
|
|
||||||
|
|
||||||
dotnet_naming_style.camel_case_underscore_style.required_prefix = _
|
|
||||||
dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case
|
|
||||||
|
|
||||||
# Code style defaults
|
|
||||||
dotnet_sort_system_directives_first = true
|
|
||||||
csharp_preserve_single_line_blocks = true
|
|
||||||
csharp_preserve_single_line_statements = false
|
|
||||||
|
|
||||||
# Expression-level preferences
|
|
||||||
dotnet_style_object_initializer = true:suggestion
|
|
||||||
dotnet_style_collection_initializer = true:suggestion
|
|
||||||
dotnet_style_explicit_tuple_names = true:suggestion
|
|
||||||
dotnet_style_coalesce_expression = true:suggestion
|
|
||||||
dotnet_style_null_propagation = true:suggestion
|
|
||||||
|
|
||||||
# Expression-bodied members
|
|
||||||
csharp_style_expression_bodied_methods = false:none
|
|
||||||
csharp_style_expression_bodied_constructors = false:none
|
|
||||||
csharp_style_expression_bodied_operators = false:none
|
|
||||||
csharp_style_expression_bodied_properties = true:none
|
|
||||||
csharp_style_expression_bodied_indexers = true:none
|
|
||||||
csharp_style_expression_bodied_accessors = true:none
|
|
||||||
|
|
||||||
# Pattern matching
|
|
||||||
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
|
|
||||||
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
|
|
||||||
csharp_style_inlined_variable_declaration = true:suggestion
|
|
||||||
|
|
||||||
# Null checking preferences
|
|
||||||
csharp_style_throw_expression = true:suggestion
|
|
||||||
csharp_style_conditional_delegate_call = true:suggestion
|
|
||||||
|
|
||||||
# Space preferences
|
# Space preferences
|
||||||
csharp_space_after_cast = false
|
csharp_space_after_cast = false
|
||||||
@ -139,6 +179,7 @@ csharp_space_after_dot = false
|
|||||||
csharp_space_after_keywords_in_control_flow_statements = true
|
csharp_space_after_keywords_in_control_flow_statements = true
|
||||||
csharp_space_after_semicolon_in_for_statement = true
|
csharp_space_after_semicolon_in_for_statement = true
|
||||||
csharp_space_around_binary_operators = before_and_after
|
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_colon_in_inheritance_clause = true
|
||||||
csharp_space_before_comma = false
|
csharp_space_before_comma = false
|
||||||
csharp_space_before_dot = false
|
csharp_space_before_dot = false
|
||||||
@ -154,6 +195,200 @@ csharp_space_between_method_declaration_parameter_list_parentheses = false
|
|||||||
csharp_space_between_parentheses = false
|
csharp_space_between_parentheses = false
|
||||||
csharp_space_between_square_brackets = false
|
csharp_space_between_square_brackets = false
|
||||||
|
|
||||||
|
# Wrapping preferences
|
||||||
|
csharp_preserve_single_line_blocks = true
|
||||||
|
csharp_preserve_single_line_statements = true
|
||||||
|
|
||||||
|
#### Naming styles ####
|
||||||
|
[*.{cs,vb}]
|
||||||
|
|
||||||
|
# Naming rules
|
||||||
|
|
||||||
|
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
|
||||||
|
dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
|
||||||
|
dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
|
||||||
|
dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
|
||||||
|
dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
|
||||||
|
dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.events_should_be_pascalcase.symbols = events
|
||||||
|
dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
|
||||||
|
dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
|
||||||
|
dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
|
||||||
|
dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
|
||||||
|
dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
|
||||||
|
dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
|
||||||
|
dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
|
||||||
|
dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
|
||||||
|
dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
|
||||||
|
dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
|
||||||
|
dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
|
||||||
|
dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
|
||||||
|
dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
|
||||||
|
dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
|
||||||
|
|
||||||
|
# Symbol specifications
|
||||||
|
|
||||||
|
dotnet_naming_symbols.interfaces.applicable_kinds = interface
|
||||||
|
dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.interfaces.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.enums.applicable_kinds = enum
|
||||||
|
dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.enums.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.events.applicable_kinds = event
|
||||||
|
dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.events.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.methods.applicable_kinds = method
|
||||||
|
dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.methods.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.properties.applicable_kinds = property
|
||||||
|
dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.properties.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.public_fields.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
|
||||||
|
dotnet_naming_symbols.public_fields.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.private_fields.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.private_fields.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.private_static_fields.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.private_static_fields.required_modifiers = static
|
||||||
|
|
||||||
|
dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
|
||||||
|
dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.types_and_namespaces.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
|
||||||
|
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.non_field_members.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
|
||||||
|
dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
|
||||||
|
dotnet_naming_symbols.type_parameters.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.private_constant_fields.required_modifiers = const
|
||||||
|
|
||||||
|
dotnet_naming_symbols.local_variables.applicable_kinds = local
|
||||||
|
dotnet_naming_symbols.local_variables.applicable_accessibilities = local
|
||||||
|
dotnet_naming_symbols.local_variables.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.local_constants.applicable_kinds = local
|
||||||
|
dotnet_naming_symbols.local_constants.applicable_accessibilities = local
|
||||||
|
dotnet_naming_symbols.local_constants.required_modifiers = const
|
||||||
|
|
||||||
|
dotnet_naming_symbols.parameters.applicable_kinds = parameter
|
||||||
|
dotnet_naming_symbols.parameters.applicable_accessibilities = *
|
||||||
|
dotnet_naming_symbols.parameters.required_modifiers =
|
||||||
|
|
||||||
|
dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
|
||||||
|
dotnet_naming_symbols.public_constant_fields.required_modifiers = const
|
||||||
|
|
||||||
|
dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
|
||||||
|
dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
|
||||||
|
|
||||||
|
dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
|
||||||
|
dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
|
||||||
|
dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
|
||||||
|
|
||||||
|
dotnet_naming_symbols.local_functions.applicable_kinds = local_function
|
||||||
|
dotnet_naming_symbols.local_functions.applicable_accessibilities = *
|
||||||
|
dotnet_naming_symbols.local_functions.required_modifiers =
|
||||||
|
|
||||||
|
# Naming styles
|
||||||
|
|
||||||
|
dotnet_naming_style.pascalcase.required_prefix =
|
||||||
|
dotnet_naming_style.pascalcase.required_suffix =
|
||||||
|
dotnet_naming_style.pascalcase.word_separator =
|
||||||
|
dotnet_naming_style.pascalcase.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.ipascalcase.required_prefix = I
|
||||||
|
dotnet_naming_style.ipascalcase.required_suffix =
|
||||||
|
dotnet_naming_style.ipascalcase.word_separator =
|
||||||
|
dotnet_naming_style.ipascalcase.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style.tpascalcase.required_prefix = T
|
||||||
|
dotnet_naming_style.tpascalcase.required_suffix =
|
||||||
|
dotnet_naming_style.tpascalcase.word_separator =
|
||||||
|
dotnet_naming_style.tpascalcase.capitalization = pascal_case
|
||||||
|
|
||||||
|
dotnet_naming_style._camelcase.required_prefix = _
|
||||||
|
dotnet_naming_style._camelcase.required_suffix =
|
||||||
|
dotnet_naming_style._camelcase.word_separator =
|
||||||
|
dotnet_naming_style._camelcase.capitalization = camel_case
|
||||||
|
|
||||||
|
dotnet_naming_style.camelcase.required_prefix =
|
||||||
|
dotnet_naming_style.camelcase.required_suffix =
|
||||||
|
dotnet_naming_style.camelcase.word_separator =
|
||||||
|
dotnet_naming_style.camelcase.capitalization = camel_case
|
||||||
|
|
||||||
|
dotnet_naming_style.s_camelcase.required_prefix = s_
|
||||||
|
dotnet_naming_style.s_camelcase.required_suffix =
|
||||||
|
dotnet_naming_style.s_camelcase.word_separator =
|
||||||
|
dotnet_naming_style.s_camelcase.capitalization = camel_case
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/quality-rules/
|
||||||
@ -175,7 +410,7 @@ dotnet_diagnostic.CA1304.severity = error
|
|||||||
# CA1307: Specify StringComparison for clarity
|
# CA1307: Specify StringComparison for clarity
|
||||||
dotnet_diagnostic.CA1307.severity = error
|
dotnet_diagnostic.CA1307.severity = error
|
||||||
# CA1308: Normalize strings to uppercase
|
# CA1308: Normalize strings to uppercase
|
||||||
dotnet_diagnostic.CA1308.severity = error
|
dotnet_diagnostic.CA1308.severity = none
|
||||||
# CA1309: Use ordinal StringComparison
|
# CA1309: Use ordinal StringComparison
|
||||||
dotnet_diagnostic.CA1309.severity = error
|
dotnet_diagnostic.CA1309.severity = error
|
||||||
# CA1724: Type names should not match namespaces
|
# CA1724: Type names should not match namespaces
|
||||||
@ -210,7 +445,18 @@ dotnet_diagnostic.ca1848.severity = Suggestion
|
|||||||
dotnet_diagnostic.ca1810.severity = Suggestion
|
dotnet_diagnostic.ca1810.severity = Suggestion
|
||||||
# CA1725: Parameter names should match base declaration
|
# CA1725: Parameter names should match base declaration
|
||||||
dotnet_diagnostic.ca1725.severity = Suggestion
|
dotnet_diagnostic.ca1725.severity = Suggestion
|
||||||
# https://csharpier.com/docs/IntegratingWithLinters#code-analysis-rules
|
# CA1515: Consider making public types internal
|
||||||
|
dotnet_diagnostic.CA1515.severity = None
|
||||||
|
# CA2000: Dispose objects before losing scope
|
||||||
|
dotnet_diagnostic.CA2000.severity = Suggestion
|
||||||
|
# CA1707: Identifiers should not contain underscores
|
||||||
|
dotnet_diagnostic.CA1707.severity = None
|
||||||
|
# CA1716: Identifiers should not match keywords
|
||||||
|
dotnet_diagnostic.CA1716.severity = Suggestion
|
||||||
|
# CA1032: Implement standard exception constructors
|
||||||
|
dotnet_diagnostic.CA1032.severity = Suggestion
|
||||||
|
# AV1500: A method should not exceed a predefined number (60-100 lines) of lines
|
||||||
|
dotnet_diagnostic.AV1500.severity = none
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/
|
||||||
@ -220,7 +466,7 @@ dotnet_diagnostic.ca1725.severity = Suggestion
|
|||||||
dotnet_diagnostic.IDE0048.severity = Suggestion
|
dotnet_diagnostic.IDE0048.severity = Suggestion
|
||||||
dotnet_diagnostic.IDE0028.severity = Suggestion
|
dotnet_diagnostic.IDE0028.severity = Suggestion
|
||||||
dotnet_diagnostic.IDE0029.severity = Suggestion
|
dotnet_diagnostic.IDE0029.severity = Suggestion
|
||||||
dotnet_diagnostic.IDE0030.severity = Suggestion
|
dotnet_diagnostic.IDE0030 .severity = Suggestion
|
||||||
dotnet_diagnostic.IDE0004.severity = error
|
dotnet_diagnostic.IDE0004.severity = error
|
||||||
|
|
||||||
# IDE0005: Remove unnecessary usings/imports
|
# IDE0005: Remove unnecessary usings/imports
|
||||||
@ -230,16 +476,21 @@ dotnet_diagnostic.IDE0005.severity = warning
|
|||||||
dotnet_diagnostic.IDE0051.severity = Suggestion
|
dotnet_diagnostic.IDE0051.severity = Suggestion
|
||||||
|
|
||||||
# IDE0052: Remove unread private members (writes but no reads)
|
# IDE0052: Remove unread private members (writes but no reads)
|
||||||
dotnet_diagnostic.IDE0052.severity = error
|
dotnet_diagnostic.IDE0052.severity = warning
|
||||||
|
|
||||||
|
# Remove unnecessary using directives (IDE0005)
|
||||||
|
dotnet_diagnostic.IDE0005.severity = none
|
||||||
|
|
||||||
|
# CS1574: XML comment on 'construct' has syntactically incorrect cref attribute 'name'
|
||||||
|
dotnet_diagnostic.CS1574.severity = error
|
||||||
|
|
||||||
# IDE0055: Fix formatting
|
# IDE0055: Fix formatting
|
||||||
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options
|
||||||
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/dotnet-formatting-options
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/dotnet-formatting-options
|
||||||
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055
|
||||||
dotnet_diagnostic.IDE0055.severity = suggestion
|
|
||||||
|
|
||||||
# CS1574: XML comment on 'construct' has syntactically incorrect cref attribute 'name'
|
# https://csharpier.com/docs/IntegratingWithLinters#code-analysis-rules
|
||||||
dotnet_diagnostic.CS1574.severity = error
|
dotnet_diagnostic.IDE0055.severity = none
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
# https://jetbrains.com.xy2401.com/help/resharper/EditorConfig_Index.html
|
# https://jetbrains.com.xy2401.com/help/resharper/EditorConfig_Index.html
|
||||||
@ -373,59 +624,6 @@ resharper_blank_lines_before_multiline_statements = 1
|
|||||||
resharper_parentheses_non_obvious_operations = arithmetic, multiplicative, equality, relational, additive
|
resharper_parentheses_non_obvious_operations = arithmetic, multiplicative, equality, relational, additive
|
||||||
resharper_parentheses_redundancy_style = remove_if_not_clarifies_precedence
|
resharper_parentheses_redundancy_style = remove_if_not_clarifies_precedence
|
||||||
|
|
||||||
##################################################################################
|
|
||||||
## https://github.com/bkoelman/CSharpGuidelinesAnalyzer
|
|
||||||
## CSharpGuidelines
|
|
||||||
##################################################################################
|
|
||||||
|
|
||||||
dotnet_diagnostic.AV1561.max_parameter_count = 5
|
|
||||||
# AV1008: Class should be non-static or its name should be suffixed with Extensions
|
|
||||||
dotnet_diagnostic.AV1008.severity = none
|
|
||||||
# AV1010: Type hides inherited member
|
|
||||||
dotnet_diagnostic.AV1010.severity = none
|
|
||||||
# AV1115: Member or local function contains the word 'and', which suggests doing multiple things
|
|
||||||
dotnet_diagnostic.AV1115.severity = suggestion
|
|
||||||
# AV1130: Return type in signature for Type should be a collection interface instead of a concrete type
|
|
||||||
dotnet_diagnostic.AV1130.severity = none
|
|
||||||
# AV1135: null is returned from method which has return type of string, collection or task
|
|
||||||
dotnet_diagnostic.AV1135.severity = none # re-enable if we can distinguish between string, collection and task
|
|
||||||
# AV1210: Catch a specific exception instead of Exception, SystemException or ApplicationException
|
|
||||||
dotnet_diagnostic.AV1210.severity = none
|
|
||||||
# AV1250: Evaluate LINQ query before returning it
|
|
||||||
dotnet_diagnostic.AV1250.severity = suggestion
|
|
||||||
# AV1500: Method 'CallerIdentifier.DetermineCallerIdentity()' contains 10 statements, which exceeds the maximum of 7 statements
|
|
||||||
dotnet_diagnostic.AV1500.severity = none
|
|
||||||
# AV1532: Loop statement contains nested loop
|
|
||||||
dotnet_diagnostic.AV1532.severity = suggestion
|
|
||||||
# AV1535: Missing block in case or default clause of switch statement
|
|
||||||
dotnet_diagnostic.AV1535.severity = none # re-enable if we can adjust the formatting to not indent the scope braces
|
|
||||||
# AV1537: If-else-if construct should end with an unconditional else clause
|
|
||||||
dotnet_diagnostic.AV1537.severity = suggestion
|
|
||||||
# AV1551: Method overload with the most parameters should be virtual
|
|
||||||
dotnet_diagnostic.AV1551.severity = none
|
|
||||||
# AV1555: Avoid using non-(nullable-)boolean named arguments
|
|
||||||
dotnet_diagnostic.AV1555.severity = suggestion
|
|
||||||
# AV1561: Method contains 5 parameters, which exceeds the maximum of 3 parameters
|
|
||||||
dotnet_diagnostic.AV1561.severity = suggestion
|
|
||||||
# AV1564: Parameter in public or internal member is of type bool or bool?
|
|
||||||
dotnet_diagnostic.AV1564.severity = suggestion
|
|
||||||
# AV1554: Do not use optional parameters in interface methods or their concrete implementations
|
|
||||||
dotnet_diagnostic.AV1554.severity = none
|
|
||||||
# AV1580: Argument for parameter calls nested method
|
|
||||||
dotnet_diagnostic.AV1580.severity = none
|
|
||||||
# AV1706: Parameter 'p' should have a more descriptive name
|
|
||||||
dotnet_diagnostic.AV1706.severity = warning
|
|
||||||
# AV1708: Type name contains term that should be avoided
|
|
||||||
dotnet_diagnostic.AV1708.severity = suggestion
|
|
||||||
# AV1710: Field contains the name of its containing type
|
|
||||||
dotnet_diagnostic.AV1710.severity = none
|
|
||||||
# AV2202: Replace call to Nullable<T>.HasValue with null check
|
|
||||||
dotnet_diagnostic.AV2202.severity = none
|
|
||||||
# AV2305: Missing XML comment for internally visible type or member
|
|
||||||
dotnet_diagnostic.AV2305.severity = none
|
|
||||||
# AV2407: Region should be removed
|
|
||||||
dotnet_diagnostic.AV2407.severity = none
|
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
## https://github.com/DotNetAnalyzers/StyleCopAnalyzers/tree/master/documentation
|
## https://github.com/DotNetAnalyzers/StyleCopAnalyzers/tree/master/documentation
|
||||||
## https://documentation.help/StyleCop/StyleCop.html
|
## https://documentation.help/StyleCop/StyleCop.html
|
||||||
@ -548,16 +746,85 @@ dotnet_diagnostic.sa1101.severity = None
|
|||||||
# The keywords within the declaration of an element do not follow a standard ordering scheme.
|
# The keywords within the declaration of an element do not follow a standard ordering scheme.
|
||||||
dotnet_diagnostic.SA1206.severity = None
|
dotnet_diagnostic.SA1206.severity = None
|
||||||
|
|
||||||
# A single-line comment within C# code is not preceded by a blank line.
|
dotnet_diagnostic.SA1106.severity = None
|
||||||
dotnet_diagnostic.SA1515.severity = Suggestion
|
|
||||||
|
# https://csharpier.com/docs/IntegratingWithLinters#stylecopanalyzers
|
||||||
|
# IDE0055: Fix formatting
|
||||||
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/csharp-formatting-options
|
||||||
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/dotnet-formatting-options
|
||||||
|
# https://learn.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/ide0055
|
||||||
|
|
||||||
|
# StyleCopAnalyzers
|
||||||
|
dotnet_diagnostic.SA1000.severity = none
|
||||||
|
dotnet_diagnostic.SA1009.severity = none
|
||||||
|
dotnet_diagnostic.SA1111.severity = none
|
||||||
|
dotnet_diagnostic.SA1118.severity = none
|
||||||
|
dotnet_diagnostic.SA1137.severity = none
|
||||||
|
dotnet_diagnostic.SA1413.severity = none
|
||||||
|
dotnet_diagnostic.SA1500.severity = none
|
||||||
|
dotnet_diagnostic.SA1501.severity = none
|
||||||
|
dotnet_diagnostic.SA1502.severity = none
|
||||||
|
dotnet_diagnostic.SA1504.severity = none
|
||||||
|
dotnet_diagnostic.SA1515.severity = none
|
||||||
|
dotnet_diagnostic.SA1516.severity = none
|
||||||
|
|
||||||
|
# for csharpier <= 0.21.0
|
||||||
|
dotnet_diagnostic.SA1127.severity = none
|
||||||
|
dotnet_diagnostic.SA1128.severity = none
|
||||||
|
|
||||||
|
dotnet_diagnostic.SA1001.severity = none
|
||||||
|
dotnet_diagnostic.SA1002.severity = none
|
||||||
|
dotnet_diagnostic.SA1003.severity = none
|
||||||
|
dotnet_diagnostic.SA1007.severity = none
|
||||||
|
dotnet_diagnostic.SA1008.severity = none
|
||||||
|
dotnet_diagnostic.SA1010.severity = none
|
||||||
|
dotnet_diagnostic.SA1011.severity = none
|
||||||
|
dotnet_diagnostic.SA1012.severity = none
|
||||||
|
dotnet_diagnostic.SA1013.severity = none
|
||||||
|
dotnet_diagnostic.SA1014.severity = none
|
||||||
|
dotnet_diagnostic.SA1015.severity = none
|
||||||
|
dotnet_diagnostic.SA1016.severity = none
|
||||||
|
dotnet_diagnostic.SA1017.severity = none
|
||||||
|
dotnet_diagnostic.SA1018.severity = none
|
||||||
|
dotnet_diagnostic.SA1019.severity = none
|
||||||
|
dotnet_diagnostic.SA1020.severity = none
|
||||||
|
dotnet_diagnostic.SA1021.severity = none
|
||||||
|
dotnet_diagnostic.SA1022.severity = none
|
||||||
|
dotnet_diagnostic.SA1023.severity = none
|
||||||
|
dotnet_diagnostic.SA1024.severity = none
|
||||||
|
dotnet_diagnostic.SA1025.severity = none
|
||||||
|
dotnet_diagnostic.SA1026.severity = none
|
||||||
|
dotnet_diagnostic.SA1027.severity = none
|
||||||
|
dotnet_diagnostic.SA1028.severity = none
|
||||||
|
dotnet_diagnostic.SA1102.severity = none
|
||||||
|
dotnet_diagnostic.SA1103.severity = none
|
||||||
|
dotnet_diagnostic.SA1104.severity = none
|
||||||
|
dotnet_diagnostic.SA1105.severity = none
|
||||||
|
dotnet_diagnostic.SA1107.severity = none
|
||||||
|
dotnet_diagnostic.SA1110.severity = none
|
||||||
|
dotnet_diagnostic.SA1112.severity = none
|
||||||
|
dotnet_diagnostic.SA1113.severity = none
|
||||||
|
dotnet_diagnostic.SA1114.severity = none
|
||||||
|
dotnet_diagnostic.SA1115.severity = none
|
||||||
|
dotnet_diagnostic.SA1116.severity = none
|
||||||
|
dotnet_diagnostic.SA1117.severity = none
|
||||||
|
dotnet_diagnostic.SA1127.severity = none
|
||||||
|
dotnet_diagnostic.SA1128.severity = none
|
||||||
|
dotnet_diagnostic.SA1136.severity = none
|
||||||
|
dotnet_diagnostic.SA1505.severity = none
|
||||||
|
dotnet_diagnostic.SA1506.severity = none
|
||||||
|
dotnet_diagnostic.SA1507.severity = none
|
||||||
|
dotnet_diagnostic.SA1508.severity = none
|
||||||
|
dotnet_diagnostic.SA1509.severity = none
|
||||||
|
dotnet_diagnostic.SA1510.severity = none
|
||||||
|
dotnet_diagnostic.SA1511.severity = none
|
||||||
|
dotnet_diagnostic.SA1517.severity = none
|
||||||
|
dotnet_diagnostic.SA1518.severity = none
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
## https://github.com/meziantou/Meziantou.Analyzer/tree/main/docs
|
## https://github.com/meziantou/Meziantou.Analyzer/tree/main/docs
|
||||||
## Meziantou.Analyzer
|
## Meziantou.Analyzer
|
||||||
|
|
||||||
# MA0049: Type name should not match containing namespace
|
|
||||||
dotnet_diagnostic.ma0049.severity = Suggestion
|
|
||||||
|
|
||||||
# MA0048: File name must match type name
|
# MA0048: File name must match type name
|
||||||
dotnet_diagnostic.ma0048.severity = Suggestion
|
dotnet_diagnostic.ma0048.severity = Suggestion
|
||||||
|
|
||||||
@ -611,11 +878,14 @@ dotnet_diagnostic.MA0047.severity = none
|
|||||||
# Use an overload of 'GetHashCode' that has a StringComparison parameter
|
# Use an overload of 'GetHashCode' that has a StringComparison parameter
|
||||||
dotnet_diagnostic.MA0074.severity = none
|
dotnet_diagnostic.MA0074.severity = none
|
||||||
|
|
||||||
|
# MA0049: Type name should not match containing namespace
|
||||||
|
dotnet_diagnostic.MA0049.severity = none
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
## http://pihrt.net/Roslynator/Analyzers
|
|
||||||
## http://pihrt.net/Roslynator/Refactorings
|
|
||||||
## https://github.com/JosefPihrt/Roslynator/blob/main/docs/Configuration.md
|
## https://github.com/JosefPihrt/Roslynator/blob/main/docs/Configuration.md
|
||||||
|
## https://josefpihrt.github.io/docs/
|
||||||
## Roslynator
|
## Roslynator
|
||||||
|
##################################################################################
|
||||||
|
|
||||||
# RCS1036 - Remove redundant empty line.
|
# RCS1036 - Remove redundant empty line.
|
||||||
dotnet_diagnostic.rcs1036.severity = None
|
dotnet_diagnostic.rcs1036.severity = None
|
||||||
@ -650,7 +920,6 @@ dotnet_diagnostic.rcs1047.severity = error
|
|||||||
# RCS1174: Remove redundant async/await
|
# RCS1174: Remove redundant async/await
|
||||||
dotnet_diagnostic.rcs1174.severity = Suggestion
|
dotnet_diagnostic.rcs1174.severity = Suggestion
|
||||||
|
|
||||||
|
|
||||||
# Combine 'Enumerable.Where' method chain. It doesn't make it more readable in all cases.
|
# Combine 'Enumerable.Where' method chain. It doesn't make it more readable in all cases.
|
||||||
dotnet_diagnostic.RCS1112.severity = suggestion
|
dotnet_diagnostic.RCS1112.severity = suggestion
|
||||||
|
|
||||||
@ -688,6 +957,30 @@ dotnet_diagnostic.RCS1237.severity = none
|
|||||||
# RCS1228: Unused element in documentation comment. (Equivalent to SA1614)
|
# RCS1228: Unused element in documentation comment. (Equivalent to SA1614)
|
||||||
dotnet_diagnostic.RCS1228.severity = suggestion
|
dotnet_diagnostic.RCS1228.severity = suggestion
|
||||||
|
|
||||||
|
# RCS1047: Non-asynchronous method name should not end with 'Async'
|
||||||
|
#dotnet_diagnostic.RCS1047.severity = suggestion
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
## https://github.com/semihokur/asyncfixer
|
||||||
|
## AsyncFixer01
|
||||||
|
##################################################################################
|
||||||
|
|
||||||
|
# https://cezarypiatek.github.io/post/async-analyzers-p1/#1-redundant-asyncawait
|
||||||
|
# AsyncFixer01: Unnecessary async/await usage
|
||||||
|
dotnet_diagnostic.asyncfixer01.severity = Suggestion
|
||||||
|
|
||||||
|
# https://cezarypiatek.github.io/post/async-analyzers-p1/#2-calling-synchronous-method-inside-the-async-method
|
||||||
|
# AsyncFixer02: Long-running or blocking operations inside an async method
|
||||||
|
dotnet_diagnostic.asyncfixer02.severity = error
|
||||||
|
|
||||||
|
# https://cezarypiatek.github.io/post/async-analyzers-p1/#3-async-void-method
|
||||||
|
# AsyncFixer03: Fire & forget async void methods
|
||||||
|
dotnet_diagnostic.asyncfixer03.severity = error
|
||||||
|
|
||||||
|
# https://cezarypiatek.github.io/post/async-analyzers-p1/#6-not-awaited-task-inside-the-using-block
|
||||||
|
# AsyncFixer04: Fire & forget async call inside a using block
|
||||||
|
dotnet_diagnostic.asyncfixer04.severity = error
|
||||||
|
|
||||||
##################################################################################
|
##################################################################################
|
||||||
## https://github.com/microsoft/vs-threading
|
## https://github.com/microsoft/vs-threading
|
||||||
## Microsoft.VisualStudio.Threading.Analyzers
|
## Microsoft.VisualStudio.Threading.Analyzers
|
||||||
@ -697,7 +990,6 @@ dotnet_diagnostic.RCS1228.severity = suggestion
|
|||||||
# VSTHRD103: Call async methods when in an async method
|
# VSTHRD103: Call async methods when in an async method
|
||||||
dotnet_diagnostic.vsthrd103.severity = Suggestion
|
dotnet_diagnostic.vsthrd103.severity = Suggestion
|
||||||
|
|
||||||
|
|
||||||
# https://cezarypiatek.github.io/post/async-analyzers-p1/#3-async-void-method
|
# https://cezarypiatek.github.io/post/async-analyzers-p1/#3-async-void-method
|
||||||
# VSTHRD100: Avoid async void methods
|
# VSTHRD100: Avoid async void methods
|
||||||
dotnet_diagnostic.vsthrd100.severity = error
|
dotnet_diagnostic.vsthrd100.severity = error
|
||||||
@ -733,3 +1025,20 @@ dotnet_diagnostic.vsthrd200.severity = Suggestion
|
|||||||
# https://cezarypiatek.github.io/post/async-analyzers-p2/#12-non-asynchronous-method-names-shouldnt-end-with-async
|
# https://cezarypiatek.github.io/post/async-analyzers-p2/#12-non-asynchronous-method-names-shouldnt-end-with-async
|
||||||
# VSTHRD200: Use "Async" suffix for async methods
|
# VSTHRD200: Use "Async" suffix for async methods
|
||||||
dotnet_diagnostic.vsthrd200.severity = Suggestion
|
dotnet_diagnostic.vsthrd200.severity = Suggestion
|
||||||
|
|
||||||
|
# VSTHRD003 Avoid awaiting foreign Tasks
|
||||||
|
dotnet_diagnostic.VSTHRD003.severity = Suggestion
|
||||||
|
|
||||||
|
|
||||||
|
##################################################################################
|
||||||
|
## https://github.com/hvanbakel/Asyncify-CSharp
|
||||||
|
## Asyncify
|
||||||
|
##################################################################################
|
||||||
|
|
||||||
|
# https://cezarypiatek.github.io/post/async-analyzers-p2/#8-synchronous-waits
|
||||||
|
# AsyncifyInvocation: Use Task Async
|
||||||
|
dotnet_diagnostic.asyncifyinvocation.severity = error
|
||||||
|
|
||||||
|
# https://cezarypiatek.github.io/post/async-analyzers-p2/#8-synchronous-waits
|
||||||
|
# AsyncifyVariable: Use Task Async
|
||||||
|
dotnet_diagnostic.asyncifyvariable.severity = error
|
||||||
|
|||||||
64
.gitattributes
vendored
Normal file
64
.gitattributes
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
###############################################################################
|
||||||
|
# Set default behavior to automatically normalize line endings.
|
||||||
|
###############################################################################
|
||||||
|
* text=auto
|
||||||
|
*.sh text eol=lf
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set default behavior for command prompt diff.
|
||||||
|
#
|
||||||
|
# This is need for earlier builds of msysgit that does not have it on by
|
||||||
|
# default for csharp files.
|
||||||
|
# Note: This is only used by command line
|
||||||
|
###############################################################################
|
||||||
|
#*.cs diff=csharp
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Set the merge driver for project and solution files
|
||||||
|
#
|
||||||
|
# Merging from the command prompt will add diff markers to the files if there
|
||||||
|
# are conflicts (Merging from VS is not affected by the settings below, in VS
|
||||||
|
# the diff markers are never inserted). Diff markers may cause the following
|
||||||
|
# file extensions to fail to load in VS. An alternative would be to treat
|
||||||
|
# these files as binary and thus will always conflict and require user
|
||||||
|
# intervention with every merge. To do so, just uncomment the entries below
|
||||||
|
###############################################################################
|
||||||
|
#*.sln merge=binary
|
||||||
|
#*.csproj merge=binary
|
||||||
|
#*.vbproj merge=binary
|
||||||
|
#*.vcxproj merge=binary
|
||||||
|
#*.vcproj merge=binary
|
||||||
|
#*.dbproj merge=binary
|
||||||
|
#*.fsproj merge=binary
|
||||||
|
#*.lsproj merge=binary
|
||||||
|
#*.wixproj merge=binary
|
||||||
|
#*.modelproj merge=binary
|
||||||
|
#*.sqlproj merge=binary
|
||||||
|
#*.wwaproj merge=binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# behavior for image files
|
||||||
|
#
|
||||||
|
# image files are treated as binary by default.
|
||||||
|
###############################################################################
|
||||||
|
#*.jpg binary
|
||||||
|
#*.png binary
|
||||||
|
#*.gif binary
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# diff behavior for common document formats
|
||||||
|
#
|
||||||
|
# Convert binary document formats to text before diffing them. This feature
|
||||||
|
# is only available from the command line. Turn it on by uncommenting the
|
||||||
|
# entries below.
|
||||||
|
###############################################################################
|
||||||
|
#*.doc diff=astextplain
|
||||||
|
#*.DOC diff=astextplain
|
||||||
|
#*.docx diff=astextplain
|
||||||
|
#*.DOCX diff=astextplain
|
||||||
|
#*.dot diff=astextplain
|
||||||
|
#*.DOT diff=astextplain
|
||||||
|
#*.pdf diff=astextplain
|
||||||
|
#*.PDF diff=astextplain
|
||||||
|
#*.rtf diff=astextplain
|
||||||
|
#*.RTF diff=astextplain
|
||||||
6
.github/actions/build/action.yml
vendored
6
.github/actions/build/action.yml
vendored
@ -25,14 +25,16 @@ runs:
|
|||||||
# https://devblogs.microsoft.com/dotnet/dotnet-loves-github-actions/
|
# https://devblogs.microsoft.com/dotnet/dotnet-loves-github-actions/
|
||||||
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net#caching-dependencies
|
# https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net#caching-dependencies
|
||||||
- name: Cache NuGet Packages
|
- name: Cache NuGet Packages
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v4
|
||||||
if: success()
|
if: success()
|
||||||
with:
|
with:
|
||||||
path: ~/.nuget/packages
|
path: ~/.nuget/packages
|
||||||
key: ${{ runner.os }}-dotnet-nuget
|
key: ${{ runner.os }}-dotnet-nuget
|
||||||
|
|
||||||
- name: Setup .NET
|
- name: Setup .NET
|
||||||
uses: actions/setup-dotnet@v3
|
uses: actions/setup-dotnet@v4
|
||||||
|
with:
|
||||||
|
dotnet-version: '10.x.x'
|
||||||
|
|
||||||
# https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools
|
# https://learn.microsoft.com/en-us/dotnet/core/tools/global-tools
|
||||||
- name: Restore .NET Tools
|
- name: Restore .NET Tools
|
||||||
|
|||||||
18
.github/workflows/ci.yml
vendored
18
.github/workflows/ci.yml
vendored
@ -18,9 +18,9 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Build and Test Flight
|
- name: Build and Test Flight Microservice
|
||||||
uses: ./.github/actions/build-test
|
uses: ./.github/actions/build-test
|
||||||
if: success()
|
if: success()
|
||||||
id: build-test-flight-step
|
id: build-test-flight-step
|
||||||
@ -35,7 +35,7 @@ jobs:
|
|||||||
service-name: 'Flight'
|
service-name: 'Flight'
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and Test Identity
|
- name: Build and Test Identity Microservice
|
||||||
uses: ./.github/actions/build-test
|
uses: ./.github/actions/build-test
|
||||||
if: success()
|
if: success()
|
||||||
id: build-test-identity-step
|
id: build-test-identity-step
|
||||||
@ -50,7 +50,7 @@ jobs:
|
|||||||
service-name: 'Identity'
|
service-name: 'Identity'
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and Test Passenger
|
- name: Build and Test Passenger Microservice
|
||||||
uses: ./.github/actions/build-test
|
uses: ./.github/actions/build-test
|
||||||
if: success()
|
if: success()
|
||||||
id: build-test-passenger-step
|
id: build-test-passenger-step
|
||||||
@ -65,7 +65,7 @@ jobs:
|
|||||||
service-name: 'Passenger'
|
service-name: 'Passenger'
|
||||||
token: ${{ secrets.GITHUB_TOKEN }}
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Build and Test Booking
|
- name: Build and Test Booking Microservice
|
||||||
uses: ./.github/actions/build-test
|
uses: ./.github/actions/build-test
|
||||||
if: success()
|
if: success()
|
||||||
id: build-test-booking-step
|
id: build-test-booking-step
|
||||||
@ -91,7 +91,7 @@ jobs:
|
|||||||
run:
|
run:
|
||||||
echo "Release version is:" ${{ steps.last_release.outputs.tag_name }}
|
echo "Release version is:" ${{ steps.last_release.outputs.tag_name }}
|
||||||
|
|
||||||
- name: Build and Publish Identity to Docker
|
- name: Build and Publish Identity Microservice to Docker
|
||||||
if: ${{ github.ref == 'refs/heads/main' && success() }}
|
if: ${{ github.ref == 'refs/heads/main' && success() }}
|
||||||
uses: ./.github/actions/docker-build-publish
|
uses: ./.github/actions/docker-build-publish
|
||||||
with:
|
with:
|
||||||
@ -101,7 +101,7 @@ jobs:
|
|||||||
dockerfile-path: 'src/Services/Identity/Dockerfile'
|
dockerfile-path: 'src/Services/Identity/Dockerfile'
|
||||||
image-name: 'booking-microservices-identity'
|
image-name: 'booking-microservices-identity'
|
||||||
|
|
||||||
- name: Build and Publish Flight to Docker
|
- name: Build and Publish Flight Microservice to Docker
|
||||||
if: ${{ github.ref == 'refs/heads/main' && success() }}
|
if: ${{ github.ref == 'refs/heads/main' && success() }}
|
||||||
uses: ./.github/actions/docker-build-publish
|
uses: ./.github/actions/docker-build-publish
|
||||||
with:
|
with:
|
||||||
@ -111,7 +111,7 @@ jobs:
|
|||||||
dockerfile-path: 'src/Services/Flight/Dockerfile'
|
dockerfile-path: 'src/Services/Flight/Dockerfile'
|
||||||
image-name: 'booking-microservices-flight'
|
image-name: 'booking-microservices-flight'
|
||||||
|
|
||||||
- name: Build and Publish Passenger to Docker
|
- name: Build and Publish Passenger Microservice to Docker
|
||||||
if: ${{ github.ref == 'refs/heads/main' && success() }}
|
if: ${{ github.ref == 'refs/heads/main' && success() }}
|
||||||
uses: ./.github/actions/docker-build-publish
|
uses: ./.github/actions/docker-build-publish
|
||||||
with:
|
with:
|
||||||
@ -121,7 +121,7 @@ jobs:
|
|||||||
dockerfile-path: 'src/Services/Passenger/Dockerfile'
|
dockerfile-path: 'src/Services/Passenger/Dockerfile'
|
||||||
image-name: 'booking-microservices-passenger'
|
image-name: 'booking-microservices-passenger'
|
||||||
|
|
||||||
- name: Build and Publish Booking to Docker
|
- name: Build and Publish Booking Microservice to Docker
|
||||||
if: ${{ github.ref == 'refs/heads/main' && success() }}
|
if: ${{ github.ref == 'refs/heads/main' && success() }}
|
||||||
uses: ./.github/actions/docker-build-publish
|
uses: ./.github/actions/docker-build-publish
|
||||||
with:
|
with:
|
||||||
|
|||||||
1
.gitpod.Dockerfile
vendored
1
.gitpod.Dockerfile
vendored
@ -1 +0,0 @@
|
|||||||
FROM gitpod/workspace-dotnet:latest
|
|
||||||
37
.gitpod.yml
37
.gitpod.yml
@ -1,37 +0,0 @@
|
|||||||
# https://github.com/gitpod-samples/template-dotnet-core-cli-csharp
|
|
||||||
# https://www.gitpod.io/docs/introduction/languages/dotnet
|
|
||||||
# https://github.com/gitpod-samples/template-docker-compose
|
|
||||||
# https://www.gitpod.io/docs/references/gitpod-yml
|
|
||||||
# https://www.gitpod.io/docs/configure
|
|
||||||
# https://www.gitpod.io/docs/configure/workspaces/ports
|
|
||||||
|
|
||||||
image:
|
|
||||||
file: .gitpod.Dockerfile
|
|
||||||
|
|
||||||
vscode:
|
|
||||||
extensions:
|
|
||||||
- muhammad-sammy.csharp
|
|
||||||
- editorconfig.editorconfig
|
|
||||||
- vivaxy.vscode-conventional-commits
|
|
||||||
- humao.rest-client
|
|
||||||
- ms-azuretools.vscode-docker
|
|
||||||
- donjayamanne.githistory
|
|
||||||
- pkief.material-icon-theme
|
|
||||||
- emmanuelbeziat.vscode-great-icons
|
|
||||||
|
|
||||||
# https://www.gitpod.io/docs/configure/workspaces/tasks#execution-order
|
|
||||||
# https://www.gitpod.io/docs/configure/projects/prebuilds
|
|
||||||
tasks:
|
|
||||||
- name: Init Docker-Compose
|
|
||||||
# https://www.gitpod.io/docs/configure/projects/prebuilds
|
|
||||||
# We load docker on pre-build for increasing speed
|
|
||||||
init: |
|
|
||||||
docker-compose pull
|
|
||||||
docker-compose -f ./deployments/docker-compose/infrastracture.yaml up -d
|
|
||||||
- name: Setup kubectl
|
|
||||||
command: bash $GITPOD_REPO_ROOT/scripts/setup_kubectl_gitpod.sh
|
|
||||||
- name: Restore & Build
|
|
||||||
init: |
|
|
||||||
dotnet dev-certs https
|
|
||||||
dotnet restore
|
|
||||||
dotnet build
|
|
||||||
@ -1 +1,2 @@
|
|||||||
npm run format
|
npm run format
|
||||||
|
npm run ci-format
|
||||||
|
|||||||
@ -1,34 +1,35 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project>
|
<Project>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<RepositoryRoot>$(MSBuildThisFileDirectory)</RepositoryRoot>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" Version="1.1.118">
|
<PackageReference Include="StyleCop.Analyzers" PrivateAssets="all" Version="1.1.118">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" Version="2.0.163">
|
<PackageReference Include="Meziantou.Analyzer" PrivateAssets="all" Version="2.0.299">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="all" Version="4.12.5">
|
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="all" Version="4.15.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" PrivateAssets="all" Version="4.12.5">
|
<PackageReference Include="Roslynator.CodeAnalysis.Analyzers" PrivateAssets="all" Version="4.15.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Roslynator.Formatting.Analyzers" PrivateAssets="all" Version="4.12.5">
|
<PackageReference Include="Roslynator.Formatting.Analyzers" PrivateAssets="all" Version="4.15.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="all" Version="17.11.20">
|
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" PrivateAssets="all" Version="17.14.15">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="AsyncAwaitBestPractices" PrivateAssets="all" Version="8.0.0">
|
<PackageReference Include="AsyncAwaitBestPractices" PrivateAssets="all" Version="10.0.0">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
<PackageReference Include="SerilogAnalyzer" PrivateAssets="all" Version="0.15.0">
|
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="CSharpGuidelinesAnalyzer" PrivateAssets="all" Version="3.8.5">
|
<PackageReference Include="CSharpGuidelinesAnalyzer" PrivateAssets="all" Version="3.8.5">
|
||||||
|
|||||||
131
README.md
131
README.md
@ -6,13 +6,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
> 🚀 **A practical and imaginary microservices for implementing an infrastructure for up and running distributed system with the latest technology and architecture like Vertical Slice Architecture, Event Sourcing, CQRS, DDD, gRpc, MongoDB, RabbitMq, Masstransit in .Net 8.**
|
> 🚀 **A practical microservices with the latest technologies and architectures like Vertical Slice Architecture, Event Sourcing, CQRS, DDD, gRpc, MongoDB, RabbitMq, Masstransit, and Aspire in .Net 10.**
|
||||||
|
|
||||||
> 💡 **This project is not business-oriented and most of my focus was in the thechnical part for implement a distributed system with a sample project. In this project I implemented some concept in microservices like Messaging, Tracing, Event Driven Architecture, Vertical Slice Architecture, Event Sourcing, CQRS, DDD and gRpc.**
|
## You can find other version of this project here:
|
||||||
|
- [Booking with Modular Monolith Architecture](https://github.com/meysamhadeli/booking-modular-monolith)
|
||||||
|
- [Booking with Monolith Architecture](https://github.com/meysamhadeli/booking-monolith)
|
||||||
|
|
||||||
<div>
|
|
||||||
<a href="https://gitpod.io/#https://github.com/meysamhadeli/booking-microservices"><img alt="Open in Gitpod" src="https://gitpod.io/button/open-in-gitpod.svg"/></a>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<a href='https://codespaces.new/meysamhadeli/booking-microservices?quickstart=1'><img alt='Open in GitHub Codespaces' src='https://github.com/codespaces/badge.svg'></a>
|
<a href='https://codespaces.new/meysamhadeli/booking-microservices?quickstart=1'><img alt='Open in GitHub Codespaces' src='https://github.com/codespaces/badge.svg'></a>
|
||||||
</div>
|
</div>
|
||||||
@ -20,16 +19,19 @@
|
|||||||
# Table of Contents
|
# Table of Contents
|
||||||
|
|
||||||
- [The Goals of This Project](#the-goals-of-this-project)
|
- [The Goals of This Project](#the-goals-of-this-project)
|
||||||
- [Plan](#plan)
|
|
||||||
- [Technologies - Libraries](#technologies---libraries)
|
- [Technologies - Libraries](#technologies---libraries)
|
||||||
|
- [Key Features](#key-features)
|
||||||
|
- [When to Use](#when-to-use)
|
||||||
|
- [Challenges](#challenges)
|
||||||
- [The Domain and Bounded Context - Service Boundary](#the-domain-and-bounded-context---service-boundary)
|
- [The Domain and Bounded Context - Service Boundary](#the-domain-and-bounded-context---service-boundary)
|
||||||
- [Structure of Project](#structure-of-project)
|
- [Structure of Project](#structure-of-project)
|
||||||
- [Development Setup](#development-setup)
|
- [Development Setup](#development-setup)
|
||||||
- [Dotnet Tools Packages](#dotnet-tools-packages)
|
- [Dotnet Tools Packages](#dotnet-tools-packages)
|
||||||
- [Husky](#husky)
|
- [Husky](#husky)
|
||||||
- [Upgrade Nuget Packages](#upgrade-nuget-packages)
|
- [Upgrade Nuget Packages](#upgrade-nuget-packages)
|
||||||
- [How to Run](#how-to-run)
|
- [How to Run](#how-to-run)
|
||||||
- [Config Certificate](#config-certificate)
|
- [Config Certificate](#config-certificate)
|
||||||
|
- [Aspire](#aspire)
|
||||||
- [Docker Compose](#docker-compose)
|
- [Docker Compose](#docker-compose)
|
||||||
- [Kubernetes](#kubernetes)
|
- [Kubernetes](#kubernetes)
|
||||||
- [Build](#build)
|
- [Build](#build)
|
||||||
@ -39,22 +41,24 @@
|
|||||||
- [Support](#support)
|
- [Support](#support)
|
||||||
- [Contribution](#contribution)
|
- [Contribution](#contribution)
|
||||||
|
|
||||||
|
|
||||||
## The Goals of This Project
|
## The Goals of This Project
|
||||||
|
|
||||||
- :sparkle: Using `Vertical Slice Architecture` for `architecture` level.
|
- :sparkle: Using `Vertical Slice Architecture` for `architecture` level.
|
||||||
- :sparkle: Using `Domain Driven Design (DDD)` to implement all `business processes` in microservices.
|
- :sparkle: Using `Domain Driven Design (DDD)` to implement all `business logic`.
|
||||||
- :sparkle: Using `Rabbitmq` on top of `Masstransit` for `Event Driven Architecture` between our microservices.
|
- :sparkle: Using `Rabbitmq` on top of `Masstransit` for `Event Driven Architecture`.
|
||||||
- :sparkle: Using `gRPC` for `internal communication` between our microservices.
|
- :sparkle: Using `gRPC` for `internal communication`.
|
||||||
- :sparkle: Using `CQRS` implementation with `MediatR` library.
|
- :sparkle: Using `CQRS` implementation with `MediatR` library.
|
||||||
- :sparkle: Using `Postgres` for `write side` of some microservices.
|
- :sparkle: Using `Postgres` for `write side` database.
|
||||||
- :sparkle: Using `MongoDB` for `read side` of some microservices.
|
- :sparkle: Using `MongoDB` for `read side` database.
|
||||||
- :sparkle: Using `Event Store` for `write side` of Booking-Microservice to store all `historical state` of aggregate.
|
- :sparkle: Using `Event Store` for `write side` of Booking Microservice/Module to store all `historical change` of aggregate.
|
||||||
- :sparkle: Using `Inbox Pattern` for ensuring message idempotency for receiver and `Exactly once Delivery`.
|
- :sparkle: Using `Inbox Pattern` for ensuring message idempotency for receiver and `Exactly once Delivery`.
|
||||||
- :sparkle: Using `Outbox Pattern` for ensuring no message is lost and there is at `At Least One Delivery`.
|
- :sparkle: Using `Outbox Pattern` for ensuring no message is lost and there is at `At Least One Delivery`.
|
||||||
- :sparkle: Using `Unit Testing` for testing small units and mocking our dependencies with `Nsubstitute`.
|
- :sparkle: Using `Unit Testing` for testing small units and mocking our dependencies with `Nsubstitute`.
|
||||||
- :sparkle: Using `End-To-End Testing` and `Integration Testing` for testing `features` with all dependencies using `testcontainers`.
|
- :sparkle: Using `End-To-End Testing` and `Integration Testing` for testing `features` with all dependencies using `testcontainers`.
|
||||||
- :sparkle: Using `Fluent Validation` and a `Validation Pipeline Behaviour` on top of `MediatR`.
|
- :sparkle: Using `Fluent Validation` and a `Validation Pipeline Behaviour` on top of `MediatR`.
|
||||||
- :sparkle: Using `Minimal API` for all endpoints.
|
- :sparkle: Using `Minimal API` for all endpoints.
|
||||||
|
- :sparkle: Using `AspNetCore OpenApi` for `generating` built-in support `OpenAPI documentation` in ASP.NET Core.
|
||||||
- :sparkle: Using `Health Check` for `reporting` the `health` of app infrastructure components.
|
- :sparkle: Using `Health Check` for `reporting` the `health` of app infrastructure components.
|
||||||
- :sparkle: Using `Docker-Compose` and `Kubernetes` for our deployment mechanism.
|
- :sparkle: Using `Docker-Compose` and `Kubernetes` for our deployment mechanism.
|
||||||
- :sparkle: Using `Kibana` on top of `Serilog` for `logging`.
|
- :sparkle: Using `Kibana` on top of `Serilog` for `logging`.
|
||||||
@ -65,44 +69,31 @@
|
|||||||
- :sparkle: Using `Kubernetes` to achieve efficient `scaling` and ensure `high availability` for each of our microservices.
|
- :sparkle: Using `Kubernetes` to achieve efficient `scaling` and ensure `high availability` for each of our microservices.
|
||||||
- :sparkle: Using `Nginx Ingress Controller` for `load balancing` between our microservices top of `Kubernetes`.
|
- :sparkle: Using `Nginx Ingress Controller` for `load balancing` between our microservices top of `Kubernetes`.
|
||||||
- :sparkle: Using `cert-manager` to Configure `TLS` in `kubernetes cluster`.
|
- :sparkle: Using `cert-manager` to Configure `TLS` in `kubernetes cluster`.
|
||||||
|
- :sparkle: Using `Aspire` for `service discovery`, `observability`, and `local orchestration` of microservices.
|
||||||
|
|
||||||
## Plan
|
|
||||||
|
|
||||||
> 🌀This project is a work in progress, new features will be added over time.🌀
|
|
||||||
|
|
||||||
I will try to register future goals and additions in the [Issues](https://github.com/meysamhadeli/booking-microservices/issues) section of this repository.
|
|
||||||
|
|
||||||
High-level plan is represented in the table
|
|
||||||
|
|
||||||
| Feature | Status |
|
|
||||||
| ----------------- | -------------- |
|
|
||||||
| API Gateway | Completed ✔️ |
|
|
||||||
| Identity Service | Completed ✔️ |
|
|
||||||
| Flight Service | Completed ✔️ |
|
|
||||||
| Passenger Service | Completed ✔️ |
|
|
||||||
| Booking Service | Completed ✔️ |
|
|
||||||
| Building Blocks | Completed ✔️ |
|
|
||||||
|
|
||||||
## Technologies - Libraries
|
## Technologies - Libraries
|
||||||
|
|
||||||
- ✔️ **[`.NET 7`](https://dotnet.microsoft.com/download)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core
|
- ✔️ **[`.NET 10`](https://github.com/dotnet/aspnetcore)** - .NET Framework and .NET Core, including ASP.NET and ASP.NET Core.
|
||||||
- ✔️ **[`MVC Versioning API`](https://github.com/microsoft/aspnet-api-versioning)** - Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core
|
- ✔️ **[`MVC Versioning API`](https://github.com/microsoft/aspnet-api-versioning)** - Set of libraries which add service API versioning to ASP.NET Web API, OData with ASP.NET Web API, and ASP.NET Core.
|
||||||
- ✔️ **[`EF Core`](https://github.com/dotnet/efcore)** - Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations
|
- ✔️ **[`EF Core`](https://github.com/dotnet/efcore)** - Modern object-database mapper for .NET. It supports LINQ queries, change tracking, updates, and schema migrations.
|
||||||
|
- ✔️ **[`AspNetCore OpenApi`](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/aspnetcore-openapi)** - Provides built-in support for OpenAPI document generation in ASP.NET Core.
|
||||||
- ✔️ **[`Masstransit`](https://github.com/MassTransit/MassTransit)** - Distributed Application Framework for .NET.
|
- ✔️ **[`Masstransit`](https://github.com/MassTransit/MassTransit)** - Distributed Application Framework for .NET.
|
||||||
- ✔️ **[`MediatR`](https://github.com/jbogard/MediatR)** - Simple, unambitious mediator implementation in .NET.
|
- ✔️ **[`MediatR`](https://github.com/jbogard/MediatR)** - Simple, unambitious mediator implementation in .NET.
|
||||||
- ✔️ **[`FluentValidation`](https://github.com/FluentValidation/FluentValidation)** - Popular .NET validation library for building strongly-typed validation rules
|
- ✔️ **[`FluentValidation`](https://github.com/FluentValidation/FluentValidation)** - Popular .NET validation library for building strongly-typed validation rules.
|
||||||
- ✔️ **[`Swagger & Swagger UI`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)** - Swagger tools for documenting API's built on ASP.NET Core
|
- ✔️ **[`Scalar`](https://github.com/scalar/scalar/tree/main/packages/scalar.aspnetcore)** - Scalar provides an easy way to render beautiful API references based on OpenAPI/Swagger documents.
|
||||||
|
- ✔️ **[`Swagger UI`](https://github.com/domaindrivendev/Swashbuckle.AspNetCore)** - Swagger tools for documenting API's built on ASP.NET Core.
|
||||||
- ✔️ **[`Serilog`](https://github.com/serilog/serilog)** - Simple .NET logging with fully-structured events
|
- ✔️ **[`Serilog`](https://github.com/serilog/serilog)** - Simple .NET logging with fully-structured events
|
||||||
- ✔️ **[`Polly`](https://github.com/App-vNext/Polly)** - Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner
|
- ✔️ **[`Polly`](https://github.com/App-vNext/Polly)** - Polly is a .NET resilience and transient-fault-handling library that allows developers to express policies such as Retry, Circuit Breaker, Timeout, Bulkhead Isolation, and Fallback in a fluent and thread-safe manner.
|
||||||
- ✔️ **[`Scrutor`](https://github.com/khellang/Scrutor)** - Assembly scanning and decoration extensions for Microsoft.Extensions.DependencyInjection
|
- ✔️ **[`Scrutor`](https://github.com/khellang/Scrutor)** - Assembly scanning and decoration extensions for Microsoft.Extensions.DependencyInjection
|
||||||
- ✔️ **[`Opentelemetry-dotnet`](https://github.com/open-telemetry/opentelemetry-dotnet)** - The OpenTelemetry .NET Client
|
- ✔️ **[`Opentelemetry-dotnet`](https://github.com/open-telemetry/opentelemetry-dotnet)** - The OpenTelemetry .NET Client
|
||||||
- ✔️ **[`DuendeSoftware IdentityServer`](https://github.com/DuendeSoftware/IdentityServer)** - The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core
|
- ✔️ **[`DuendeSoftware IdentityServer`](https://github.com/DuendeSoftware/IdentityServer)** - The most flexible and standards-compliant OpenID Connect and OAuth 2.x framework for ASP.NET Core.
|
||||||
- ✔️ **[`EasyCaching`](https://github.com/dotnetcore/EasyCaching)** - Open source caching library that contains basic usages and some advanced usages of caching which can help us to handle caching more easier.
|
- ✔️ **[`EasyCaching`](https://github.com/dotnetcore/EasyCaching)** - Open source caching library that contains basic usages and some advanced usages of caching which can help us to handle caching more easier.
|
||||||
- ✔️ **[`Mapster`](https://github.com/MapsterMapper/Mapster)** - Convention-based object-object mapper in .NET.
|
- ✔️ **[`Mapster`](https://github.com/MapsterMapper/Mapster)** - Convention-based object-object mapper in .NET.
|
||||||
- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core
|
- ✔️ **[`Hellang.Middleware.ProblemDetails`](https://github.com/khellang/Middleware/tree/master/src/ProblemDetails)** - A middleware for handling exception in .Net Core.
|
||||||
- ✔️ **[`NewId`](https://github.com/phatboyg/NewId)** - NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs
|
- ✔️ **[`NewId`](https://github.com/phatboyg/NewId)** - NewId can be used as an embedded unique ID generator that produces 128 bit (16 bytes) sequential IDs.
|
||||||
- ✔️ **[`Yarp`](https://github.com/microsoft/reverse-proxy)** - Reverse proxy toolkit for building fast proxy servers in .NET
|
- ✔️ **[`Yarp`](https://github.com/microsoft/reverse-proxy)** - Reverse proxy toolkit for building fast proxy servers in .NET.
|
||||||
- ✔️ **[`Tye`](https://github.com/dotnet/tye)** - Developer tool that makes developing, testing, and deploying microservices and distributed applications easier
|
- ✔️ **[`Tye`](https://github.com/dotnet/tye)** - Developer tool that makes developing, testing, and deploying microservices and distributed applications easier.
|
||||||
- ✔️ **[`gRPC-dotnet`](https://github.com/grpc/grpc-dotnet)** - gRPC functionality for .NET.
|
- ✔️ **[`gRPC-dotnet`](https://github.com/grpc/grpc-dotnet)** - gRPC functionality for .NET.
|
||||||
- ✔️ **[`EventStore`](https://github.com/EventStore/EventStore)** - The open-source, functional database with Complex Event Processing.
|
- ✔️ **[`EventStore`](https://github.com/EventStore/EventStore)** - The open-source, functional database with Complex Event Processing.
|
||||||
- ✔️ **[`MongoDB.Driver`](https://github.com/mongodb/mongo-csharp-driver)** - .NET Driver for MongoDB.
|
- ✔️ **[`MongoDB.Driver`](https://github.com/mongodb/mongo-csharp-driver)** - .NET Driver for MongoDB.
|
||||||
@ -110,6 +101,29 @@ High-level plan is represented in the table
|
|||||||
- ✔️ **[`Respawn`](https://github.com/jbogard/Respawn)** - Respawn is a small utility to help in resetting test databases to a clean state.
|
- ✔️ **[`Respawn`](https://github.com/jbogard/Respawn)** - Respawn is a small utility to help in resetting test databases to a clean state.
|
||||||
- ✔️ **[`Testcontainers`](https://github.com/testcontainers/testcontainers-dotnet)** - Testcontainers for .NET is a library to support tests with throwaway instances of Docker containers.
|
- ✔️ **[`Testcontainers`](https://github.com/testcontainers/testcontainers-dotnet)** - Testcontainers for .NET is a library to support tests with throwaway instances of Docker containers.
|
||||||
- ✔️ **[`K6`](https://github.com/grafana/k6)** - Modern load testing for developers and testers in the DevOps era.
|
- ✔️ **[`K6`](https://github.com/grafana/k6)** - Modern load testing for developers and testers in the DevOps era.
|
||||||
|
- ✔️ **[`Aspire`](https://github.com/dotnet/aspire)** - .NET stack for building and orchestrating observable, distributed cloud-native applications.
|
||||||
|
|
||||||
|
|
||||||
|
## Key Features
|
||||||
|
1. **Independent Services**: Each service is a separate project with its own database and deployment pipeline, enabling independent development and deployment.
|
||||||
|
2. **Decentralized Communication**: Services communicate via APIs (REST, gRPC) or message brokers (RabbitMQ, Kafka), ensuring loose coupling and resilience.
|
||||||
|
3. **Scalability**: Services can be scaled independently based on demand, allowing efficient resource utilization.
|
||||||
|
4. **Fault Tolerance**: Failures are isolated, preventing cascading failures and ensuring high availability.
|
||||||
|
5. **Technology Agnostic**: Services can use different technologies, frameworks, or databases, providing flexibility.
|
||||||
|
|
||||||
|
|
||||||
|
## When to Use
|
||||||
|
1. **Large and Complex Projects**: Ideal for applications with complex business logic that can be broken into smaller, manageable services.
|
||||||
|
2. **High Scalability Needs**: Suitable for applications requiring independent scaling of components.
|
||||||
|
3. **Fault Tolerance and High Availability**: Perfect for systems where failure isolation and uptime are critical.
|
||||||
|
4. **Distributed Teams**: Enables teams to work independently on different services.
|
||||||
|
5. **Frequent Updates**: Supports continuous deployment and A/B testing for individual services.
|
||||||
|
6. **Technology Diversity**: Allows the use of different technologies for different services.
|
||||||
|
|
||||||
|
|
||||||
|
## Challenges
|
||||||
|
- Increased complexity in management, DevOps overhead, data consistency, latency, and higher costs.
|
||||||
|
|
||||||
|
|
||||||
## The Domain And Bounded Context - Service Boundary
|
## The Domain And Bounded Context - Service Boundary
|
||||||
|
|
||||||
@ -123,19 +137,10 @@ High-level plan is represented in the table
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Structure of Project
|
## Structure of Project
|
||||||
|
|
||||||
In this project I used a mix of [clean architecture](https://jasontaylor.dev/clean-architecture-getting-started/), [vertical slice architecture](https://jimmybogard.com/vertical-slice-architecture/) and I used [feature folder structure](http://www.kamilgrzybek.com/design/feature-folders/) to structure my files.
|
In this project, I used [vertical slice architecture](https://jimmybogard.com/vertical-slice-architecture/) at the architectural level and [feature folder structure](http://www.kamilgrzybek.com/design/feature-folders/) to structure my files.
|
||||||
|
|
||||||
I used [yarp reverse proxy](https://microsoft.github.io/reverse-proxy/articles/index.html) to route synchronous and asynchronous requests to the corresponding microservice. Each microservice has its dependencies such as databases, files etc. Each microservice is decoupled from other microservices and developed and deployed separately. Microservices talk to each other with Rest or gRPC for synchronous calls and use RabbitMq or Kafka for asynchronous calls.
|
|
||||||
|
|
||||||
We have a separate microservice ([IdentityServer](https://github.com/DuendeSoftware/IdentityServer)) for authentication and authorization of each request. Once signed-in users are issued a JWT token. This token is used by other microservices to validate the user, read claims and allow access to authorized/role specific endpoints.
|
|
||||||
|
|
||||||
I used [RabbitMQ](https://github.com/rabbitmq) as my MessageBroker for async communication between microservices using the eventual consistency mechanism. Each microservice uses [MassTransit](https://github.com/MassTransit/MassTransit) to interface with [RabbitMQ](https://github.com/rabbitmq) providing, messaging, availability, reliability, etc.
|
|
||||||
|
|
||||||
Microservices are `event based` which means they can publish and/or subscribe to any events occurring in the setup. By using this approach for communicating between services, each microservice does not need to know about the other services or handle errors occurred in other microservices.
|
|
||||||
|
|
||||||
After saving data in write side, I save a [Internal Command](https://github.com/kgrzybek/modular-monolith-with-ddd#38-internal-processing) record in my Persist Messages storage (like something we do in outbox pattern) and after committing transaction in write side, trigger our command handler in read side and this handler could save their read models in our MongoDB database.
|
|
||||||
|
|
||||||
I treat each request as a distinct use case or slice, encapsulating and grouping all concerns from front-end to back.
|
I treat each request as a distinct use case or slice, encapsulating and grouping all concerns from front-end to back.
|
||||||
When adding or changing a feature in an application in n-tire architecture, we are typically touching many "layers" in an application. We are changing the user interface, adding fields to models, modifying validation, and so on. Instead of coupling across a layer, we couple vertically along a slice. We `minimize coupling` `between slices`, and `maximize coupling` `in a slice`.
|
When adding or changing a feature in an application in n-tire architecture, we are typically touching many "layers" in an application. We are changing the user interface, adding fields to models, modifying validation, and so on. Instead of coupling across a layer, we couple vertically along a slice. We `minimize coupling` `between slices`, and `maximize coupling` `in a slice`.
|
||||||
@ -158,6 +163,7 @@ I used CQRS to decompose my features into small parts that makes our application
|
|||||||
|
|
||||||
Using the CQRS pattern, we cut each business functionality into vertical slices, for each of these slices we group classes (see [technical folders structure](http://www.kamilgrzybek.com/design/feature-folders)) specific to that feature together (command, handlers, infrastructure, repository, controllers, etc). In our CQRS pattern each command/query handler is a separate slice. This is where you can reduce coupling between layers. Each handler can be a separated code unit, even copy/pasted. Thanks to that, we can tune down the specific method to not follow general conventions (e.g. use custom SQL query or even different storage). In a traditional layered architecture, when we change the core generic mechanism in one layer, it can impact all methods.
|
Using the CQRS pattern, we cut each business functionality into vertical slices, for each of these slices we group classes (see [technical folders structure](http://www.kamilgrzybek.com/design/feature-folders)) specific to that feature together (command, handlers, infrastructure, repository, controllers, etc). In our CQRS pattern each command/query handler is a separate slice. This is where you can reduce coupling between layers. Each handler can be a separated code unit, even copy/pasted. Thanks to that, we can tune down the specific method to not follow general conventions (e.g. use custom SQL query or even different storage). In a traditional layered architecture, when we change the core generic mechanism in one layer, it can impact all methods.
|
||||||
|
|
||||||
|
|
||||||
## Development Setup
|
## Development Setup
|
||||||
|
|
||||||
### Dotnet Tools Packages
|
### Dotnet Tools Packages
|
||||||
@ -171,8 +177,8 @@ dotnet tool restore
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Husky
|
### Husky
|
||||||
Here we use `husky` to handel some pre commit rules and we used `conventional commits` rules and `formatting` as pre commit rules, here in [package.json](./package.json). of course, we can add more rules for pre commit in future. (find more about husky in the [documentation](https://typicode.github.io/husky/get-started.html))
|
Here we use `husky` to handel some pre commit rules and we used `conventional commits` rules and `formatting` as pre commit rules, here in [package.json](.././package.json). of course, we can add more rules for pre commit in future. (find more about husky in the [documentation](https://typicode.github.io/husky/get-started.html))
|
||||||
We need to install `husky` package for `manage` `pre commits hooks` and also I add two packages `@commitlint/cli` and `@commitlint/config-conventional` for handling conventional commits rules in [package.json](./package.json).
|
We need to install `husky` package for `manage` `pre commits hooks` and also I add two packages `@commitlint/cli` and `@commitlint/config-conventional` for handling conventional commits rules in [package.json](.././package.json).
|
||||||
Run the command bellow in the root of project to install all npm dependencies related to husky:
|
Run the command bellow in the root of project to install all npm dependencies related to husky:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@ -198,13 +204,24 @@ Run the following commands to [Config SSL](https://docs.microsoft.com/en-us/aspn
|
|||||||
dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\aspnetapp.pfx -p password
|
dotnet dev-certs https -ep %USERPROFILE%\.aspnet\https\aspnetapp.pfx -p password
|
||||||
dotnet dev-certs https --trust
|
dotnet dev-certs https --trust
|
||||||
```
|
```
|
||||||
***Note:** for running this command in `powershell` use `$env:USERPROFILE` instead of `%USERPROFILE%`*
|
> Note: for running this command in `powershell` use `$env:USERPROFILE` instead of `%USERPROFILE%`*
|
||||||
|
|
||||||
#### macOS or Linux
|
#### macOS or Linux
|
||||||
```bash
|
```bash
|
||||||
dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p $CREDENTIAL_PLACEHOLDER$
|
dotnet dev-certs https -ep ${HOME}/.aspnet/https/aspnetapp.pfx -p $CREDENTIAL_PLACEHOLDER$
|
||||||
dotnet dev-certs https --trust
|
dotnet dev-certs https --trust
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Aspire
|
||||||
|
|
||||||
|
To run the application using the `Aspire App Host`, execute the following command from the solution root:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aspire run
|
||||||
|
```
|
||||||
|
|
||||||
|
> Note:The `Aspire dashboard` will be available at `http://localhost:18888`
|
||||||
|
|
||||||
> ### Docker Compose
|
> ### Docker Compose
|
||||||
|
|
||||||
|
|
||||||
@ -248,7 +265,7 @@ dotnet test
|
|||||||
|
|
||||||
> ### Documentation Apis
|
> ### Documentation Apis
|
||||||
|
|
||||||
Each microservice has a `Swagger OpenAPI`. Browse to `/swagger` for a list of endpoints.
|
Each microservice provides `API documentation` and navigate to `/swagger` for `Swagger OpenAPI` or `/scalar/v1` for `Scalar OpenAPI` to visit list of endpoints.
|
||||||
|
|
||||||
As part of API testing, I created the [booking.rest](./booking.rest) file which can be run with the [REST Client](https://github.com/Huachao/vscode-restclient) `VSCode plugin`.
|
As part of API testing, I created the [booking.rest](./booking.rest) file which can be run with the [REST Client](https://github.com/Huachao/vscode-restclient) `VSCode plugin`.
|
||||||
|
|
||||||
|
|||||||
471
assets/booking-microservices.drawio
Normal file
471
assets/booking-microservices.drawio
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 643 KiB |
@ -1,175 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio Version 16
|
|
||||||
VisualStudioVersion = 16.0.30114.105
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{5B69EDFD-4B09-457A-AAAF-D816D402D595}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{9010E0B5-9C42-4256-ADE4-E290434F2CEF}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiGateway", "ApiGateway", "{3E38DD17-9EEE-4815-9D5B-BEB5549020A0}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{776BDF43-0DEA-44A3-AF72-99408CE544EE}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiGateway", "src\ApiGateway\src\ApiGateway.csproj", "{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildingBlocks", "src\BuildingBlocks\BuildingBlocks.csproj", "{E42BB533-4144-4D78-BCCE-50BA00BCADBE}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Flight", "Flight", "{5F0996AB-F8DB-4240-BD4A-DFDD70638A73}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Passenger", "Passenger", "{1A2ABCD9-493B-4848-9C69-919CDBCA61F3}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Booking", "Booking", "{22447274-717D-4321-87F3-868BAF93CBEC}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Identity", "Identity", "{55BE6759-95AA-434D-925D-A8D32F274E66}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E2637D6D-04A5-4DE4-8AAF-E015C65DE8E1}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{5185D5C5-0EAD-49D5-B405-93B939F3639B}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{53D0AA09-F5FA-4721-8C1B-375CBD15B4E8}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C6EE337B-91EA-472A-87C7-E9528408CE59}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F39D8F09-6233-4495-ACD0-F98904993B7E}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{295284BA-D4E4-40AA-A2C2-BE36343F7DE6}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{85DA00E5-CC11-463C-8577-C34967C328F7}"
|
|
||||||
EndProject
|
|
||||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{C1EBE17D-BFAD-47DA-88EB-BB073B84593E}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Booking", "src\Services\Booking\src\Booking\Booking.csproj", "{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Booking.Api", "src\Services\Booking\src\Booking.Api\Booking.Api.csproj", "{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flight", "src\Services\Flight\src\Flight\Flight.csproj", "{574222F8-9C26-4015-8F35-C1E5D41A505F}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flight.Api", "src\Services\Flight\src\Flight.Api\Flight.Api.csproj", "{B8F734F5-873C-4367-9EBD-38EA420CD868}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity", "src\Services\Identity\src\Identity\Identity.csproj", "{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity.Api", "src\Services\Identity\src\Identity.Api\Identity.Api.csproj", "{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Passenger", "src\Services\Passenger\src\Passenger\Passenger.csproj", "{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Passenger.Api", "src\Services\Passenger\src\Passenger.Api\Passenger.Api.csproj", "{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Flight\tests\IntegrationTest\Integration.Test.csproj", "{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Identity\tests\IntegrationTest\Integration.Test.csproj", "{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Passenger\tests\IntegrationTest\Integration.Test.csproj", "{539364C8-88B1-48A3-8406-D0B19FF30509}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Booking\tests\IntegrationTest\Integration.Test.csproj", "{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unit.Test", "src\Services\Flight\tests\UnitTest\Unit.Test.csproj", "{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}"
|
|
||||||
EndProject
|
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EndToEnd.Test", "src\Services\Flight\tests\EndToEndTest\EndToEnd.Test.csproj", "{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|Any CPU = Debug|Any CPU
|
|
||||||
Release|Any CPU = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(NestedProjects) = preSolution
|
|
||||||
{776BDF43-0DEA-44A3-AF72-99408CE544EE} = {3E38DD17-9EEE-4815-9D5B-BEB5549020A0}
|
|
||||||
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5} = {776BDF43-0DEA-44A3-AF72-99408CE544EE}
|
|
||||||
{E42BB533-4144-4D78-BCCE-50BA00BCADBE} = {5B69EDFD-4B09-457A-AAAF-D816D402D595}
|
|
||||||
{5F0996AB-F8DB-4240-BD4A-DFDD70638A73} = {9010E0B5-9C42-4256-ADE4-E290434F2CEF}
|
|
||||||
{1A2ABCD9-493B-4848-9C69-919CDBCA61F3} = {9010E0B5-9C42-4256-ADE4-E290434F2CEF}
|
|
||||||
{22447274-717D-4321-87F3-868BAF93CBEC} = {9010E0B5-9C42-4256-ADE4-E290434F2CEF}
|
|
||||||
{55BE6759-95AA-434D-925D-A8D32F274E66} = {9010E0B5-9C42-4256-ADE4-E290434F2CEF}
|
|
||||||
{E2637D6D-04A5-4DE4-8AAF-E015C65DE8E1} = {22447274-717D-4321-87F3-868BAF93CBEC}
|
|
||||||
{5185D5C5-0EAD-49D5-B405-93B939F3639B} = {22447274-717D-4321-87F3-868BAF93CBEC}
|
|
||||||
{53D0AA09-F5FA-4721-8C1B-375CBD15B4E8} = {5F0996AB-F8DB-4240-BD4A-DFDD70638A73}
|
|
||||||
{C6EE337B-91EA-472A-87C7-E9528408CE59} = {5F0996AB-F8DB-4240-BD4A-DFDD70638A73}
|
|
||||||
{F39D8F09-6233-4495-ACD0-F98904993B7E} = {55BE6759-95AA-434D-925D-A8D32F274E66}
|
|
||||||
{295284BA-D4E4-40AA-A2C2-BE36343F7DE6} = {55BE6759-95AA-434D-925D-A8D32F274E66}
|
|
||||||
{85DA00E5-CC11-463C-8577-C34967C328F7} = {1A2ABCD9-493B-4848-9C69-919CDBCA61F3}
|
|
||||||
{C1EBE17D-BFAD-47DA-88EB-BB073B84593E} = {1A2ABCD9-493B-4848-9C69-919CDBCA61F3}
|
|
||||||
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136} = {E2637D6D-04A5-4DE4-8AAF-E015C65DE8E1}
|
|
||||||
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C} = {E2637D6D-04A5-4DE4-8AAF-E015C65DE8E1}
|
|
||||||
{574222F8-9C26-4015-8F35-C1E5D41A505F} = {53D0AA09-F5FA-4721-8C1B-375CBD15B4E8}
|
|
||||||
{B8F734F5-873C-4367-9EBD-38EA420CD868} = {53D0AA09-F5FA-4721-8C1B-375CBD15B4E8}
|
|
||||||
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4} = {F39D8F09-6233-4495-ACD0-F98904993B7E}
|
|
||||||
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA} = {F39D8F09-6233-4495-ACD0-F98904993B7E}
|
|
||||||
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7} = {85DA00E5-CC11-463C-8577-C34967C328F7}
|
|
||||||
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837} = {85DA00E5-CC11-463C-8577-C34967C328F7}
|
|
||||||
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2} = {C6EE337B-91EA-472A-87C7-E9528408CE59}
|
|
||||||
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2} = {295284BA-D4E4-40AA-A2C2-BE36343F7DE6}
|
|
||||||
{539364C8-88B1-48A3-8406-D0B19FF30509} = {C1EBE17D-BFAD-47DA-88EB-BB073B84593E}
|
|
||||||
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC} = {5185D5C5-0EAD-49D5-B405-93B939F3639B}
|
|
||||||
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8} = {C6EE337B-91EA-472A-87C7-E9528408CE59}
|
|
||||||
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB} = {C6EE337B-91EA-472A-87C7-E9528408CE59}
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{A2D7C5C4-5148-4C3E-BB12-B7A197A290F5}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{E42BB533-4144-4D78-BCCE-50BA00BCADBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{E42BB533-4144-4D78-BCCE-50BA00BCADBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{E42BB533-4144-4D78-BCCE-50BA00BCADBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{E42BB533-4144-4D78-BCCE-50BA00BCADBE}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B2BAA061-C005-409F-9D3E-BDCBE5B1B136}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{4E8FB852-4317-43D2-8EFC-14E3ECCFDA2C}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{574222F8-9C26-4015-8F35-C1E5D41A505F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{574222F8-9C26-4015-8F35-C1E5D41A505F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{574222F8-9C26-4015-8F35-C1E5D41A505F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{574222F8-9C26-4015-8F35-C1E5D41A505F}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{B8F734F5-873C-4367-9EBD-38EA420CD868}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{B8F734F5-873C-4367-9EBD-38EA420CD868}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{B8F734F5-873C-4367-9EBD-38EA420CD868}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{B8F734F5-873C-4367-9EBD-38EA420CD868}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{65C1BB58-2A2E-44FF-B15D-2B023CF088D4}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{BEE7A9D7-1BFC-477E-B070-4BE63C0361AA}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{6D7BCECE-D77D-4C57-A296-CA6E728E94B7}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{4F29C4B6-A7DA-4A92-9CDB-42FE98238837}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{6B6603C8-D8B6-4775-9C7A-FFE6058070C2}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{BC7871B8-BB18-4BCC-96A8-7324C11BF4A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{539364C8-88B1-48A3-8406-D0B19FF30509}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{539364C8-88B1-48A3-8406-D0B19FF30509}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{539364C8-88B1-48A3-8406-D0B19FF30509}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{539364C8-88B1-48A3-8406-D0B19FF30509}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{50C66B53-ACA0-4AFF-8C5C-834D4EDA8FAC}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{8F78BCE2-C705-4357-A6B9-1B83B55ABBE8}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{8561089E-9FB9-4ACD-A1F5-EAAF213E1DDB}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
348
booking-microservices.sln
Normal file
348
booking-microservices.sln
Normal file
@ -0,0 +1,348 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{CD4A4407-C3B0-422D-BB8C-2A810CED9938}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ApiGateway", "ApiGateway", "{CDFA86FA-BBBA-4A5B-A833-3BE219E373E5}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Services", "Services", "{B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BuildingBlocks", "BuildingBlocks", "{C734CEF7-A2AC-3076-84D8-694B7490AA9D}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Flight", "Flight", "{A3579DE0-F7C5-67E8-3CF8-3AC89B64E059}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Booking", "Booking", "{C6034A5C-F49A-5FA4-86A6-65B2CB19613F}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Passenger", "Passenger", "{9D4F3958-FE6E-C048-E6F9-6F53D8AF03CA}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Identity", "Identity", "{B465D535-05D9-3A0A-08BF-35A1C18CEC46}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A2834164-BF04-BF13-ADC5-A97145852861}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{1B4FBE3A-43F5-1B1E-2877-3036AC5431EF}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DDEDC5E0-5D13-A45C-2393-A774DD4A1A07}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{51D8F471-B8EB-AD1C-0E89-AA84C5D0C759}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{773BFBD8-04CD-79F8-8301-C81308C3ED45}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{4B043475-1AFA-C467-FE09-A46D09CD6936}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5CED3889-AECF-A6CD-55DC-F680D3C18861}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{54BCCDE8-25E6-6FCB-4A9E-D5D2AF76D352}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Booking.Api", "src\Services\Booking\src\Booking.Api\Booking.Api.csproj", "{D3BF565A-C413-4185-9528-BE1B4F46993C}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Booking", "src\Services\Booking\src\Booking\Booking.csproj", "{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flight", "src\Services\Flight\src\Flight\Flight.csproj", "{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flight.Api", "src\Services\Flight\src\Flight.Api\Flight.Api.csproj", "{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity", "src\Services\Identity\src\Identity\Identity.csproj", "{BCDEAB10-6373-46E7-B408-846A3B0B508B}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Identity.Api", "src\Services\Identity\src\Identity.Api\Identity.Api.csproj", "{B0EC74C5-9B2D-492C-ABAE-3E868397B122}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Passenger", "src\Services\Passenger\src\Passenger\Passenger.csproj", "{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Passenger.Api", "src\Services\Passenger\src\Passenger.Api\Passenger.Api.csproj", "{101FFD12-17A4-4615-9438-F347BBF4CC85}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BuildingBlocks", "src\BuildingBlocks\BuildingBlocks.csproj", "{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{427BE8BE-DA7B-FC74-412B-547671E05463}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ApiGateway", "src\ApiGateway\src\ApiGateway.csproj", "{C015BF35-6977-407B-8948-636A9C81C5BE}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Booking\tests\IntegrationTest\Integration.Test.csproj", "{19A89F36-FD3A-448D-90D1-04A1B67BB255}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EndToEnd.Test", "src\Services\Flight\tests\EndToEndTest\EndToEnd.Test.csproj", "{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Flight\tests\IntegrationTest\Integration.Test.csproj", "{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Unit.Test", "src\Services\Flight\tests\UnitTest\Unit.Test.csproj", "{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Identity\tests\IntegrationTest\Integration.Test.csproj", "{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Integration.Test", "src\Services\Passenger\tests\IntegrationTest\Integration.Test.csproj", "{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Aspire", "Aspire", "{D1B6353A-63F5-4DD9-90E6-42B2CFDF1DEA}"
|
||||||
|
EndProject
|
||||||
|
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{C4287034-6833-4505-A6EB-704A86392ECB}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppHost", "src\Aspire\src\AppHost\AppHost.csproj", "{490BCB11-314C-473C-9B85-A32164783507}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceDefaults", "src\Aspire\src\ServiceDefaults\ServiceDefaults.csproj", "{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Debug|x64 = Debug|x64
|
||||||
|
Debug|x86 = Debug|x86
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
Release|x64 = Release|x64
|
||||||
|
Release|x86 = Release|x86
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(NestedProjects) = preSolution
|
||||||
|
{CDFA86FA-BBBA-4A5B-A833-3BE219E373E5} = {CD4A4407-C3B0-422D-BB8C-2A810CED9938}
|
||||||
|
{B19FD14B-4DFE-26B6-646B-3D5D94CC4D36} = {CD4A4407-C3B0-422D-BB8C-2A810CED9938}
|
||||||
|
{C734CEF7-A2AC-3076-84D8-694B7490AA9D} = {CD4A4407-C3B0-422D-BB8C-2A810CED9938}
|
||||||
|
{A3579DE0-F7C5-67E8-3CF8-3AC89B64E059} = {B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}
|
||||||
|
{C6034A5C-F49A-5FA4-86A6-65B2CB19613F} = {B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}
|
||||||
|
{9D4F3958-FE6E-C048-E6F9-6F53D8AF03CA} = {B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}
|
||||||
|
{B465D535-05D9-3A0A-08BF-35A1C18CEC46} = {B19FD14B-4DFE-26B6-646B-3D5D94CC4D36}
|
||||||
|
{A2834164-BF04-BF13-ADC5-A97145852861} = {C6034A5C-F49A-5FA4-86A6-65B2CB19613F}
|
||||||
|
{1B4FBE3A-43F5-1B1E-2877-3036AC5431EF} = {C6034A5C-F49A-5FA4-86A6-65B2CB19613F}
|
||||||
|
{DDEDC5E0-5D13-A45C-2393-A774DD4A1A07} = {A3579DE0-F7C5-67E8-3CF8-3AC89B64E059}
|
||||||
|
{51D8F471-B8EB-AD1C-0E89-AA84C5D0C759} = {A3579DE0-F7C5-67E8-3CF8-3AC89B64E059}
|
||||||
|
{773BFBD8-04CD-79F8-8301-C81308C3ED45} = {B465D535-05D9-3A0A-08BF-35A1C18CEC46}
|
||||||
|
{4B043475-1AFA-C467-FE09-A46D09CD6936} = {B465D535-05D9-3A0A-08BF-35A1C18CEC46}
|
||||||
|
{5CED3889-AECF-A6CD-55DC-F680D3C18861} = {9D4F3958-FE6E-C048-E6F9-6F53D8AF03CA}
|
||||||
|
{54BCCDE8-25E6-6FCB-4A9E-D5D2AF76D352} = {9D4F3958-FE6E-C048-E6F9-6F53D8AF03CA}
|
||||||
|
{D3BF565A-C413-4185-9528-BE1B4F46993C} = {A2834164-BF04-BF13-ADC5-A97145852861}
|
||||||
|
{3EA375C7-2900-4927-B1E5-C9D31E67F4A8} = {A2834164-BF04-BF13-ADC5-A97145852861}
|
||||||
|
{BCC8A8A6-C2ED-42D2-86BB-A05C790D7279} = {DDEDC5E0-5D13-A45C-2393-A774DD4A1A07}
|
||||||
|
{836D1466-3C20-4D74-B54A-FA09C0EE0FA2} = {DDEDC5E0-5D13-A45C-2393-A774DD4A1A07}
|
||||||
|
{BCDEAB10-6373-46E7-B408-846A3B0B508B} = {773BFBD8-04CD-79F8-8301-C81308C3ED45}
|
||||||
|
{B0EC74C5-9B2D-492C-ABAE-3E868397B122} = {773BFBD8-04CD-79F8-8301-C81308C3ED45}
|
||||||
|
{9B4BDD42-56F3-4DB9-B3E5-74ABB7C19538} = {5CED3889-AECF-A6CD-55DC-F680D3C18861}
|
||||||
|
{101FFD12-17A4-4615-9438-F347BBF4CC85} = {5CED3889-AECF-A6CD-55DC-F680D3C18861}
|
||||||
|
{AEDB3219-5E1D-4716-8DE2-F5F9391913A2} = {C734CEF7-A2AC-3076-84D8-694B7490AA9D}
|
||||||
|
{427BE8BE-DA7B-FC74-412B-547671E05463} = {CDFA86FA-BBBA-4A5B-A833-3BE219E373E5}
|
||||||
|
{C015BF35-6977-407B-8948-636A9C81C5BE} = {427BE8BE-DA7B-FC74-412B-547671E05463}
|
||||||
|
{19A89F36-FD3A-448D-90D1-04A1B67BB255} = {1B4FBE3A-43F5-1B1E-2877-3036AC5431EF}
|
||||||
|
{B27759CD-5A7D-43A4-A55C-FE1154DC4CC4} = {51D8F471-B8EB-AD1C-0E89-AA84C5D0C759}
|
||||||
|
{BD23EEF8-9196-4E0F-BF33-E14E99D34C1B} = {51D8F471-B8EB-AD1C-0E89-AA84C5D0C759}
|
||||||
|
{B52D6341-AAD9-43CB-82AF-2DBE39CBF1DB} = {51D8F471-B8EB-AD1C-0E89-AA84C5D0C759}
|
||||||
|
{0DAACE48-4EA6-4DB7-8A5C-99B86BCB1E01} = {4B043475-1AFA-C467-FE09-A46D09CD6936}
|
||||||
|
{A85AE27D-81ED-485A-BA4B-161B25BEB8A5} = {54BCCDE8-25E6-6FCB-4A9E-D5D2AF76D352}
|
||||||
|
{D1B6353A-63F5-4DD9-90E6-42B2CFDF1DEA} = {CD4A4407-C3B0-422D-BB8C-2A810CED9938}
|
||||||
|
{C4287034-6833-4505-A6EB-704A86392ECB} = {D1B6353A-63F5-4DD9-90E6-42B2CFDF1DEA}
|
||||||
|
{490BCB11-314C-473C-9B85-A32164783507} = {C4287034-6833-4505-A6EB-704A86392ECB}
|
||||||
|
{5B7BF918-E47F-4932-B5C5-E8C2C35890E4} = {C4287034-6833-4505-A6EB-704A86392ECB}
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
@ -29,7 +29,9 @@ grant_type=password
|
|||||||
&client_secret=secret
|
&client_secret=secret
|
||||||
&username=samh
|
&username=samh
|
||||||
&password=Admin@123456
|
&password=Admin@123456
|
||||||
&scope=flight-api
|
&scope=flight-api role
|
||||||
|
|
||||||
|
### change scope base on microservices scope (eg. passenger-api, ...)
|
||||||
###
|
###
|
||||||
|
|
||||||
|
|
||||||
@ -45,7 +47,7 @@ authorization: bearer {{Authenticate.response.body.access_token}}
|
|||||||
"firstName": "John",
|
"firstName": "John",
|
||||||
"lastName": "Do",
|
"lastName": "Do",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"passportNumber": "412900000000",
|
"passportNumber": "41290000",
|
||||||
"email": "admin@admin.com",
|
"email": "admin@admin.com",
|
||||||
"password": "Admin@12345",
|
"password": "Admin@12345",
|
||||||
"confirmPassword": "Admin@12345"
|
"confirmPassword": "Admin@12345"
|
||||||
@ -218,7 +220,7 @@ Content-Type: application/json
|
|||||||
authorization: bearer {{Authenticate.response.body.access_token}}
|
authorization: bearer {{Authenticate.response.body.access_token}}
|
||||||
|
|
||||||
{
|
{
|
||||||
"passportNumber": "412900000000",
|
"passportNumber": "41290000",
|
||||||
"passengerType": 1,
|
"passengerType": 1,
|
||||||
"age": 30
|
"age": 30
|
||||||
}
|
}
|
||||||
|
|||||||
8
deployments/configs/dashboards.md
Normal file
8
deployments/configs/dashboards.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
# Dashboards
|
||||||
|
|
||||||
|
- [Introducing ASP.NET Core metrics and Grafana dashboards in .NET 8](https://devblogs.microsoft.com/dotnet/introducing-aspnetcore-metrics-and-grafana-dashboards-in-dotnet-8/)
|
||||||
|
- [ASP.NET Core](https://grafana.com/grafana/dashboards/19924-asp-net-core/)
|
||||||
|
- [ASP.NET Core Endpoint](https://grafana.com/grafana/dashboards/19925-asp-net-core-endpoint/)
|
||||||
|
- [Node Exporter Quickstart and Dashboard](https://grafana.com/grafana/dashboards/13978-node-exporter-quickstart-and-dashboard/)
|
||||||
|
- [PostgreSQL Exporter Quickstart and Dashboard](https://grafana.com/grafana/dashboards/14114-postgres-overview/)
|
||||||
|
- [RabbitMQ-Overview](https://grafana.com/grafana/dashboards/10991-rabbitmq-overview/)
|
||||||
892
deployments/configs/grafana/dashboards/node-exporter.json
Normal file
892
deployments/configs/grafana/dashboards/node-exporter.json
Normal file
@ -0,0 +1,892 @@
|
|||||||
|
{
|
||||||
|
"__inputs": [],
|
||||||
|
"__requires": [
|
||||||
|
{
|
||||||
|
"type": "grafana",
|
||||||
|
"id": "grafana",
|
||||||
|
"name": "Grafana",
|
||||||
|
"version": "7.4.3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "panel",
|
||||||
|
"id": "graph",
|
||||||
|
"name": "Graph",
|
||||||
|
"version": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "datasource",
|
||||||
|
"id": "prometheus",
|
||||||
|
"name": "Prometheus",
|
||||||
|
"version": "1.0.0"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"annotations": {
|
||||||
|
"list": []
|
||||||
|
},
|
||||||
|
"editable": false,
|
||||||
|
"gnetId": 13978,
|
||||||
|
"graphTooltip": 0,
|
||||||
|
"hideControls": false,
|
||||||
|
"id": null,
|
||||||
|
"links": [],
|
||||||
|
"refresh": "",
|
||||||
|
"rows": [
|
||||||
|
{
|
||||||
|
"collapse": false,
|
||||||
|
"collapsed": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "$datasource",
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {},
|
||||||
|
"id": 2,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": false,
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"rightSide": false,
|
||||||
|
"show": true,
|
||||||
|
"sideWidth": null,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"repeat": null,
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"span": 6,
|
||||||
|
"stack": true,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "(\n (1 - rate(node_cpu_seconds_total{job=\"node\", mode=\"idle\", instance=\"$instance\"}[$__interval]))\n/ ignoring(cpu) group_left\n count without (cpu)( node_cpu_seconds_total{job=\"node\", mode=\"idle\", instance=\"$instance\"})\n)\n",
|
||||||
|
"format": "time_series",
|
||||||
|
"interval": "1m",
|
||||||
|
"intervalFactor": 5,
|
||||||
|
"legendFormat": "{{cpu}}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "CPU Usage",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "percentunit",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": 1,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "percentunit",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": 1,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "$datasource",
|
||||||
|
"fill": 0,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {},
|
||||||
|
"id": 3,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": false,
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"rightSide": false,
|
||||||
|
"show": true,
|
||||||
|
"sideWidth": null,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"repeat": null,
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"span": 6,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "node_load1{job=\"node\", instance=\"$instance\"}",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "1m load average",
|
||||||
|
"refId": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": "node_load5{job=\"node\", instance=\"$instance\"}",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "5m load average",
|
||||||
|
"refId": "B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": "node_load15{job=\"node\", instance=\"$instance\"}",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "15m load average",
|
||||||
|
"refId": "C"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": "count(node_cpu_seconds_total{job=\"node\", instance=\"$instance\", mode=\"idle\"})",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "logical cores",
|
||||||
|
"refId": "D"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Load Average",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "short",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repeat": null,
|
||||||
|
"repeatIteration": null,
|
||||||
|
"repeatRowId": null,
|
||||||
|
"showTitle": false,
|
||||||
|
"title": "Dashboard Row",
|
||||||
|
"titleSize": "h6",
|
||||||
|
"type": "row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapse": false,
|
||||||
|
"collapsed": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "$datasource",
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {},
|
||||||
|
"id": 4,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": false,
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"rightSide": false,
|
||||||
|
"show": true,
|
||||||
|
"sideWidth": null,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"repeat": null,
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"span": 9,
|
||||||
|
"stack": true,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "(\n node_memory_MemTotal_bytes{job=\"node\", instance=\"$instance\"}\n-\n node_memory_MemFree_bytes{job=\"node\", instance=\"$instance\"}\n-\n node_memory_Buffers_bytes{job=\"node\", instance=\"$instance\"}\n-\n node_memory_Cached_bytes{job=\"node\", instance=\"$instance\"}\n)\n",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "memory used",
|
||||||
|
"refId": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": "node_memory_Buffers_bytes{job=\"node\", instance=\"$instance\"}",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "memory buffers",
|
||||||
|
"refId": "B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": "node_memory_Cached_bytes{job=\"node\", instance=\"$instance\"}",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "memory cached",
|
||||||
|
"refId": "C"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": "node_memory_MemFree_bytes{job=\"node\", instance=\"$instance\"}",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "memory free",
|
||||||
|
"refId": "D"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Memory Usage",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "bytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "bytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cacheTimeout": null,
|
||||||
|
"colorBackground": false,
|
||||||
|
"colorValue": false,
|
||||||
|
"colors": [
|
||||||
|
"rgba(50, 172, 45, 0.97)",
|
||||||
|
"rgba(237, 129, 40, 0.89)",
|
||||||
|
"rgba(245, 54, 54, 0.9)"
|
||||||
|
],
|
||||||
|
"datasource": "$datasource",
|
||||||
|
"format": "percent",
|
||||||
|
"gauge": {
|
||||||
|
"maxValue": 100,
|
||||||
|
"minValue": 0,
|
||||||
|
"show": true,
|
||||||
|
"thresholdLabels": false,
|
||||||
|
"thresholdMarkers": true
|
||||||
|
},
|
||||||
|
"gridPos": {},
|
||||||
|
"id": 5,
|
||||||
|
"interval": null,
|
||||||
|
"links": [],
|
||||||
|
"mappingType": 1,
|
||||||
|
"mappingTypes": [
|
||||||
|
{
|
||||||
|
"name": "value to text",
|
||||||
|
"value": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "range to text",
|
||||||
|
"value": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"maxDataPoints": 100,
|
||||||
|
"nullPointMode": "connected",
|
||||||
|
"nullText": null,
|
||||||
|
"postfix": "",
|
||||||
|
"postfixFontSize": "50%",
|
||||||
|
"prefix": "",
|
||||||
|
"prefixFontSize": "50%",
|
||||||
|
"rangeMaps": [
|
||||||
|
{
|
||||||
|
"from": "null",
|
||||||
|
"text": "N/A",
|
||||||
|
"to": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"span": 3,
|
||||||
|
"sparkline": {
|
||||||
|
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||||
|
"full": false,
|
||||||
|
"lineColor": "rgb(31, 120, 193)",
|
||||||
|
"show": false
|
||||||
|
},
|
||||||
|
"tableColumn": "",
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "100 -\n(\n avg(node_memory_MemAvailable_bytes{job=\"node\", instance=\"$instance\"})\n/\n avg(node_memory_MemTotal_bytes{job=\"node\", instance=\"$instance\"})\n* 100\n)\n",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": "80, 90",
|
||||||
|
"title": "Memory Usage",
|
||||||
|
"type": "singlestat",
|
||||||
|
"valueFontSize": "80%",
|
||||||
|
"valueMaps": [
|
||||||
|
{
|
||||||
|
"op": "=",
|
||||||
|
"text": "N/A",
|
||||||
|
"value": "null"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"valueName": "current"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repeat": null,
|
||||||
|
"repeatIteration": null,
|
||||||
|
"repeatRowId": null,
|
||||||
|
"showTitle": false,
|
||||||
|
"title": "Dashboard Row",
|
||||||
|
"titleSize": "h6",
|
||||||
|
"type": "row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapse": false,
|
||||||
|
"collapsed": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "$datasource",
|
||||||
|
"fill": 0,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {},
|
||||||
|
"id": 6,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": false,
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"rightSide": false,
|
||||||
|
"show": true,
|
||||||
|
"sideWidth": null,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"repeat": null,
|
||||||
|
"seriesOverrides": [
|
||||||
|
{
|
||||||
|
"alias": "/ read| written/",
|
||||||
|
"yaxis": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "/ io time/",
|
||||||
|
"yaxis": 2
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"span": 6,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "rate(node_disk_read_bytes_total{job=\"node\", instance=\"$instance\", device!=\"\"}[$__interval])",
|
||||||
|
"format": "time_series",
|
||||||
|
"interval": "1m",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "{{device}} read",
|
||||||
|
"refId": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": "rate(node_disk_written_bytes_total{job=\"node\", instance=\"$instance\", device!=\"\"}[$__interval])",
|
||||||
|
"format": "time_series",
|
||||||
|
"interval": "1m",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "{{device}} written",
|
||||||
|
"refId": "B"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": "rate(node_disk_io_time_seconds_total{job=\"node\", instance=\"$instance\", device!=\"\"}[$__interval])",
|
||||||
|
"format": "time_series",
|
||||||
|
"interval": "1m",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "{{device}} io time",
|
||||||
|
"refId": "C"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Disk I/O",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "bytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "s",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": null,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "$datasource",
|
||||||
|
"fill": 1,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {},
|
||||||
|
"id": 7,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": false,
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"rightSide": false,
|
||||||
|
"show": true,
|
||||||
|
"sideWidth": null,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"repeat": null,
|
||||||
|
"seriesOverrides": [
|
||||||
|
{
|
||||||
|
"alias": "used",
|
||||||
|
"color": "#E0B400"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"alias": "available",
|
||||||
|
"color": "#73BF69"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"span": 6,
|
||||||
|
"stack": true,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "sum(\n max by (device) (\n node_filesystem_size_bytes{job=\"node\", instance=\"$instance\", fstype!=\"\"}\n -\n node_filesystem_avail_bytes{job=\"node\", instance=\"$instance\", fstype!=\"\"}\n )\n)\n",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "used",
|
||||||
|
"refId": "A"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"expr": "sum(\n max by (device) (\n node_filesystem_avail_bytes{job=\"node\", instance=\"$instance\", fstype!=\"\"}\n )\n)\n",
|
||||||
|
"format": "time_series",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "available",
|
||||||
|
"refId": "B"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Disk Space Usage",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "bytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "bytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repeat": null,
|
||||||
|
"repeatIteration": null,
|
||||||
|
"repeatRowId": null,
|
||||||
|
"showTitle": false,
|
||||||
|
"title": "Dashboard Row",
|
||||||
|
"titleSize": "h6",
|
||||||
|
"type": "row"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapse": false,
|
||||||
|
"collapsed": false,
|
||||||
|
"panels": [
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "$datasource",
|
||||||
|
"fill": 0,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {},
|
||||||
|
"id": 8,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": false,
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"rightSide": false,
|
||||||
|
"show": true,
|
||||||
|
"sideWidth": null,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"repeat": null,
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"span": 6,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "rate(node_network_receive_bytes_total{job=\"node\", instance=\"$instance\", device!=\"lo\"}[$__interval])",
|
||||||
|
"format": "time_series",
|
||||||
|
"interval": "1m",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "{{device}}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Network Received",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "bytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "bytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"aliasColors": {},
|
||||||
|
"bars": false,
|
||||||
|
"dashLength": 10,
|
||||||
|
"dashes": false,
|
||||||
|
"datasource": "$datasource",
|
||||||
|
"fill": 0,
|
||||||
|
"fillGradient": 0,
|
||||||
|
"gridPos": {},
|
||||||
|
"id": 9,
|
||||||
|
"legend": {
|
||||||
|
"alignAsTable": false,
|
||||||
|
"avg": false,
|
||||||
|
"current": false,
|
||||||
|
"max": false,
|
||||||
|
"min": false,
|
||||||
|
"rightSide": false,
|
||||||
|
"show": true,
|
||||||
|
"sideWidth": null,
|
||||||
|
"total": false,
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"lines": true,
|
||||||
|
"linewidth": 1,
|
||||||
|
"links": [],
|
||||||
|
"nullPointMode": "null",
|
||||||
|
"percentage": false,
|
||||||
|
"pointradius": 5,
|
||||||
|
"points": false,
|
||||||
|
"renderer": "flot",
|
||||||
|
"repeat": null,
|
||||||
|
"seriesOverrides": [],
|
||||||
|
"spaceLength": 10,
|
||||||
|
"span": 6,
|
||||||
|
"stack": false,
|
||||||
|
"steppedLine": false,
|
||||||
|
"targets": [
|
||||||
|
{
|
||||||
|
"expr": "rate(node_network_transmit_bytes_total{job=\"node\", instance=\"$instance\", device!=\"lo\"}[$__interval])",
|
||||||
|
"format": "time_series",
|
||||||
|
"interval": "1m",
|
||||||
|
"intervalFactor": 2,
|
||||||
|
"legendFormat": "{{device}}",
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"thresholds": [],
|
||||||
|
"timeFrom": null,
|
||||||
|
"timeShift": null,
|
||||||
|
"title": "Network Transmitted",
|
||||||
|
"tooltip": {
|
||||||
|
"shared": true,
|
||||||
|
"sort": 0,
|
||||||
|
"value_type": "individual"
|
||||||
|
},
|
||||||
|
"type": "graph",
|
||||||
|
"xaxis": {
|
||||||
|
"buckets": null,
|
||||||
|
"mode": "time",
|
||||||
|
"name": null,
|
||||||
|
"show": true,
|
||||||
|
"values": []
|
||||||
|
},
|
||||||
|
"yaxes": [
|
||||||
|
{
|
||||||
|
"format": "bytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"format": "bytes",
|
||||||
|
"label": null,
|
||||||
|
"logBase": 1,
|
||||||
|
"max": null,
|
||||||
|
"min": 0,
|
||||||
|
"show": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"repeat": null,
|
||||||
|
"repeatIteration": null,
|
||||||
|
"repeatRowId": null,
|
||||||
|
"showTitle": false,
|
||||||
|
"title": "Dashboard Row",
|
||||||
|
"titleSize": "h6",
|
||||||
|
"type": "row"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"schemaVersion": 14,
|
||||||
|
"style": "dark",
|
||||||
|
"tags": [],
|
||||||
|
"templating": {
|
||||||
|
"list": [
|
||||||
|
{
|
||||||
|
"current": {
|
||||||
|
"text": "Prometheus",
|
||||||
|
"value": "Prometheus"
|
||||||
|
},
|
||||||
|
"hide": 0,
|
||||||
|
"label": null,
|
||||||
|
"name": "datasource",
|
||||||
|
"options": [],
|
||||||
|
"query": "prometheus",
|
||||||
|
"refresh": 1,
|
||||||
|
"regex": "",
|
||||||
|
"type": "datasource"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allValue": null,
|
||||||
|
"current": {},
|
||||||
|
"datasource": "$datasource",
|
||||||
|
"hide": 0,
|
||||||
|
"includeAll": false,
|
||||||
|
"label": null,
|
||||||
|
"multi": false,
|
||||||
|
"name": "instance",
|
||||||
|
"options": [],
|
||||||
|
"query": "label_values(node_exporter_build_info{job=\"node\"}, instance)",
|
||||||
|
"refresh": 2,
|
||||||
|
"regex": "",
|
||||||
|
"sort": 0,
|
||||||
|
"tagValuesQuery": "",
|
||||||
|
"tags": [],
|
||||||
|
"tagsQuery": "",
|
||||||
|
"type": "query",
|
||||||
|
"useTags": false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"time": {
|
||||||
|
"from": "now-1h",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"timepicker": {
|
||||||
|
"refresh_intervals": [
|
||||||
|
"5s",
|
||||||
|
"10s",
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h",
|
||||||
|
"2h",
|
||||||
|
"1d"
|
||||||
|
],
|
||||||
|
"time_options": [
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"1h",
|
||||||
|
"6h",
|
||||||
|
"12h",
|
||||||
|
"24h",
|
||||||
|
"2d",
|
||||||
|
"7d",
|
||||||
|
"30d"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"timezone": "browser",
|
||||||
|
"title": "Node Exporter Quickstart and Dashboard",
|
||||||
|
"version": 0,
|
||||||
|
"description": "A quickstart to setup Prometheus Node Exporter with preconfigured dashboards, alerting rules, and recording rules."
|
||||||
|
}
|
||||||
1459
deployments/configs/grafana/dashboards/postgresql.json
Normal file
1459
deployments/configs/grafana/dashboards/postgresql.json
Normal file
File diff suppressed because it is too large
Load Diff
8209
deployments/configs/grafana/dashboards/rabbitmq.json
Normal file
8209
deployments/configs/grafana/dashboards/rabbitmq.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,14 @@
|
|||||||
|
# https://grafana.com/docs/grafana/latest/administration/provisioning/#dashboards
|
||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
providers:
|
||||||
|
- name: "default"
|
||||||
|
orgId: 1
|
||||||
|
folder: ""
|
||||||
|
type: file
|
||||||
|
disableDeletion: false
|
||||||
|
editable: true
|
||||||
|
allowUiUpdates: true
|
||||||
|
updateIntervalSeconds: 5 # how often Grafana will scan for changed dashboards
|
||||||
|
options:
|
||||||
|
path: /var/lib/grafana/dashboards # path to dashboards on disk
|
||||||
@ -0,0 +1,88 @@
|
|||||||
|
# https://grafana.com/docs/grafana/latest/administration/provisioning/
|
||||||
|
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/grafana-datasources.yaml
|
||||||
|
# https://github.com/grafana/intro-to-mltp/blob/main/grafana/provisioning/datasources/datasources.yaml
|
||||||
|
|
||||||
|
apiVersion: 1
|
||||||
|
|
||||||
|
datasources:
|
||||||
|
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/grafana-datasources.yaml
|
||||||
|
- name: Prometheus
|
||||||
|
type: prometheus
|
||||||
|
typeName: Prometheus
|
||||||
|
uid: prometheus-uid
|
||||||
|
access: proxy
|
||||||
|
orgId: 1
|
||||||
|
url: http://prometheus:9090
|
||||||
|
basicAuth: false
|
||||||
|
isDefault: true
|
||||||
|
readOnly: false
|
||||||
|
user: ''
|
||||||
|
database: ''
|
||||||
|
version: 1
|
||||||
|
editable: false
|
||||||
|
jsonData:
|
||||||
|
httpMethod: GET
|
||||||
|
|
||||||
|
- name: Jaeger
|
||||||
|
type: jaeger
|
||||||
|
access: proxy
|
||||||
|
url: http://jaeger-all-in-one:16686
|
||||||
|
editable: false
|
||||||
|
uid: jaeger-uid
|
||||||
|
|
||||||
|
- name: Zipkin
|
||||||
|
type: zipkin
|
||||||
|
access: proxy
|
||||||
|
url: http://zipkin-all-in-one:9411
|
||||||
|
editable: false
|
||||||
|
uid: zipkin-uid
|
||||||
|
|
||||||
|
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/grafana-datasources.yaml
|
||||||
|
- name: Tempo
|
||||||
|
type: tempo
|
||||||
|
access: proxy
|
||||||
|
orgId: 1
|
||||||
|
url: http://tempo:3200
|
||||||
|
basicAuth: false
|
||||||
|
isDefault: false
|
||||||
|
version: 1
|
||||||
|
editable: false
|
||||||
|
apiVersion: 1
|
||||||
|
uid: tempo-uid
|
||||||
|
jsonData:
|
||||||
|
httpMethod: GET
|
||||||
|
serviceMap:
|
||||||
|
datasourceUid: prometheus-uid
|
||||||
|
streamingEnabled:
|
||||||
|
search: true
|
||||||
|
|
||||||
|
#https://github.com/grafana/intro-to-mltp/blob/main/grafana/provisioning/datasources/datasources.yaml
|
||||||
|
- name: Loki
|
||||||
|
type: loki
|
||||||
|
access: proxy
|
||||||
|
uid: loki-uid
|
||||||
|
url: http://loki:3100
|
||||||
|
user: ''
|
||||||
|
database: ''
|
||||||
|
readOnly: false
|
||||||
|
jsonData:
|
||||||
|
derivedFields:
|
||||||
|
- datasourceUid: tempo-uid
|
||||||
|
matcherRegex: "^.*?traceI[d|D]=(\\w+).*$"
|
||||||
|
name: traceId
|
||||||
|
url: '$${__value.raw}'
|
||||||
|
|
||||||
|
- name: Kibana
|
||||||
|
type: elasticsearch
|
||||||
|
url: http://elasticsearch:9200
|
||||||
|
access: proxy
|
||||||
|
isDefault: false
|
||||||
|
uid: kibana-uid
|
||||||
|
jsonData:
|
||||||
|
esVersion: 7
|
||||||
|
timeField: "@timestamp"
|
||||||
|
maxConcurrentShardRequests: 256
|
||||||
|
interval: Daily
|
||||||
|
logMessageField: "message" # Optional: Field for log messages
|
||||||
|
logLevelField: "level" # Optional: Field for log levels
|
||||||
|
editable: true
|
||||||
44
deployments/configs/loki-config.yaml
Normal file
44
deployments/configs/loki-config.yaml
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# https://grafana.com/docs/loki/latest/configure/examples/configuration-examples/
|
||||||
|
# https://github.com/grafana/loki/issues/2018#issuecomment-970789233
|
||||||
|
# https://grafana.com/docs/opentelemetry/collector/send-logs-to-loki/
|
||||||
|
# https://github.com/grafana/loki/blob/main/examples/getting-started/loki-config.yaml
|
||||||
|
# https://github.com/grafana/loki/blob/main/cmd/loki/loki-local-config.yaml
|
||||||
|
# https://grafana.com/docs/loki/latest/configure/examples/configuration-examples/#1-local-configuration-exampleyaml
|
||||||
|
|
||||||
|
---
|
||||||
|
# https://grafana.com/docs/loki/latest/configure/examples/configuration-examples/#1-local-configuration-exampleyaml
|
||||||
|
auth_enabled: false
|
||||||
|
|
||||||
|
# This is a complete configuration to deploy Loki backed by the filesystem.
|
||||||
|
# The index will be shipped to the storage via tsdb-shipper.
|
||||||
|
server:
|
||||||
|
http_listen_port: 3100
|
||||||
|
|
||||||
|
common:
|
||||||
|
ring:
|
||||||
|
instance_addr: 127.0.0.1
|
||||||
|
kvstore:
|
||||||
|
store: inmemory
|
||||||
|
replication_factor: 1
|
||||||
|
path_prefix: /tmp/loki
|
||||||
|
|
||||||
|
schema_config:
|
||||||
|
configs:
|
||||||
|
- from: 2020-05-15
|
||||||
|
store: tsdb
|
||||||
|
object_store: filesystem
|
||||||
|
schema: v13
|
||||||
|
index:
|
||||||
|
prefix: index_
|
||||||
|
period: 24h
|
||||||
|
|
||||||
|
storage_config:
|
||||||
|
filesystem:
|
||||||
|
directory: /tmp/loki/chunks
|
||||||
|
|
||||||
|
# https://grafana.com/docs/loki/latest/send-data/otel/
|
||||||
|
# https://grafana.com/docs/loki/latest/send-data/otel/#changing-the-default-mapping-of-otlp-to-loki-format
|
||||||
|
limits_config:
|
||||||
|
# this attribute should be `true` when we use `otlphttp/loki`, but if we want to use `loki component` from `opentelemetry-collector-contrib` it should be false.
|
||||||
|
allow_structured_metadata: true
|
||||||
|
|
||||||
131
deployments/configs/otel-collector-config.yaml
Normal file
131
deployments/configs/otel-collector-config.yaml
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
# ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/examples/demo/otel-collector-config.yaml
|
||||||
|
# https://opentelemetry.io/docs/collector/configuration/
|
||||||
|
# https://opentelemetry.io/docs/collector/architecture/
|
||||||
|
# https://betterstack.com/community/guides/observability/opentelemetry-collector/
|
||||||
|
# https://signoz.io/blog/opentelemetry-collector-complete-guide/
|
||||||
|
|
||||||
|
# This configuration sets up an OpenTelemetry Collector that receives trace data via the OTLP protocol over HTTP on port 4318, applies batch processing, and then exports the processed traces
|
||||||
|
# to exporter components like `Jaeger` endpoint located at `jaeger-all-in-one:4317`. It also includes a health_check extension for monitoring the collector's status.
|
||||||
|
|
||||||
|
# Receivers in the OpenTelemetry Collector are components that collect telemetry data (traces, metrics, and logs) from various sources, such as instrumented applications or agents.
|
||||||
|
# They act as entry points, converting incoming data into OpenTelemetry's internal format for processing and export.
|
||||||
|
# https://betterstack.com/community/guides/observability/opentelemetry-collector/#exploring-the-opentelemetry-collector-components
|
||||||
|
# https://opentelemetry.io/docs/collector/architecture/#receivers
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector/blob/main/receiver/README.md
|
||||||
|
# https://opentelemetry.io/docs/collector/configuration/#receivers
|
||||||
|
receivers:
|
||||||
|
# supported receivers
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver
|
||||||
|
# instead of specifying details explicitly we can just use `otlp` and it uses both grpc and http
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
|
endpoint: 0.0.0.0:4318
|
||||||
|
# prometheus:
|
||||||
|
# config:
|
||||||
|
# scrape_configs:
|
||||||
|
# - job_name: 'node-exporter'
|
||||||
|
# scrape_interval: 10s
|
||||||
|
# static_configs:
|
||||||
|
# - targets: [ 'node-exporter:9100' ]
|
||||||
|
|
||||||
|
|
||||||
|
# Processors in the OpenTelemetry Collector modify and enhance telemetry data by filtering, transforming, enriching, or batching it to prepare it for export.
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector/tree/main/processor
|
||||||
|
processors:
|
||||||
|
batch: # Batches logs for better performance
|
||||||
|
|
||||||
|
# - Exporters in the OpenTelemetry Collector send processed telemetry data to backend systems like observability platforms, databases, or cloud services for storage, visualization, and analysis.
|
||||||
|
# - The `key` follows the `type/name` format, where `type` specifies the exporter `type` (e.g., otlp, kafka, prometheus), and `name` (optional) can be appended to provide a unique name for multiple instance of the same type
|
||||||
|
# https://betterstack.com/community/guides/observability/opentelemetry-collector/#exploring-the-opentelemetry-collector-components
|
||||||
|
# https://opentelemetry.io/docs/collector/architecture/#exporters
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector/tree/main/exporter
|
||||||
|
# https://opentelemetry.io/docs/collector/configuration/#exporters
|
||||||
|
exporters:
|
||||||
|
|
||||||
|
# valid values: [prometheusremotewrite zipkin otlphttp file kafka prometheus debug nop otlp opencensus]
|
||||||
|
|
||||||
|
# Prometheus exporter metrics
|
||||||
|
prometheus:
|
||||||
|
endpoint: "0.0.0.0:8889"
|
||||||
|
|
||||||
|
prometheusremotewrite:
|
||||||
|
endpoint: "http://prometheus:9090/api/v1/write"
|
||||||
|
|
||||||
|
# https://grafana.com/docs/loki/latest/send-data/otel/
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/exporter/lokiexporter/README.md
|
||||||
|
otlphttp/loki:
|
||||||
|
endpoint: "http://loki:3100/otlp"
|
||||||
|
tls:
|
||||||
|
insecure: true
|
||||||
|
|
||||||
|
# # we can also use `loki component` from `opentelemetry-collector-contrib` if we don't want to use builtin `otlphttp` exporter type and `http://loki:3100/otlp` loki endpoint
|
||||||
|
# loki:
|
||||||
|
# endpoint: "http://loki:3100/loki/api/v1/push"
|
||||||
|
# tls:
|
||||||
|
# insecure: true
|
||||||
|
|
||||||
|
debug:
|
||||||
|
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/elasticsearchexporter
|
||||||
|
# using `elasticsearch` from `opentelemetry-collector-contrib` components because it doesn't exist in `opentelemetry-collector`
|
||||||
|
elasticsearch:
|
||||||
|
endpoint: "http://elasticsearch:9200"
|
||||||
|
|
||||||
|
zipkin:
|
||||||
|
endpoint: "http://zipkin-all-in-one:9411/api/v2/spans"
|
||||||
|
format: proto
|
||||||
|
|
||||||
|
# export collected telemetry traces to jaeger OTLP grpc port, we can send data to other available endpoints and ports on jaeger as well
|
||||||
|
otlp/jaeger:
|
||||||
|
endpoint: "http://jaeger-all-in-one:4317"
|
||||||
|
tls:
|
||||||
|
insecure: true
|
||||||
|
|
||||||
|
otlp/tempo:
|
||||||
|
endpoint: "http://tempo:4317"
|
||||||
|
tls:
|
||||||
|
insecure: true
|
||||||
|
|
||||||
|
# seq-otlp:
|
||||||
|
# endpoint: "http://seq:5341/ingest/otlp"
|
||||||
|
|
||||||
|
# https://opentelemetry.io/docs/collector/configuration/#extensions
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector/blob/main/extension/README.md
|
||||||
|
extensions:
|
||||||
|
pprof:
|
||||||
|
endpoint: 0.0.0.0:1888
|
||||||
|
zpages:
|
||||||
|
endpoint: 0.0.0.0:55679
|
||||||
|
health_check:
|
||||||
|
endpoint: 0.0.0.0:13133
|
||||||
|
|
||||||
|
# - The service section is used to configure what components are enabled in the Collector based on the configuration found in the receivers, processors, exporters, and extensions sections.
|
||||||
|
# - If a component is configured, but not defined within the service section, then it’s not enabled.
|
||||||
|
# https://betterstack.com/community/guides/observability/opentelemetry-collector/#exploring-the-opentelemetry-collector-components
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector/blob/main/exporter/README.md
|
||||||
|
# https://opentelemetry.io/docs/collector/architecture/
|
||||||
|
# https://opentelemetry.io/docs/collector/configuration/#service
|
||||||
|
service:
|
||||||
|
# The `service.extensions` subsection determines which of the configured extensions will be enabled
|
||||||
|
extensions: [pprof, zpages, health_check]
|
||||||
|
# The `service.pipeline` Each pipeline starts with one or more receivers collecting data, which is then processed sequentially by processors (applying transformations, filtering, or sampling).
|
||||||
|
# The processed data is finally sent to all configured exporters, ensuring each receives a copy. Components must be pre-configured in their respective sections before being used in a pipeline.
|
||||||
|
# pipeline activate predefined components, defined components are disabled by default
|
||||||
|
pipelines:
|
||||||
|
traces:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [batch]
|
||||||
|
# https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#exporter-selection
|
||||||
|
exporters: [debug, zipkin, otlp/jaeger, otlp/tempo]
|
||||||
|
metrics:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [batch]
|
||||||
|
exporters: [debug, prometheusremotewrite, prometheus]
|
||||||
|
logs:
|
||||||
|
receivers: [otlp]
|
||||||
|
processors: [batch]
|
||||||
|
exporters: [otlphttp/loki, elasticsearch]
|
||||||
48
deployments/configs/prometheus.yaml
Normal file
48
deployments/configs/prometheus.yaml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
# ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/examples/demo/prometheus.yaml
|
||||||
|
# https://prometheus.io/docs/introduction/first_steps/
|
||||||
|
# https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
|
||||||
|
|
||||||
|
global:
|
||||||
|
scrape_interval: 5s
|
||||||
|
|
||||||
|
scrape_configs:
|
||||||
|
# when we use otel-collector we should comment other jobs in prometheus config, and we read configs from `otel-collector-config`
|
||||||
|
- job_name: "otel-collector"
|
||||||
|
scrape_interval: 10s
|
||||||
|
static_configs:
|
||||||
|
# otel-collector Prometheus exporter metrics
|
||||||
|
- targets: [ 'otel-collector:8889' ]
|
||||||
|
- targets: [ 'otel-collector:8888' ]
|
||||||
|
|
||||||
|
- job_name: "prometheus"
|
||||||
|
static_configs:
|
||||||
|
- targets: ["prometheus:9090"]
|
||||||
|
|
||||||
|
# # https://prometheus.io/docs/guides/node-exporter/
|
||||||
|
# # https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
|
||||||
|
# - job_name: "node-exporter"
|
||||||
|
# static_configs:
|
||||||
|
# - targets: [ 'node-exporter:9100' ]
|
||||||
|
|
||||||
|
# # if we don't use otel collector we should uncomment this
|
||||||
|
# # scrap application metrics
|
||||||
|
# # http://localhost:4000/metrics by AddPrometheusExporter()
|
||||||
|
# - job_name: vertical-slice-template-api
|
||||||
|
# scrape_interval: 10s
|
||||||
|
# metrics_path: /metrics
|
||||||
|
# static_configs:
|
||||||
|
# - targets: ['host.docker.internal:4000']
|
||||||
|
#
|
||||||
|
# # if we don't use otel collector we should uncomment this
|
||||||
|
# # scrap application health metrics
|
||||||
|
# # http://localhost:4000/health/metrics by AddPrometheusExporter()
|
||||||
|
# - job_name: vertical-slice-template-api-healthchecks
|
||||||
|
# scrape_interval: 10s
|
||||||
|
# metrics_path: /health/metrics
|
||||||
|
# static_configs:
|
||||||
|
# - targets: ['host.docker.internal:4000']
|
||||||
|
|
||||||
|
## https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/prometheus.yaml
|
||||||
|
# - job_name: 'tempo'
|
||||||
|
# static_configs:
|
||||||
|
# - targets: [ 'tempo:3200' ]
|
||||||
49
deployments/configs/tempo.yaml
Normal file
49
deployments/configs/tempo.yaml
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# https://grafana.com/docs/tempo/latest/configuration/
|
||||||
|
# https://github.com/grafana/tempo/blob/main/example/docker-compose/local/tempo.yaml
|
||||||
|
|
||||||
|
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared/tempo.yaml
|
||||||
|
stream_over_http_enabled: true
|
||||||
|
server:
|
||||||
|
http_listen_port: 3200
|
||||||
|
log_level: info
|
||||||
|
|
||||||
|
distributor:
|
||||||
|
receivers:
|
||||||
|
otlp:
|
||||||
|
protocols:
|
||||||
|
grpc:
|
||||||
|
endpoint: "tempo:4317"
|
||||||
|
|
||||||
|
ingester:
|
||||||
|
max_block_duration: 5m # cut the headblock when this much time passes. this is being set for demo purposes and should probably be left alone normally
|
||||||
|
|
||||||
|
compactor:
|
||||||
|
compaction:
|
||||||
|
block_retention: 1h # overall Tempo trace retention. set for demo purposes
|
||||||
|
|
||||||
|
metrics_generator:
|
||||||
|
registry:
|
||||||
|
external_labels:
|
||||||
|
source: tempo
|
||||||
|
cluster: docker-compose
|
||||||
|
storage:
|
||||||
|
path: /var/tempo/generator/wal
|
||||||
|
remote_write:
|
||||||
|
- url: http://prometheus:9090/api/v1/write
|
||||||
|
send_exemplars: true
|
||||||
|
traces_storage:
|
||||||
|
path: /var/tempo/generator/traces
|
||||||
|
|
||||||
|
storage:
|
||||||
|
trace:
|
||||||
|
backend: local # backend configuration to use
|
||||||
|
wal:
|
||||||
|
path: /var/tempo/wal # where to store the wal locally
|
||||||
|
local:
|
||||||
|
path: /var/tempo/blocks
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
defaults:
|
||||||
|
metrics_generator:
|
||||||
|
processors: [service-graphs, span-metrics, local-blocks] # enables metrics generator
|
||||||
|
generate_native_histograms: both
|
||||||
362
deployments/docker-compose/docker-compose.infrastructure.yaml
Normal file
362
deployments/docker-compose/docker-compose.infrastructure.yaml
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
# ref: https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/examples/demo/docker-compose.yaml
|
||||||
|
# ref: https://github.com/joaofbantunes/DotNetMicroservicesObservabilitySample/blob/main/docker-compose.yml
|
||||||
|
# ref: https://github.com/oskardudycz/EventSourcing.NetCore/blob/main/docker-compose.yml
|
||||||
|
# https://github.com/grafana/intro-to-mltp
|
||||||
|
# https://stackoverflow.com/questions/65272764/ports-are-not-available-listen-tcp-0-0-0-0-50070-bind-an-attempt-was-made-to
|
||||||
|
|
||||||
|
|
||||||
|
name: booking-microservices-infrastructure
|
||||||
|
|
||||||
|
services:
|
||||||
|
#######################################################
|
||||||
|
# rabbitmq
|
||||||
|
#######################################################
|
||||||
|
rabbitmq:
|
||||||
|
image: rabbitmq:management
|
||||||
|
container_name: rabbitmq
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5672:5672"
|
||||||
|
- "15672:15672"
|
||||||
|
# volumes:
|
||||||
|
# - rabbitmq:/var/lib/rabbitmq
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# postgres
|
||||||
|
#######################################################
|
||||||
|
postgres:
|
||||||
|
image: postgres:latest
|
||||||
|
container_name: postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '5432:5432'
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=postgres
|
||||||
|
- POSTGRES_PASSWORD=postgres
|
||||||
|
command:
|
||||||
|
- "postgres"
|
||||||
|
- "-c"
|
||||||
|
- "wal_level=logical"
|
||||||
|
- "-c"
|
||||||
|
- "max_prepared_transactions=10"
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# EventStoreDB
|
||||||
|
#######################################################
|
||||||
|
eventstore:
|
||||||
|
container_name: eventstore
|
||||||
|
image: eventstore/eventstore:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- EVENTSTORE_CLUSTER_SIZE=1
|
||||||
|
- EVENTSTORE_RUN_PROJECTIONS=All
|
||||||
|
- EVENTSTORE_START_STANDARD_PROJECTIONS=True
|
||||||
|
- EVENTSTORE_HTTP_PORT=2113
|
||||||
|
- EVENTSTORE_INSECURE=True
|
||||||
|
- EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=True
|
||||||
|
ports:
|
||||||
|
- "2113:2113"
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # Mongo
|
||||||
|
# #######################################################
|
||||||
|
mongo:
|
||||||
|
image: mongo:latest
|
||||||
|
container_name: mongo
|
||||||
|
restart: unless-stopped
|
||||||
|
# environment:
|
||||||
|
# - MONGO_INITDB_ROOT_USERNAME=root
|
||||||
|
# - MONGO_INITDB_ROOT_PASSWORD=secret
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # Redis
|
||||||
|
# #######################################################
|
||||||
|
redis:
|
||||||
|
image: redis
|
||||||
|
container_name: redis
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- 6379:6379
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # jaeger
|
||||||
|
# # https://www.jaegertracing.io/docs/1.64/deployment/
|
||||||
|
# # https://www.jaegertracing.io/docs/1.6/deployment/
|
||||||
|
# #######################################################
|
||||||
|
jaeger-all-in-one:
|
||||||
|
image: jaegertracing/all-in-one:latest
|
||||||
|
container_name: jaeger-all-in-one
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "6831:6831/udp" # UDP port for Jaeger agent
|
||||||
|
- "16686:16686" # endpoints and Jaeger UI
|
||||||
|
- "14268:14268" # HTTP port for accept trace spans directly from clients
|
||||||
|
- "14317:4317" # OTLP gRPC receiver for jaeger
|
||||||
|
- "14318:4318" # OTLP http receiver for jaeger
|
||||||
|
# - "9411" # Accepts Zipkin spans - /api/v2/spans
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # zipkin
|
||||||
|
# # https://zipkin.io/pages/quickstart
|
||||||
|
# #######################################################
|
||||||
|
zipkin-all-in-one:
|
||||||
|
image: openzipkin/zipkin:latest
|
||||||
|
container_name: zipkin-all-in-one
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "9411:9411"
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # otel-collector
|
||||||
|
# # https://opentelemetry.io/docs/collector/installation/
|
||||||
|
# # https://github.com/open-telemetry/opentelemetry-collector
|
||||||
|
# # https://github.com/open-telemetry/opentelemetry-collector-contrib
|
||||||
|
# # we can use none contrib docker `otel/opentelemetry-collector` version from `https://github.com/open-telemetry/opentelemetry-collector` repository but,
|
||||||
|
# # if we need more components like `elasticsearch` we should use `otel/opentelemetry-collector-contrib` image of `https://github.com/open-telemetry/opentelemetry-collector-contrib` repository.
|
||||||
|
# #######################################################
|
||||||
|
otel-collector:
|
||||||
|
image: otel/opentelemetry-collector-contrib:latest
|
||||||
|
container_name: otel-collector
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["--config=/etc/otelcol-contrib/config.yaml"]
|
||||||
|
volumes:
|
||||||
|
- ./../configs/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
|
||||||
|
ports:
|
||||||
|
- "11888:1888" # pprof extension
|
||||||
|
- "8888:8888" # Prometheus metrics exposed by the Collector
|
||||||
|
- "8889:8889" # Prometheus exporter metrics
|
||||||
|
- "13133:13133" # health_check extension
|
||||||
|
- "4317:4317" # OTLP gRPC receiver
|
||||||
|
- "4318:4318" # OTLP http receiver
|
||||||
|
- "55679:55679" # zpages extension
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # prometheus
|
||||||
|
# # https://prometheus.io/docs/introduction/first_steps/
|
||||||
|
# # https://prometheus.io/docs/prometheus/3.1/installation/
|
||||||
|
# # https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
|
||||||
|
# #######################################################
|
||||||
|
prometheus:
|
||||||
|
image: prom/prometheus:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "9090:9090"
|
||||||
|
volumes:
|
||||||
|
- ./../configs/prometheus.yaml:/etc/prometheus/prometheus.yml
|
||||||
|
# to passe one flag, such as "--log.level=debug" or "--web.enable-remote-write-receiver", we need to override the whole command, as we can't just pass one extra argument
|
||||||
|
command:
|
||||||
|
- "--config.file=/etc/prometheus/prometheus.yml"
|
||||||
|
- "--storage.tsdb.path=/prometheus"
|
||||||
|
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
|
||||||
|
- "--web.console.templates=/usr/share/prometheus/consoles"
|
||||||
|
# need this for the OpenTelemetry collector to be able to put metrics into Prometheus
|
||||||
|
- "--web.enable-remote-write-receiver"
|
||||||
|
# - "--log.level=debug"
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # node-exporter
|
||||||
|
# # https://prometheus.io/docs/guides/node-exporter/
|
||||||
|
# # https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
|
||||||
|
# #######################################################
|
||||||
|
node-exporter:
|
||||||
|
image: prom/node-exporter:latest
|
||||||
|
container_name: node-exporter
|
||||||
|
restart: unless-stopped
|
||||||
|
volumes:
|
||||||
|
- /proc:/host/proc:ro
|
||||||
|
- /sys:/host/sys:ro
|
||||||
|
- /:/rootfs:ro
|
||||||
|
command:
|
||||||
|
- '--path.procfs=/host/proc'
|
||||||
|
- '--path.rootfs=/rootfs'
|
||||||
|
- '--path.sysfs=/host/sys'
|
||||||
|
ports:
|
||||||
|
- "9101:9100"
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # grafana
|
||||||
|
# # https://grafana.com/docs/grafana/latest/administration/provisioning/
|
||||||
|
# # https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/
|
||||||
|
# # https://grafana.com/docs/grafana/latest/setup-grafana/configure-docker/
|
||||||
|
# # https://github.com/grafana/intro-to-mltp/blob/main/grafana/provisioning/datasources/datasources.yaml
|
||||||
|
# #######################################################
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
container_name: grafana
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
|
||||||
|
- GF_SECURITY_ADMIN_USER=admin
|
||||||
|
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||||
|
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
|
||||||
|
# - GF_AUTH_ANONYMOUS_ENABLED=true
|
||||||
|
# - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
|
||||||
|
# - GF_AUTH_DISABLE_LOGIN_FORM=true
|
||||||
|
depends_on:
|
||||||
|
- prometheus
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- ./../configs/grafana/provisioning:/etc/grafana/provisioning
|
||||||
|
- ./../configs/grafana/dashboards:/var/lib/grafana/dashboards
|
||||||
|
## https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/
|
||||||
|
# - ./../configs/grafana/grafana.ini:/etc/grafana/grafana.ini
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # tempo
|
||||||
|
# # https://github.com/grafana/tempo/blob/main/example/docker-compose/otel-collector/docker-compose.yaml
|
||||||
|
# # https://github.com/grafana/tempo/blob/main/example/docker-compose/shared
|
||||||
|
# # https://github.com/grafana/tempo/blob/main/example/docker-compose/local
|
||||||
|
# # https://github.com/grafana/tempo/tree/main/example/docker-compose
|
||||||
|
# #######################################################
|
||||||
|
tempo:
|
||||||
|
image: grafana/tempo:latest
|
||||||
|
container_name: tempo
|
||||||
|
restart: unless-stopped
|
||||||
|
command: [ "-config.file=/etc/tempo.yaml" ]
|
||||||
|
volumes:
|
||||||
|
- ./../configs/tempo.yaml:/etc/tempo.yaml
|
||||||
|
ports:
|
||||||
|
- "3200" # tempo
|
||||||
|
- "24317:4317" # otlp grpc
|
||||||
|
- "24318:4318" # otlp http
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # loki
|
||||||
|
# # https://grafana.com/docs/opentelemetry/collector/send-logs-to-loki/
|
||||||
|
# # https://github.com/grafana/loki/blob/main/production/docker-compose.yaml
|
||||||
|
# # https://github.com/grafana/loki/blob/main/examples/getting-started/docker-compose.yaml
|
||||||
|
# #######################################################
|
||||||
|
loki:
|
||||||
|
image: grafana/loki:latest
|
||||||
|
hostname: loki
|
||||||
|
container_name: loki
|
||||||
|
ports:
|
||||||
|
- "3100:3100"
|
||||||
|
command: -config.file=/etc/loki/local-config.yaml
|
||||||
|
volumes:
|
||||||
|
- ./../configs/loki-config.yaml:/etc/loki/local-config.yaml
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # elasticsearch
|
||||||
|
# # https://www.elastic.co/guide/en/elasticsearch/reference/7.17/docker.html#docker-compose-file
|
||||||
|
# #######################################################
|
||||||
|
elasticsearch:
|
||||||
|
container_name: elasticsearch
|
||||||
|
restart: unless-stopped
|
||||||
|
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
|
||||||
|
environment:
|
||||||
|
- discovery.type=single-node
|
||||||
|
- cluster.name=docker-cluster
|
||||||
|
- node.name=docker-node
|
||||||
|
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
||||||
|
- xpack.security.enabled=false
|
||||||
|
- xpack.security.http.ssl.enabled=false
|
||||||
|
- xpack.security.transport.ssl.enabled=false
|
||||||
|
- network.host=0.0.0.0
|
||||||
|
- http.port=9200
|
||||||
|
- transport.host=localhost
|
||||||
|
- bootstrap.memory_lock=true
|
||||||
|
- cluster.routing.allocation.disk.threshold_enabled=false
|
||||||
|
ulimits:
|
||||||
|
memlock:
|
||||||
|
soft: -1
|
||||||
|
hard: -1
|
||||||
|
volumes:
|
||||||
|
- elastic-data:/usr/share/elasticsearch/data
|
||||||
|
ports:
|
||||||
|
- ${ELASTIC_HOST_PORT:-9200}:${ELASTIC_PORT:-9200}
|
||||||
|
- 9300:9300
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # kibana
|
||||||
|
# # https://www.elastic.co/guide/en/kibana/current/docker.html
|
||||||
|
# #######################################################
|
||||||
|
kibana:
|
||||||
|
image: docker.elastic.co/kibana/kibana:8.17.0
|
||||||
|
container_name: kibana
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
|
||||||
|
ports:
|
||||||
|
- ${KIBANA_HOST_PORT:-5601}:${KIBANA_PORT:-5601}
|
||||||
|
depends_on:
|
||||||
|
- elasticsearch
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
# #######################################################
|
||||||
|
# # cadvisor
|
||||||
|
# #######################################################
|
||||||
|
cadvisor:
|
||||||
|
image: gcr.io/cadvisor/cadvisor:latest
|
||||||
|
container_name: cadvisor
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
volumes:
|
||||||
|
- /:/rootfs:ro
|
||||||
|
- /var/run:/var/run:ro
|
||||||
|
- /sys:/sys:ro
|
||||||
|
- /var/lib/docker/:/var/lib/docker:ro
|
||||||
|
- /dev/disk/:/dev/disk:ro
|
||||||
|
devices:
|
||||||
|
- /dev/kmsg
|
||||||
|
networks:
|
||||||
|
- infrastructure
|
||||||
|
|
||||||
|
|
||||||
|
networks:
|
||||||
|
infrastructure:
|
||||||
|
name: infrastructure
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
elastic-data:
|
||||||
|
postgres-data:
|
||||||
@ -1,79 +1,49 @@
|
|||||||
version: "3.3"
|
name: booking-microservices
|
||||||
|
|
||||||
services:
|
services:
|
||||||
|
|
||||||
#######################################################
|
#######################################################
|
||||||
# Postgres
|
# rabbitmq
|
||||||
######################################################
|
#######################################################
|
||||||
postgres:
|
|
||||||
image: postgres:latest
|
|
||||||
container_name: postgres
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- '5432:5432'
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=postgres
|
|
||||||
- POSTGRES_PASSWORD=postgres
|
|
||||||
command:
|
|
||||||
- "postgres"
|
|
||||||
- "-c"
|
|
||||||
- "wal_level=logical"
|
|
||||||
- "-c"
|
|
||||||
- "max_prepared_transactions=10"
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# SqlServer
|
|
||||||
#######################################################
|
|
||||||
# sql:
|
|
||||||
# container_name: sql
|
|
||||||
# image: mcr.microsoft.com/mssql/server
|
|
||||||
# restart: unless-stopped
|
|
||||||
# ports:
|
|
||||||
# - "1433:1433"
|
|
||||||
# environment:
|
|
||||||
# SA_PASSWORD: "Password@1234"
|
|
||||||
# ACCEPT_EULA: "Y"
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Rabbitmq
|
|
||||||
#######################################################
|
|
||||||
rabbitmq:
|
rabbitmq:
|
||||||
container_name: rabbitmq
|
|
||||||
image: rabbitmq:management
|
image: rabbitmq:management
|
||||||
|
container_name: rabbitmq
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 5672:5672
|
- "5672:5672"
|
||||||
- 15672:15672
|
- "15672:15672"
|
||||||
|
# volumes:
|
||||||
|
# - rabbitmq:/var/lib/rabbitmq
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# postgres
|
||||||
|
#######################################################
|
||||||
|
postgres:
|
||||||
|
image: postgres:latest
|
||||||
|
container_name: postgres
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '5432:5432'
|
||||||
|
environment:
|
||||||
|
- POSTGRES_USER=postgres
|
||||||
|
- POSTGRES_PASSWORD=postgres
|
||||||
|
command:
|
||||||
|
- "postgres"
|
||||||
|
- "-c"
|
||||||
|
- "wal_level=logical"
|
||||||
|
- "-c"
|
||||||
|
- "max_prepared_transactions=10"
|
||||||
|
volumes:
|
||||||
|
- postgres-data:/var/lib/postgresql/data
|
||||||
networks:
|
networks:
|
||||||
- booking
|
- booking
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
#######################################################
|
||||||
# Jaeger
|
# EventStoreDB
|
||||||
#######################################################
|
#######################################################
|
||||||
jaeger:
|
|
||||||
image: jaegertracing/all-in-one
|
|
||||||
container_name: jaeger
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
ports:
|
|
||||||
- 5775:5775/udp
|
|
||||||
- 5778:5778
|
|
||||||
- 6831:6831/udp
|
|
||||||
- 6832:6832/udp
|
|
||||||
- 9411:9411
|
|
||||||
- 14268:14268
|
|
||||||
- 16686:16686
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# EventStoreDB
|
|
||||||
#######################################################
|
|
||||||
eventstore:
|
eventstore:
|
||||||
container_name: eventstore
|
container_name: eventstore
|
||||||
image: eventstore/eventstore:latest
|
image: eventstore/eventstore:latest
|
||||||
@ -91,9 +61,9 @@ services:
|
|||||||
- booking
|
- booking
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
#######################################################
|
||||||
# Mongo
|
# Mongo
|
||||||
#######################################################
|
#######################################################
|
||||||
mongo:
|
mongo:
|
||||||
image: mongo:latest
|
image: mongo:latest
|
||||||
container_name: mongo
|
container_name: mongo
|
||||||
@ -101,93 +71,260 @@ services:
|
|||||||
# environment:
|
# environment:
|
||||||
# - MONGO_INITDB_ROOT_USERNAME=root
|
# - MONGO_INITDB_ROOT_USERNAME=root
|
||||||
# - MONGO_INITDB_ROOT_PASSWORD=secret
|
# - MONGO_INITDB_ROOT_PASSWORD=secret
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
ports:
|
ports:
|
||||||
- 27017:27017
|
- 27017:27017
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Elastic Search
|
#######################################################
|
||||||
#######################################################
|
# Redis
|
||||||
elasticsearch:
|
#######################################################
|
||||||
container_name: elasticsearch
|
redis:
|
||||||
image: elasticsearch:7.17.9
|
image: redis
|
||||||
|
container_name: redis
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- 9200:9200
|
- 6379:6379
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# jaeger
|
||||||
|
# https://www.jaegertracing.io/docs/1.64/deployment/
|
||||||
|
# https://www.jaegertracing.io/docs/1.6/deployment/
|
||||||
|
#######################################################
|
||||||
|
jaeger-all-in-one:
|
||||||
|
image: jaegertracing/all-in-one:latest
|
||||||
|
container_name: jaeger-all-in-one
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "6831:6831/udp" # UDP port for Jaeger agent
|
||||||
|
- "16686:16686" # endpoints and Jaeger UI
|
||||||
|
- "14268:14268" # HTTP port for accept trace spans directly from clients
|
||||||
|
- "14317:4317" # OTLP gRPC receiver for jaeger
|
||||||
|
- "14318:4318" # OTLP http receiver for jaeger
|
||||||
|
# - "9411" # Accepts Zipkin spans - /api/v2/spans
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# zipkin
|
||||||
|
# https://zipkin.io/pages/quickstart
|
||||||
|
#######################################################
|
||||||
|
zipkin-all-in-one:
|
||||||
|
image: openzipkin/zipkin:latest
|
||||||
|
container_name: zipkin-all-in-one
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "9411:9411"
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# otel-collector
|
||||||
|
# https://opentelemetry.io/docs/collector/installation/
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector
|
||||||
|
# https://github.com/open-telemetry/opentelemetry-collector-contrib
|
||||||
|
# we can use none contrib docker `otel/opentelemetry-collector` version from `https://github.com/open-telemetry/opentelemetry-collector` repository but,
|
||||||
|
# if we need more components like `elasticsearch` we should use `otel/opentelemetry-collector-contrib` image of `https://github.com/open-telemetry/opentelemetry-collector-contrib` repository.
|
||||||
|
#######################################################
|
||||||
|
otel-collector:
|
||||||
|
image: otel/opentelemetry-collector-contrib:latest
|
||||||
|
container_name: otel-collector
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["--config=/etc/otelcol-contrib/config.yaml"]
|
||||||
volumes:
|
volumes:
|
||||||
- elasticsearch-data:/usr/share/elasticsearch/data
|
- ./../configs/otel-collector-config.yaml:/etc/otelcol-contrib/config.yaml
|
||||||
environment:
|
|
||||||
- xpack.monitoring.enabled=true
|
|
||||||
- xpack.watcher.enabled=false
|
|
||||||
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
|
||||||
- discovery.type=single-node
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Kibana
|
|
||||||
#######################################################
|
|
||||||
kibana:
|
|
||||||
container_name: kibana
|
|
||||||
image: kibana:7.17.9
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
ports:
|
||||||
- 5601:5601
|
- "11888:1888" # pprof extension
|
||||||
depends_on:
|
- "8888:8888" # Prometheus metrics exposed by the Collector
|
||||||
- elasticsearch
|
- "8889:8889" # Prometheus exporter metrics
|
||||||
environment:
|
- "13133:13133" # health_check extension
|
||||||
- ELASTICSEARCH_URL=elasticsearch:9200
|
- "4317:4317" # OTLP gRPC receiver
|
||||||
|
- "4318:4318" # OTLP http receiver
|
||||||
|
- "55679:55679" # zpages extension
|
||||||
networks:
|
networks:
|
||||||
- booking
|
- booking
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# prometheus
|
#######################################################
|
||||||
#######################################################
|
# prometheus
|
||||||
|
# https://prometheus.io/docs/introduction/first_steps/
|
||||||
|
# https://prometheus.io/docs/prometheus/3.1/installation/
|
||||||
|
# https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
|
||||||
|
#######################################################
|
||||||
prometheus:
|
prometheus:
|
||||||
image: prom/prometheus:latest
|
image: prom/prometheus:latest
|
||||||
container_name: prometheus
|
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
ports:
|
ports:
|
||||||
- "9090:9090"
|
- "9090:9090"
|
||||||
environment:
|
|
||||||
- TZ=UTC
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./monitoring/prom/prometheus.yml:/etc/prometheus/prometheus.yml
|
- ./../configs/prometheus.yaml:/etc/prometheus/prometheus.yml
|
||||||
networks:
|
# to passe one flag, such as "--log.level=debug" or "--web.enable-remote-write-receiver", we need to override the whole command, as we can't just pass one extra argument
|
||||||
- booking
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# grafana
|
|
||||||
#######################################################
|
|
||||||
grafana:
|
|
||||||
image: grafana/grafana
|
|
||||||
container_name: grafana
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
volumes:
|
|
||||||
- ./monitoring/grafana-data/data:/var/lib/grafana
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# node_exporter
|
|
||||||
#######################################################
|
|
||||||
node_exporter:
|
|
||||||
image: quay.io/prometheus/node-exporter:latest
|
|
||||||
container_name: node_exporter
|
|
||||||
restart: unless-stopped
|
|
||||||
command:
|
command:
|
||||||
- '--path.rootfs=/host'
|
- "--config.file=/etc/prometheus/prometheus.yml"
|
||||||
pid: host
|
- "--storage.tsdb.path=/prometheus"
|
||||||
|
- "--web.console.libraries=/usr/share/prometheus/console_libraries"
|
||||||
|
- "--web.console.templates=/usr/share/prometheus/consoles"
|
||||||
|
# need this for the OpenTelemetry collector to be able to put metrics into Prometheus
|
||||||
|
- "--web.enable-remote-write-receiver"
|
||||||
|
# - "--log.level=debug"
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# node-exporter
|
||||||
|
# https://prometheus.io/docs/guides/node-exporter/
|
||||||
|
# https://grafana.com/docs/grafana-cloud/send-data/metrics/metrics-prometheus/prometheus-config-examples/docker-compose-linux/
|
||||||
|
#######################################################
|
||||||
|
node-exporter:
|
||||||
|
image: prom/node-exporter:latest
|
||||||
|
container_name: node-exporter
|
||||||
|
restart: unless-stopped
|
||||||
volumes:
|
volumes:
|
||||||
- /proc:/host/proc:ro
|
- /proc:/host/proc:ro
|
||||||
- /sys:/host/sys:ro
|
- /sys:/host/sys:ro
|
||||||
- /:/rootfs:ro
|
- /:/rootfs:ro
|
||||||
|
command:
|
||||||
|
- '--path.procfs=/host/proc'
|
||||||
|
- '--path.rootfs=/rootfs'
|
||||||
|
- '--path.sysfs=/host/sys'
|
||||||
|
ports:
|
||||||
|
- "9101:9100"
|
||||||
networks:
|
networks:
|
||||||
- booking
|
- booking
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# grafana
|
||||||
|
# https://grafana.com/docs/grafana/latest/administration/provisioning/
|
||||||
|
# https://grafana.com/docs/grafana/latest/setup-grafana/installation/docker/
|
||||||
|
# https://grafana.com/docs/grafana/latest/setup-grafana/configure-docker/
|
||||||
|
# https://github.com/grafana/intro-to-mltp/blob/main/grafana/provisioning/datasources/datasources.yaml
|
||||||
|
#######################################################
|
||||||
|
grafana:
|
||||||
|
image: grafana/grafana:latest
|
||||||
|
container_name: grafana
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- GF_INSTALL_PLUGINS=grafana-clock-panel,grafana-simple-json-datasource
|
||||||
|
- GF_SECURITY_ADMIN_USER=admin
|
||||||
|
- GF_SECURITY_ADMIN_PASSWORD=admin
|
||||||
|
- GF_FEATURE_TOGGLES_ENABLE=traceqlEditor
|
||||||
|
# - GF_AUTH_ANONYMOUS_ENABLED=true
|
||||||
|
# - GF_AUTH_ANONYMOUS_ORG_ROLE=Admin
|
||||||
|
# - GF_AUTH_DISABLE_LOGIN_FORM=true
|
||||||
|
depends_on:
|
||||||
|
- prometheus
|
||||||
|
ports:
|
||||||
|
- "3000:3000"
|
||||||
|
volumes:
|
||||||
|
- ./../configs/grafana/provisioning:/etc/grafana/provisioning
|
||||||
|
- ./../configs/grafana/dashboards:/var/lib/grafana/dashboards
|
||||||
|
## https://grafana.com/docs/grafana/latest/setup-grafana/configure-grafana/
|
||||||
|
# - ./../configs/grafana/grafana.ini:/etc/grafana/grafana.ini
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# tempo
|
||||||
|
# https://github.com/grafana/tempo/blob/main/example/docker-compose/otel-collector/docker-compose.yaml
|
||||||
|
# https://github.com/grafana/tempo/blob/main/example/docker-compose/shared
|
||||||
|
# https://github.com/grafana/tempo/blob/main/example/docker-compose/local
|
||||||
|
# https://github.com/grafana/tempo/tree/main/example/docker-compose
|
||||||
|
#######################################################
|
||||||
|
tempo:
|
||||||
|
image: grafana/tempo:latest
|
||||||
|
container_name: tempo
|
||||||
|
restart: unless-stopped
|
||||||
|
command: [ "-config.file=/etc/tempo.yaml" ]
|
||||||
|
volumes:
|
||||||
|
- ./../configs/tempo.yaml:/etc/tempo.yaml
|
||||||
|
ports:
|
||||||
|
- "3200" # tempo
|
||||||
|
- "24317:4317" # otlp grpc
|
||||||
|
- "24318:4318" # otlp http
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# loki
|
||||||
|
# https://grafana.com/docs/opentelemetry/collector/send-logs-to-loki/
|
||||||
|
# https://github.com/grafana/loki/blob/main/production/docker-compose.yaml
|
||||||
|
# https://github.com/grafana/loki/blob/main/examples/getting-started/docker-compose.yaml
|
||||||
|
#######################################################
|
||||||
|
loki:
|
||||||
|
image: grafana/loki:latest
|
||||||
|
hostname: loki
|
||||||
|
container_name: loki
|
||||||
|
ports:
|
||||||
|
- "3100:3100"
|
||||||
|
command: -config.file=/etc/loki/local-config.yaml
|
||||||
|
volumes:
|
||||||
|
- ./../configs/loki-config.yaml:/etc/loki/local-config.yaml
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# elasticsearch
|
||||||
|
# https://www.elastic.co/guide/en/elasticsearch/reference/7.17/docker.html#docker-compose-file
|
||||||
|
#######################################################
|
||||||
|
elasticsearch:
|
||||||
|
container_name: elasticsearch
|
||||||
|
restart: unless-stopped
|
||||||
|
image: docker.elastic.co/elasticsearch/elasticsearch:8.17.0
|
||||||
|
environment:
|
||||||
|
- discovery.type=single-node
|
||||||
|
- cluster.name=docker-cluster
|
||||||
|
- node.name=docker-node
|
||||||
|
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
||||||
|
- xpack.security.enabled=false
|
||||||
|
- xpack.security.http.ssl.enabled=false
|
||||||
|
- xpack.security.transport.ssl.enabled=false
|
||||||
|
- network.host=0.0.0.0
|
||||||
|
- http.port=9200
|
||||||
|
- transport.host=localhost
|
||||||
|
- bootstrap.memory_lock=true
|
||||||
|
- cluster.routing.allocation.disk.threshold_enabled=false
|
||||||
|
ulimits:
|
||||||
|
memlock:
|
||||||
|
soft: -1
|
||||||
|
hard: -1
|
||||||
|
volumes:
|
||||||
|
- elastic-data:/usr/share/elasticsearch/data
|
||||||
|
ports:
|
||||||
|
- ${ELASTIC_HOST_PORT:-9200}:${ELASTIC_PORT:-9200}
|
||||||
|
- 9300:9300
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
|
||||||
|
#######################################################
|
||||||
|
# kibana
|
||||||
|
# https://www.elastic.co/guide/en/kibana/current/docker.html
|
||||||
|
#######################################################
|
||||||
|
kibana:
|
||||||
|
image: docker.elastic.co/kibana/kibana:8.17.0
|
||||||
|
container_name: kibana
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
|
||||||
|
ports:
|
||||||
|
- ${KIBANA_HOST_PORT:-5601}:${KIBANA_PORT:-5601}
|
||||||
|
depends_on:
|
||||||
|
- elasticsearch
|
||||||
|
networks:
|
||||||
|
- booking
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
#######################################################
|
||||||
# cadvisor
|
# cadvisor
|
||||||
#######################################################
|
#######################################################
|
||||||
@ -208,17 +345,18 @@ services:
|
|||||||
networks:
|
networks:
|
||||||
- booking
|
- booking
|
||||||
|
|
||||||
|
|
||||||
######################################################
|
######################################################
|
||||||
# Gateway
|
# Api-Gateway
|
||||||
######################################################
|
######################################################
|
||||||
gateway:
|
api-gateway:
|
||||||
image: gateway
|
image: api-gateway
|
||||||
build:
|
build:
|
||||||
args:
|
args:
|
||||||
Version: "1"
|
Version: "1"
|
||||||
context: ../../
|
context: ../../
|
||||||
dockerfile: src/ApiGateway/dev.Dockerfile
|
dockerfile: src/ApiGateway/Dockerfile
|
||||||
container_name: gateway
|
container_name: api-gateway
|
||||||
ports:
|
ports:
|
||||||
- "5001:80"
|
- "5001:80"
|
||||||
- "5000:443"
|
- "5000:443"
|
||||||
@ -244,7 +382,7 @@ services:
|
|||||||
args:
|
args:
|
||||||
Version: "1"
|
Version: "1"
|
||||||
context: ../../
|
context: ../../
|
||||||
dockerfile: src/Services/Flight/dev.Dockerfile
|
dockerfile: src/Services/Flight/Dockerfile
|
||||||
container_name: flight
|
container_name: flight
|
||||||
ports:
|
ports:
|
||||||
- 5004:80
|
- 5004:80
|
||||||
@ -270,7 +408,7 @@ services:
|
|||||||
args:
|
args:
|
||||||
Version: "1"
|
Version: "1"
|
||||||
context: ../../
|
context: ../../
|
||||||
dockerfile: src/Services/Identity/dev.Dockerfile
|
dockerfile: src/Services/Identity/Dockerfile
|
||||||
container_name: identity
|
container_name: identity
|
||||||
ports:
|
ports:
|
||||||
- 6005:80
|
- 6005:80
|
||||||
@ -297,7 +435,7 @@ services:
|
|||||||
args:
|
args:
|
||||||
Version: "1"
|
Version: "1"
|
||||||
context: ../../
|
context: ../../
|
||||||
dockerfile: src/Services/Passenger/dev.Dockerfile
|
dockerfile: src/Services/Passenger/Dockerfile
|
||||||
container_name: passenger
|
container_name: passenger
|
||||||
ports:
|
ports:
|
||||||
- 6012:80
|
- 6012:80
|
||||||
@ -324,7 +462,7 @@ services:
|
|||||||
args:
|
args:
|
||||||
Version: "1"
|
Version: "1"
|
||||||
context: ../../
|
context: ../../
|
||||||
dockerfile: src/Services/Booking/dev.Dockerfile
|
dockerfile: src/Services/Booking/Dockerfile
|
||||||
container_name: booking
|
container_name: booking
|
||||||
ports:
|
ports:
|
||||||
- 6010:80
|
- 6010:80
|
||||||
@ -344,9 +482,11 @@ services:
|
|||||||
|
|
||||||
networks:
|
networks:
|
||||||
booking:
|
booking:
|
||||||
|
name: booking
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
elasticsearch-data:
|
elastic-data:
|
||||||
|
postgres-data:
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,234 +0,0 @@
|
|||||||
version: "3.3"
|
|
||||||
services:
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Rabbitmq
|
|
||||||
#######################################################
|
|
||||||
rabbitmq:
|
|
||||||
container_name: rabbitmq
|
|
||||||
image: rabbitmq:management
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- 5672:5672
|
|
||||||
- 15672:15672
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Postgres
|
|
||||||
######################################################
|
|
||||||
postgres:
|
|
||||||
image: postgres:latest
|
|
||||||
container_name: postgres
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- '5432:5432'
|
|
||||||
environment:
|
|
||||||
- POSTGRES_USER=postgres
|
|
||||||
- POSTGRES_PASSWORD=postgres
|
|
||||||
command:
|
|
||||||
- "postgres"
|
|
||||||
- "-c"
|
|
||||||
- "wal_level=logical"
|
|
||||||
- "-c"
|
|
||||||
- "max_prepared_transactions=10"
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# SqlServer
|
|
||||||
#######################################################
|
|
||||||
# sql:
|
|
||||||
# container_name: sql
|
|
||||||
# image: mcr.microsoft.com/mssql/server
|
|
||||||
# restart: unless-stopped
|
|
||||||
# ports:
|
|
||||||
# - "1433:1433"
|
|
||||||
# environment:
|
|
||||||
# SA_PASSWORD: "Password@1234"
|
|
||||||
# ACCEPT_EULA: "Y"
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Jaeger
|
|
||||||
#######################################################
|
|
||||||
jaeger:
|
|
||||||
container_name: jaeger
|
|
||||||
image: jaegertracing/all-in-one
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
ports:
|
|
||||||
- 5775:5775/udp
|
|
||||||
- 5778:5778
|
|
||||||
- 6831:6831/udp
|
|
||||||
- 6832:6832/udp
|
|
||||||
- 9411:9411
|
|
||||||
- 14268:14268
|
|
||||||
- 16686:16686
|
|
||||||
|
|
||||||
|
|
||||||
# #######################################################
|
|
||||||
# # EventStoreDB
|
|
||||||
# #######################################################
|
|
||||||
# #https://stackoverflow.com/questions/65272764/ports-are-not-available-listen-tcp-0-0-0-0-50070-bind-an-attempt-was-made-to
|
|
||||||
|
|
||||||
eventstore:
|
|
||||||
container_name: eventstore
|
|
||||||
image: eventstore/eventstore:latest
|
|
||||||
restart: unless-stopped
|
|
||||||
environment:
|
|
||||||
- EVENTSTORE_CLUSTER_SIZE=1
|
|
||||||
- EVENTSTORE_RUN_PROJECTIONS=All
|
|
||||||
- EVENTSTORE_START_STANDARD_PROJECTIONS=True
|
|
||||||
- EVENTSTORE_HTTP_PORT=2113
|
|
||||||
- EVENTSTORE_INSECURE=True
|
|
||||||
- EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP=True
|
|
||||||
ports:
|
|
||||||
- "2113:2113"
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Mongo
|
|
||||||
#######################################################
|
|
||||||
mongo:
|
|
||||||
image: mongo:latest
|
|
||||||
container_name: mongo
|
|
||||||
restart: unless-stopped
|
|
||||||
# environment:
|
|
||||||
# - MONGO_INITDB_ROOT_USERNAME=root
|
|
||||||
# - MONGO_INITDB_ROOT_PASSWORD=secret
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
ports:
|
|
||||||
- 27017:27017
|
|
||||||
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Elastic Search
|
|
||||||
#######################################################
|
|
||||||
elasticsearch:
|
|
||||||
container_name: elasticsearch
|
|
||||||
image: elasticsearch:7.17.9
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- 9200:9200
|
|
||||||
volumes:
|
|
||||||
- elasticsearch-data:/usr/share/elasticsearch/data
|
|
||||||
environment:
|
|
||||||
- xpack.monitoring.enabled=true
|
|
||||||
- xpack.watcher.enabled=false
|
|
||||||
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
|
|
||||||
- discovery.type=single-node
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Kibana
|
|
||||||
#######################################################
|
|
||||||
kibana:
|
|
||||||
container_name: kibana
|
|
||||||
image: kibana:7.17.9
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- 5601:5601
|
|
||||||
depends_on:
|
|
||||||
- elasticsearch
|
|
||||||
environment:
|
|
||||||
- ELASTICSEARCH_URL=http://localhost:9200
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# Redis
|
|
||||||
#######################################################
|
|
||||||
redis:
|
|
||||||
image: redis
|
|
||||||
container_name: redis
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
ports:
|
|
||||||
- 6379:6379
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# prometheus
|
|
||||||
#######################################################
|
|
||||||
prometheus:
|
|
||||||
image: prom/prometheus:latest
|
|
||||||
container_name: prometheus
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "9090:9090"
|
|
||||||
environment:
|
|
||||||
- TZ=UTC
|
|
||||||
volumes:
|
|
||||||
- ./monitoring/prom/prometheus.yml:/etc/prometheus/prometheus.yml
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# grafana
|
|
||||||
#######################################################
|
|
||||||
grafana:
|
|
||||||
image: grafana/grafana
|
|
||||||
container_name: grafana
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "3000:3000"
|
|
||||||
volumes:
|
|
||||||
- ./monitoring/grafana-data/data:/var/lib/grafana
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# node_exporter
|
|
||||||
#######################################################
|
|
||||||
node_exporter:
|
|
||||||
image: quay.io/prometheus/node-exporter:latest
|
|
||||||
container_name: node_exporter
|
|
||||||
restart: unless-stopped
|
|
||||||
command:
|
|
||||||
- '--path.rootfs=/host'
|
|
||||||
pid: host
|
|
||||||
volumes:
|
|
||||||
- /proc:/host/proc:ro
|
|
||||||
- /sys:/host/sys:ro
|
|
||||||
- /:/rootfs:ro
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
#######################################################
|
|
||||||
# cadvisor
|
|
||||||
#######################################################
|
|
||||||
cadvisor:
|
|
||||||
image: gcr.io/cadvisor/cadvisor:latest
|
|
||||||
container_name: cadvisor
|
|
||||||
restart: unless-stopped
|
|
||||||
ports:
|
|
||||||
- "8080:8080"
|
|
||||||
volumes:
|
|
||||||
- /:/rootfs:ro
|
|
||||||
- /var/run:/var/run:ro
|
|
||||||
- /sys:/sys:ro
|
|
||||||
- /var/lib/docker/:/var/lib/docker:ro
|
|
||||||
- /dev/disk/:/dev/disk:ro
|
|
||||||
devices:
|
|
||||||
- /dev/kmsg
|
|
||||||
networks:
|
|
||||||
- booking
|
|
||||||
|
|
||||||
|
|
||||||
networks:
|
|
||||||
booking:
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
elasticsearch-data:
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,69 +0,0 @@
|
|||||||
global:
|
|
||||||
scrape_interval: 15s
|
|
||||||
scrape_timeout: 10s
|
|
||||||
evaluation_interval: 15s
|
|
||||||
alerting:
|
|
||||||
alertmanagers:
|
|
||||||
- scheme: http
|
|
||||||
timeout: 10s
|
|
||||||
api_version: v1
|
|
||||||
static_configs:
|
|
||||||
- targets: []
|
|
||||||
scrape_configs:
|
|
||||||
- job_name: prometheus
|
|
||||||
honor_timestamps: true
|
|
||||||
scrape_interval: 15s
|
|
||||||
scrape_timeout: 10s
|
|
||||||
metrics_path: /metrics
|
|
||||||
scheme: http
|
|
||||||
static_configs:
|
|
||||||
- targets:
|
|
||||||
- localhost:9090
|
|
||||||
- job_name: cadvisor
|
|
||||||
static_configs:
|
|
||||||
- targets:
|
|
||||||
- cadvisor:8080
|
|
||||||
- job_name: node_exporter
|
|
||||||
static_configs:
|
|
||||||
- targets:
|
|
||||||
- node_exporter:9100
|
|
||||||
- job_name: flight
|
|
||||||
scrape_interval: 15s
|
|
||||||
scrape_timeout: 10s
|
|
||||||
metrics_path: /metrics
|
|
||||||
scheme: http
|
|
||||||
static_configs:
|
|
||||||
- targets:
|
|
||||||
- host.docker.internal:5004
|
|
||||||
- job_name: identity
|
|
||||||
scrape_interval: 15s
|
|
||||||
scrape_timeout: 10s
|
|
||||||
metrics_path: /metrics
|
|
||||||
scheme: http
|
|
||||||
static_configs:
|
|
||||||
- targets:
|
|
||||||
- host.docker.internal:6005
|
|
||||||
- job_name: passenger
|
|
||||||
scrape_interval: 15s
|
|
||||||
scrape_timeout: 10s
|
|
||||||
metrics_path: /metrics
|
|
||||||
scheme: http
|
|
||||||
static_configs:
|
|
||||||
- targets:
|
|
||||||
- host.docker.internal:6012
|
|
||||||
- job_name: booking
|
|
||||||
scrape_interval: 15s
|
|
||||||
scrape_timeout: 10s
|
|
||||||
metrics_path: /metrics
|
|
||||||
scheme: http
|
|
||||||
static_configs:
|
|
||||||
- targets:
|
|
||||||
- host.docker.internal:6010
|
|
||||||
- job_name: gateway
|
|
||||||
scrape_interval: 15s
|
|
||||||
scrape_timeout: 10s
|
|
||||||
metrics_path: /metrics
|
|
||||||
scheme: https
|
|
||||||
static_configs:
|
|
||||||
- targets:
|
|
||||||
- host.docker.internal:5000
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,45 +0,0 @@
|
|||||||
name: Booking
|
|
||||||
services:
|
|
||||||
- name: booking-gateway
|
|
||||||
project: ./../../src/ApiGateway/src/ApiGateway.csproj
|
|
||||||
bindings:
|
|
||||||
- port: 5001
|
|
||||||
env:
|
|
||||||
- name: ASPNETCORE_ENVIRONMENT
|
|
||||||
value: development
|
|
||||||
|
|
||||||
|
|
||||||
- name: flight
|
|
||||||
project: ./../../src/Services/Flight/src/Flight.Api/Flight.Api.csproj
|
|
||||||
bindings:
|
|
||||||
- port: 5003
|
|
||||||
env:
|
|
||||||
- name: ASPNETCORE_ENVIRONMENT
|
|
||||||
value: development
|
|
||||||
|
|
||||||
|
|
||||||
- name: identity
|
|
||||||
project: ./../../src/Services/Identity/src/Identity.Api/Identity.Api.csproj
|
|
||||||
bindings:
|
|
||||||
- port: 5005
|
|
||||||
env:
|
|
||||||
- name: ASPNETCORE_ENVIRONMENT
|
|
||||||
value: development
|
|
||||||
|
|
||||||
|
|
||||||
- name: passenger
|
|
||||||
project: ./../../src/Services/Passenger/src/Passenger.Api/Passenger.Api.csproj
|
|
||||||
bindings:
|
|
||||||
- port: 5012
|
|
||||||
env:
|
|
||||||
- name: ASPNETCORE_ENVIRONMENT
|
|
||||||
value: development
|
|
||||||
|
|
||||||
|
|
||||||
- name: booking
|
|
||||||
project: ./../../src/Services/Booking/src/Booking.Api/Booking.Api.csproj
|
|
||||||
bindings:
|
|
||||||
- port: 5010
|
|
||||||
env:
|
|
||||||
- name: ASPNETCORE_ENVIRONMENT
|
|
||||||
value: development
|
|
||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "8.0.401",
|
"version": "10.0.103",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestFeature"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "booking-microservices",
|
"name": "monolith-to-cloud-architecture",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "booking-microservices",
|
"name": "monolith-to-cloud-architecture",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -7,8 +7,8 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "husky && dotnet tool restore",
|
"prepare": "husky && dotnet tool restore",
|
||||||
"format": "dotnet format booking-microservices-sample.sln --severity error --verbosity detailed",
|
"format": "dotnet tool run dotnet-csharpier booking-microservices.sln",
|
||||||
"ci-format": "dotnet format booking-microservices-sample.sln --verify-no-changes --severity error --verbosity detailed",
|
"ci-format": "dotnet tool run dotnet-csharpier booking-microservices.sln --check",
|
||||||
"upgrade-packages": "dotnet outdated --upgrade"
|
"upgrade-packages": "dotnet outdated --upgrade"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -1,45 +1,38 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
|
# ---------- Build Stage ----------
|
||||||
WORKDIR /
|
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||||
|
WORKDIR /src
|
||||||
|
|
||||||
COPY ./.editorconfig ./
|
# Copy solution-level files
|
||||||
COPY ./global.json ./
|
COPY .editorconfig .
|
||||||
COPY ./Directory.Build.props ./
|
COPY global.json .
|
||||||
|
COPY Directory.Build.props .
|
||||||
|
|
||||||
# Setup working directory for the project
|
# Copy project files first (better Docker caching)
|
||||||
COPY ./src/BuildingBlocks/BuildingBlocks.csproj ./BuildingBlocks/
|
COPY src/BuildingBlocks/BuildingBlocks.csproj src/BuildingBlocks/
|
||||||
COPY ./src/ApiGateway/src/ApiGateway.csproj ./ApiGateway/src/
|
COPY src/ApiGateway/src/ApiGateway.csproj src/ApiGateway/src/
|
||||||
|
COPY src/Aspire/src/ServiceDefaults/ServiceDefaults.csproj src/Aspire/src/ServiceDefaults/
|
||||||
|
|
||||||
|
# Restore dependencies
|
||||||
|
RUN dotnet restore src/ApiGateway/src/ApiGateway.csproj
|
||||||
|
|
||||||
# Restore nuget packages
|
# Copy the rest of the source code
|
||||||
RUN dotnet restore ./ApiGateway/src/ApiGateway.csproj
|
COPY src ./src
|
||||||
|
|
||||||
# Copy project files
|
# Publish (build included)
|
||||||
COPY ./src/BuildingBlocks ./BuildingBlocks/
|
RUN dotnet publish src/ApiGateway/src/ApiGateway.csproj \
|
||||||
COPY ./src/ApiGateway/src ./ApiGateway/src/
|
-c Release \
|
||||||
|
-o /app/publish \
|
||||||
|
--no-restore
|
||||||
|
|
||||||
# Build project with Release configuration
|
# ---------- Runtime Stage ----------
|
||||||
# and no restore, as we did it already
|
FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
RUN ls
|
COPY --from=build /app/publish .
|
||||||
RUN dotnet build -c Release --no-restore ./ApiGateway/src/ApiGateway.csproj
|
|
||||||
|
|
||||||
WORKDIR /ApiGateway/src
|
ENV ASPNETCORE_URLS=http://+:80
|
||||||
|
ENV ASPNETCORE_ENVIRONMENT=docker
|
||||||
# Publish project to output folder
|
|
||||||
# and no build, as we did it already
|
|
||||||
RUN dotnet publish -c Release --no-build -o out
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
|
||||||
|
|
||||||
# Setup working directory for the project
|
|
||||||
WORKDIR /
|
|
||||||
COPY --from=builder /src/ApiGateway/src/out .
|
|
||||||
|
|
||||||
ENV ASPNETCORE_URLS https://*:443, http://*:80
|
|
||||||
ENV ASPNETCORE_ENVIRONMENT docker
|
|
||||||
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
EXPOSE 443
|
|
||||||
|
|
||||||
ENTRYPOINT ["dotnet", "ApiGateway.dll"]
|
ENTRYPOINT ["dotnet", "ApiGateway.dll"]
|
||||||
|
|
||||||
|
|||||||
@ -1,48 +0,0 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS builder
|
|
||||||
WORKDIR /
|
|
||||||
|
|
||||||
COPY ./.editorconfig ./
|
|
||||||
COPY ./global.json ./
|
|
||||||
COPY ./Directory.Build.props ./
|
|
||||||
|
|
||||||
# Setup working directory for the project
|
|
||||||
COPY ./src/BuildingBlocks/BuildingBlocks.csproj ./BuildingBlocks/
|
|
||||||
COPY ./src/ApiGateway/src/ApiGateway.csproj ./ApiGateway/src/
|
|
||||||
|
|
||||||
|
|
||||||
# Restore nuget packages
|
|
||||||
RUN --mount=type=cache,id=gateway_nuget,target=/root/.nuget/packages \
|
|
||||||
dotnet restore ./ApiGateway/src/ApiGateway.csproj
|
|
||||||
|
|
||||||
# Copy project files
|
|
||||||
COPY ./src/BuildingBlocks ./BuildingBlocks/
|
|
||||||
COPY ./src/ApiGateway/src ./ApiGateway/src/
|
|
||||||
|
|
||||||
# Build project with Release configuration
|
|
||||||
# and no restore, as we did it already
|
|
||||||
|
|
||||||
RUN ls
|
|
||||||
RUN --mount=type=cache,id=gateway_nuget,target=/root/.nuget/packages \
|
|
||||||
dotnet build -c Release --no-restore ./ApiGateway/src/ApiGateway.csproj
|
|
||||||
|
|
||||||
WORKDIR /ApiGateway/src
|
|
||||||
|
|
||||||
# Publish project to output folder
|
|
||||||
# and no build, as we did it already
|
|
||||||
RUN --mount=type=cache,id=gateway_nuget,target=/root/.nuget/packages \
|
|
||||||
dotnet publish -c Release --no-build -o out
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:8.0
|
|
||||||
|
|
||||||
# Setup working directory for the project
|
|
||||||
WORKDIR /
|
|
||||||
COPY --from=builder /src/ApiGateway/src/out .
|
|
||||||
|
|
||||||
ENV ASPNETCORE_URLS https://*:443, http://*:80
|
|
||||||
ENV ASPNETCORE_ENVIRONMENT docker
|
|
||||||
|
|
||||||
EXPOSE 80
|
|
||||||
EXPOSE 443
|
|
||||||
|
|
||||||
ENTRYPOINT ["dotnet", "ApiGateway.dll"]
|
|
||||||
|
|
||||||
@ -1,7 +1,5 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\BuildingBlocks\BuildingBlocks.csproj" />
|
<ProjectReference Include="..\..\BuildingBlocks\BuildingBlocks.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
using BuildingBlocks.Logging;
|
|
||||||
using BuildingBlocks.Web;
|
using BuildingBlocks.Web;
|
||||||
using Figgle;
|
using Figgle;
|
||||||
using Serilog;
|
using Figgle.Fonts;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
var env = builder.Environment;
|
var env = builder.Environment;
|
||||||
@ -9,8 +8,6 @@ var appOptions = builder.Services.GetOptions<AppOptions>("AppOptions");
|
|||||||
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
|
Console.WriteLine(FiggleFonts.Standard.Render(appOptions.Name));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
builder.AddCustomSerilog(env);
|
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
builder.Services.AddHttpContextAccessor();
|
builder.Services.AddHttpContextAccessor();
|
||||||
|
|
||||||
@ -18,7 +15,6 @@ builder.Services.AddReverseProxy().LoadFromConfig(builder.Configuration.GetSecti
|
|||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.UseSerilogRequestLogging();
|
|
||||||
app.UseCorrelationId();
|
app.UseCorrelationId();
|
||||||
app.UseRouting();
|
app.UseRouting();
|
||||||
app.UseHttpsRedirection();
|
app.UseHttpsRedirection();
|
||||||
|
|||||||
@ -1,13 +1,5 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
"iisSettings": {
|
|
||||||
"windowsAuthentication": false,
|
|
||||||
"anonymousAuthentication": true,
|
|
||||||
"iisExpress": {
|
|
||||||
"applicationUrl": "http://localhost:17191",
|
|
||||||
"sslPort": 44352
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"ApiGateway": {
|
"ApiGateway": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
@ -17,14 +9,6 @@
|
|||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"IIS Express": {
|
|
||||||
"commandName": "IISExpress",
|
|
||||||
"launchBrowser": true,
|
|
||||||
"launchUrl": "swagger",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
33
src/Aspire/src/AppHost/AppHost.csproj
Normal file
33
src/Aspire/src/AppHost/AppHost.csproj
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<Sdk Name="Aspire.AppHost.SDK" Version="13.1.1"/>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsAspireHost>true</IsAspireHost>
|
||||||
|
<UserSecretsId>bde28db3-85ba-4201-b889-0f3faba24169</UserSecretsId>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Aspire.Hosting.AppHost" Version="13.1.1" />
|
||||||
|
<PackageReference Include="Aspire.Hosting.Docker" Version="13.1.1-preview.1.26105.8" />
|
||||||
|
<PackageReference Include="Aspire.Hosting.MongoDB" Version="13.1.1" />
|
||||||
|
<PackageReference Include="Aspire.Hosting.PostgreSQL" Version="13.1.1" />
|
||||||
|
<PackageReference Include="Aspire.Hosting.RabbitMQ" Version="13.1.1" />
|
||||||
|
<PackageReference Include="Aspire.Hosting.Redis" Version="13.1.1" />
|
||||||
|
<PackageReference Include="CommunityToolkit.Aspire.Hosting.EventStore" Version="9.9.0" />
|
||||||
|
<PackageReference Include="Elastic.Aspire.Hosting.Elasticsearch" Version="9.3.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\ApiGateway\src\ApiGateway.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\Services\Booking\src\Booking.Api\Booking.Api.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\Services\Flight\src\Flight.Api\Flight.Api.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\Services\Identity\src\Identity.Api\Identity.Api.csproj" />
|
||||||
|
<ProjectReference Include="..\..\..\Services\Passenger\src\Passenger.Api\Passenger.Api.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
366
src/Aspire/src/AppHost/Program.cs
Normal file
366
src/Aspire/src/AppHost/Program.cs
Normal file
@ -0,0 +1,366 @@
|
|||||||
|
using System.Net.Sockets;
|
||||||
|
|
||||||
|
var builder = DistributedApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.AddDockerComposeEnvironment("docker-compose");
|
||||||
|
|
||||||
|
// 1. Database Services
|
||||||
|
var pgUsername = builder.AddParameter("pg-username", "postgres", secret: true);
|
||||||
|
var pgPassword = builder.AddParameter("pg-password", "postgres", secret: true);
|
||||||
|
|
||||||
|
var postgres = builder.AddPostgres("postgres", pgUsername, pgPassword)
|
||||||
|
.WithImage("postgres:latest")
|
||||||
|
.WithEndpoint(
|
||||||
|
"tcp",
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
e.Port = 5432;
|
||||||
|
e.TargetPort = 5432;
|
||||||
|
e.IsProxied = true;
|
||||||
|
e.IsExternal = false;
|
||||||
|
})
|
||||||
|
.WithArgs(
|
||||||
|
"-c",
|
||||||
|
"wal_level=logical",
|
||||||
|
"-c",
|
||||||
|
"max_prepared_transactions=10");
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
postgres.WithDataVolume("postgres-data")
|
||||||
|
.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var flightDb = postgres.AddDatabase("flight");
|
||||||
|
var passengerDb = postgres.AddDatabase("passenger");
|
||||||
|
var identityDb = postgres.AddDatabase("identity");
|
||||||
|
var persistMessageDb = postgres.AddDatabase("persist-message");
|
||||||
|
|
||||||
|
var mongoUsername = builder.AddParameter("mongo-username", "root", secret: true);
|
||||||
|
var mongoPassword = builder.AddParameter("mongo-password", "secret", secret: true);
|
||||||
|
|
||||||
|
var mongo = builder.AddMongoDB("mongo", userName: mongoUsername, password: mongoPassword)
|
||||||
|
.WithImage("mongo")
|
||||||
|
.WithImageTag("latest")
|
||||||
|
.WithEndpoint(
|
||||||
|
"tcp",
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
e.Port = 27017;
|
||||||
|
e.TargetPort = 27017;
|
||||||
|
e.IsProxied = true;
|
||||||
|
e.IsExternal = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
mongo.WithDataVolume("mongo-data")
|
||||||
|
.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var redis = builder.AddRedis("redis")
|
||||||
|
.WithImage("redis:latest")
|
||||||
|
.WithEndpoint(
|
||||||
|
"tcp",
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
e.Port = 6379;
|
||||||
|
e.TargetPort = 6379;
|
||||||
|
e.IsProxied = true;
|
||||||
|
e.IsExternal = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
redis.WithDataVolume("redis-data")
|
||||||
|
.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var eventstore = builder.AddEventStore("eventstore")
|
||||||
|
.WithImage("eventstore/eventstore")
|
||||||
|
.WithEnvironment("EVENTSTORE_CLUSTER_SIZE", "1")
|
||||||
|
.WithEnvironment("EVENTSTORE_RUN_PROJECTIONS", "All")
|
||||||
|
.WithEnvironment("EVENTSTORE_START_STANDARD_PROJECTIONS", "True")
|
||||||
|
.WithEnvironment("EVENTSTORE_INSECURE", "True")
|
||||||
|
.WithEnvironment("EVENTSTORE_ENABLE_ATOM_PUB_OVER_HTTP", "True")
|
||||||
|
.WithEndpoint(
|
||||||
|
"http",
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
e.TargetPort = 2113;
|
||||||
|
e.Port = 2113;
|
||||||
|
e.IsProxied = true;
|
||||||
|
e.IsExternal = true;
|
||||||
|
})
|
||||||
|
.WithEndpoint(
|
||||||
|
port: 1113,
|
||||||
|
targetPort: 1113,
|
||||||
|
name: "tcp",
|
||||||
|
isProxied: true,
|
||||||
|
isExternal: false);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
eventstore.WithDataVolume("eventstore-data")
|
||||||
|
.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Messaging Services
|
||||||
|
var rabbitmqUsername = builder.AddParameter("rabbitmq-username", "guest", secret: true);
|
||||||
|
var rabbitmqPassword = builder.AddParameter("rabbitmq-password", "guest", secret: true);
|
||||||
|
|
||||||
|
var rabbitmq = builder.AddRabbitMQ("rabbitmq", rabbitmqUsername, rabbitmqPassword)
|
||||||
|
.WithManagementPlugin()
|
||||||
|
.WithEndpoint(
|
||||||
|
"tcp",
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
e.TargetPort = 5672;
|
||||||
|
e.Port = 5672;
|
||||||
|
e.IsProxied = true;
|
||||||
|
e.IsExternal = false;
|
||||||
|
})
|
||||||
|
.WithEndpoint(
|
||||||
|
"management",
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
e.TargetPort = 15672;
|
||||||
|
e.Port = 15672;
|
||||||
|
e.IsProxied = true;
|
||||||
|
e.IsExternal = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
rabbitmq.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// // 3. Observability Services
|
||||||
|
var jaeger = builder.AddContainer("jaeger-all-in-one", "jaegertracing/all-in-one")
|
||||||
|
.WithEndpoint(
|
||||||
|
port: 6831,
|
||||||
|
targetPort: 6831,
|
||||||
|
name: "agent",
|
||||||
|
protocol: ProtocolType.Udp,
|
||||||
|
isProxied: true,
|
||||||
|
isExternal: false)
|
||||||
|
.WithEndpoint(port: 16686, targetPort: 16686, name: "http", isProxied: true, isExternal: true)
|
||||||
|
.WithEndpoint(port: 14268, targetPort: 14268, name: "collector", isProxied: true, isExternal: false)
|
||||||
|
.WithEndpoint(port: 14317, targetPort: 4317, name: "otlp-grpc", isProxied: true, isExternal: false)
|
||||||
|
.WithEndpoint(port: 14318, targetPort: 4318, name: "otlp-http", isProxied: true, isExternal: false);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
jaeger.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
var zipkin = builder.AddContainer("zipkin-all-in-one", "openzipkin/zipkin")
|
||||||
|
.WithEndpoint(port: 9411, targetPort: 9411, name: "http", isProxied: true, isExternal: true);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
zipkin.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
var otelCollector = builder.AddContainer("otel-collector", "otel/opentelemetry-collector-contrib")
|
||||||
|
.WithBindMount(
|
||||||
|
"../../../../deployments/configs/otel-collector-config.yaml",
|
||||||
|
"/etc/otelcol-contrib/config.yaml",
|
||||||
|
isReadOnly: true)
|
||||||
|
.WithArgs("--config=/etc/otelcol-contrib/config.yaml")
|
||||||
|
.WithEndpoint(port: 11888, targetPort: 1888, name: "otel-pprof", isProxied: true, isExternal: true)
|
||||||
|
.WithEndpoint(port: 8888, targetPort: 8888, name: "otel-metrics", isProxied: true, isExternal: true)
|
||||||
|
.WithEndpoint(port: 8889, targetPort: 8889, name: "otel-exporter-metrics", isProxied: true, isExternal: true)
|
||||||
|
.WithEndpoint(port: 13133, targetPort: 13133, name: "otel-health", isProxied: true, isExternal: true)
|
||||||
|
.WithEndpoint(port: 4317, targetPort: 4317, name: "otel-grpc", isProxied: true, isExternal: true)
|
||||||
|
.WithEndpoint(port: 4318, targetPort: 4318, name: "otel-http", isProxied: true, isExternal: true)
|
||||||
|
.WithEndpoint(port: 55679, targetPort: 55679, name: "otel-zpages", isProxied: true, isExternal: true);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
otelCollector.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
var prometheus = builder.AddContainer("prometheus", "prom/prometheus")
|
||||||
|
.WithBindMount("../../../../deployments/configs/prometheus.yaml", "/etc/prometheus/prometheus.yml")
|
||||||
|
.WithArgs(
|
||||||
|
"--config.file=/etc/prometheus/prometheus.yml",
|
||||||
|
"--storage.tsdb.path=/prometheus",
|
||||||
|
"--web.console.libraries=/usr/share/prometheus/console_libraries",
|
||||||
|
"--web.console.templates=/usr/share/prometheus/consoles",
|
||||||
|
"--web.enable-remote-write-receiver")
|
||||||
|
.WithEndpoint(port: 9090, targetPort: 9090, name: "http", isProxied: true, isExternal: true);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
prometheus.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
var grafana = builder.AddContainer("grafana", "grafana/grafana")
|
||||||
|
.WithEnvironment("GF_INSTALL_PLUGINS", "grafana-clock-panel,grafana-simple-json-datasource")
|
||||||
|
.WithEnvironment("GF_SECURITY_ADMIN_USER", "admin")
|
||||||
|
.WithEnvironment("GF_SECURITY_ADMIN_PASSWORD", "admin")
|
||||||
|
.WithEnvironment("GF_FEATURE_TOGGLES_ENABLE", "traceqlEditor")
|
||||||
|
.WithBindMount("../../../../deployments/configs/grafana/provisioning", "/etc/grafana/provisioning")
|
||||||
|
.WithBindMount("../../../../deployments/configs/grafana/dashboards", "/var/lib/grafana/dashboards")
|
||||||
|
.WithEndpoint(port: 3000, targetPort: 3000, name: "http", isProxied: true, isExternal: true);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
grafana.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
var nodeExporter = builder.AddContainer("node-exporter", "prom/node-exporter")
|
||||||
|
.WithBindMount("/proc", "/host/proc", isReadOnly: true)
|
||||||
|
.WithBindMount("/sys", "/host/sys", isReadOnly: true)
|
||||||
|
.WithBindMount("/", "/rootfs", isReadOnly: true)
|
||||||
|
.WithArgs(
|
||||||
|
"--path.procfs=/host/proc",
|
||||||
|
"--path.rootfs=/rootfs",
|
||||||
|
"--path.sysfs=/host/sys")
|
||||||
|
.WithEndpoint(port: 9101, targetPort: 9100, name: "http", isProxied: true, isExternal: true);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
nodeExporter.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tempo = builder.AddContainer("tempo", "grafana/tempo")
|
||||||
|
.WithBindMount("../../../../deployments/configs/tempo.yaml", "/etc/tempo.yaml", isReadOnly: true)
|
||||||
|
.WithArgs("--config.file=/etc/tempo.yaml")
|
||||||
|
.WithEndpoint(port: 3200, targetPort: 3200, name: "http", isProxied: true, isExternal: false)
|
||||||
|
.WithEndpoint(port: 9095, targetPort: 9095, name: "grpc", isProxied: true, isExternal: false)
|
||||||
|
.WithEndpoint(port: 4317, targetPort: 4317, name: "otlp-grpc", isProxied: true, isExternal: false)
|
||||||
|
.WithEndpoint(port: 4318, targetPort: 4318, name: "otlp-http", isProxied: true, isExternal: false);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
tempo.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
var loki = builder.AddContainer("loki", "grafana/loki")
|
||||||
|
.WithBindMount("../../../../deployments/configs/loki-config.yaml", "/etc/loki/local-config.yaml", isReadOnly: true)
|
||||||
|
.WithArgs("-config.file=/etc/loki/local-config.yaml")
|
||||||
|
.WithEndpoint(port: 3100, targetPort: 3100, name: "http", isProxied: true, isExternal: false)
|
||||||
|
.WithEndpoint(port: 9096, targetPort: 9096, name: "grpc", isProxied: true, isExternal: false);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
loki.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
var elasticsearch = builder.AddElasticsearch("elasticsearch")
|
||||||
|
.WithImage("docker.elastic.co/elasticsearch/elasticsearch:8.17.0")
|
||||||
|
.WithEnvironment("discovery.type", "single-node")
|
||||||
|
.WithEnvironment("cluster.name", "docker-cluster")
|
||||||
|
.WithEnvironment("node.name", "docker-node")
|
||||||
|
.WithEnvironment("ES_JAVA_OPTS", "-Xms512m -Xmx512m")
|
||||||
|
.WithEnvironment("xpack.security.enabled", "false")
|
||||||
|
.WithEnvironment("xpack.security.http.ssl.enabled", "false")
|
||||||
|
.WithEnvironment("xpack.security.transport.ssl.enabled", "false")
|
||||||
|
.WithEnvironment("network.host", "0.0.0.0")
|
||||||
|
.WithEnvironment("http.port", "9200")
|
||||||
|
.WithEnvironment("transport.host", "localhost")
|
||||||
|
.WithEnvironment("bootstrap.memory_lock", "true")
|
||||||
|
.WithEnvironment("cluster.routing.allocation.disk.threshold_enabled", "false")
|
||||||
|
.WithEndpoint(
|
||||||
|
"http",
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
e.TargetPort = 9200;
|
||||||
|
e.Port = 9200;
|
||||||
|
e.IsProxied = true;
|
||||||
|
e.IsExternal = false;
|
||||||
|
})
|
||||||
|
.WithEndpoint(
|
||||||
|
"internal",
|
||||||
|
e =>
|
||||||
|
{
|
||||||
|
e.TargetPort = 9300;
|
||||||
|
e.Port = 9300;
|
||||||
|
e.IsProxied = true;
|
||||||
|
e.IsExternal = false;
|
||||||
|
})
|
||||||
|
.WithDataVolume("elastic-data");
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
elasticsearch.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
var kibana = builder.AddContainer("kibana", "docker.elastic.co/kibana/kibana:8.17.0")
|
||||||
|
.WithEnvironment("ELASTICSEARCH_HOSTS", "http://elasticsearch:9200")
|
||||||
|
.WithEndpoint(port: 5601, targetPort: 5601, name: "http", isProxied: true, isExternal: true)
|
||||||
|
.WithReference(elasticsearch)
|
||||||
|
.WaitFor(elasticsearch);
|
||||||
|
|
||||||
|
if (builder.ExecutionContext.IsPublishMode)
|
||||||
|
{
|
||||||
|
kibana.WithLifetime(ContainerLifetime.Persistent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Application Services
|
||||||
|
var identity = builder.AddProject<Projects.Identity_Api>("identity-service")
|
||||||
|
.WithReference(persistMessageDb)
|
||||||
|
.WaitFor(persistMessageDb)
|
||||||
|
.WithReference(identityDb)
|
||||||
|
.WaitFor(identityDb)
|
||||||
|
.WithReference(mongo)
|
||||||
|
.WaitFor(mongo)
|
||||||
|
.WithReference(rabbitmq)
|
||||||
|
.WaitFor(rabbitmq)
|
||||||
|
.WithHttpEndpoint(port: 6005, name: "identity-http")
|
||||||
|
.WithHttpsEndpoint(port: 5005, name: "identity-https");
|
||||||
|
|
||||||
|
var passenger = builder.AddProject<Projects.Passenger_Api>("passenger-service")
|
||||||
|
.WithReference(persistMessageDb)
|
||||||
|
.WaitFor(persistMessageDb)
|
||||||
|
.WithReference(passengerDb)
|
||||||
|
.WaitFor(passengerDb)
|
||||||
|
.WithReference(mongo)
|
||||||
|
.WaitFor(mongo)
|
||||||
|
.WithReference(rabbitmq)
|
||||||
|
.WaitFor(rabbitmq)
|
||||||
|
.WithHttpEndpoint(port: 6012, name: "passenger-http")
|
||||||
|
.WithHttpsEndpoint(port: 5012, name: "passenger-https");
|
||||||
|
|
||||||
|
var flight = builder.AddProject<Projects.Flight_Api>("flight-service")
|
||||||
|
.WithReference(persistMessageDb)
|
||||||
|
.WaitFor(persistMessageDb)
|
||||||
|
.WithReference(flightDb)
|
||||||
|
.WaitFor(flightDb)
|
||||||
|
.WithReference(mongo)
|
||||||
|
.WaitFor(mongo)
|
||||||
|
.WithReference(rabbitmq)
|
||||||
|
.WaitFor(rabbitmq)
|
||||||
|
.WithHttpEndpoint(port: 5004, name: "flight-http")
|
||||||
|
.WithHttpsEndpoint(port: 5003, name: "flight-https");
|
||||||
|
|
||||||
|
var booking = builder.AddProject<Projects.Booking_Api>("booking-service")
|
||||||
|
.WithReference(persistMessageDb)
|
||||||
|
.WaitFor(persistMessageDb)
|
||||||
|
.WithReference(eventstore)
|
||||||
|
.WaitFor(eventstore)
|
||||||
|
.WithReference(mongo)
|
||||||
|
.WaitFor(mongo)
|
||||||
|
.WithReference(rabbitmq)
|
||||||
|
.WaitFor(rabbitmq)
|
||||||
|
.WithHttpEndpoint(port: 6010, name: "booking-http")
|
||||||
|
.WithHttpsEndpoint(port: 5010, name: "booking-https");
|
||||||
|
|
||||||
|
var gateway = builder.AddProject<Projects.ApiGateway>("api-gateway")
|
||||||
|
.WithReference(flight)
|
||||||
|
.WaitFor(flight)
|
||||||
|
.WithReference(passenger)
|
||||||
|
.WaitFor(passenger)
|
||||||
|
.WithReference(identity)
|
||||||
|
.WaitFor(identity)
|
||||||
|
.WithReference(booking)
|
||||||
|
.WaitFor(booking)
|
||||||
|
.WithHttpEndpoint(port: 5001, name: "gateway-http")
|
||||||
|
.WithHttpsEndpoint(port: 5000, name: "gateway-https");
|
||||||
|
|
||||||
|
builder.Build().Run();
|
||||||
18
src/Aspire/src/AppHost/Properties/launchSettings.json
Normal file
18
src/Aspire/src/AppHost/Properties/launchSettings.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||||
|
"profiles": {
|
||||||
|
"AppHost": {
|
||||||
|
"commandName": "Project",
|
||||||
|
"dotnetRunMessages": true,
|
||||||
|
"launchBrowser": true,
|
||||||
|
"applicationUrl": "http://localhost:18888",
|
||||||
|
"environmentVariables": {
|
||||||
|
"ASPNETCORE_ENVIRONMENT": "Development",
|
||||||
|
"DOTNET_ENVIRONMENT": "Development",
|
||||||
|
"ASPIRE_ALLOW_UNSECURED_TRANSPORT": "true",
|
||||||
|
"ASPIRE_DASHBOARD_OTLP_ENDPOINT_URL": "http://otel-collector:4317",
|
||||||
|
"ASPIRE_DASHBOARD_OTLP_HTTP_ENDPOINT_URL": "http://otel-collector:4318"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/Aspire/src/AppHost/appsettings.Development.json
Normal file
8
src/Aspire/src/AppHost/appsettings.Development.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/Aspire/src/AppHost/appsettings.json
Normal file
9
src/Aspire/src/AppHost/appsettings.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"Logging": {
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Information",
|
||||||
|
"Microsoft.AspNetCore": "Warning"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AllowedHosts": "*"
|
||||||
|
}
|
||||||
41
src/Aspire/src/ServiceDefaults/Extensions.cs
Normal file
41
src/Aspire/src/ServiceDefaults/Extensions.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
using BuildingBlocks.HealthCheck;
|
||||||
|
using BuildingBlocks.OpenTelemetryCollector;
|
||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
|
namespace ServiceDefaults;
|
||||||
|
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
public static IHostApplicationBuilder AddServiceDefaults(this WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
builder.Services.AddCustomHealthCheck();
|
||||||
|
builder.AddCustomObservability();
|
||||||
|
builder.Services.AddServiceDiscovery();
|
||||||
|
|
||||||
|
builder.Services.ConfigureHttpClientDefaults(http =>
|
||||||
|
{
|
||||||
|
http.AddStandardResilienceHandler(options =>
|
||||||
|
{
|
||||||
|
var timeSpan = TimeSpan.FromMinutes(1);
|
||||||
|
options.CircuitBreaker.SamplingDuration = timeSpan * 2;
|
||||||
|
options.TotalRequestTimeout.Timeout = timeSpan * 3;
|
||||||
|
options.Retry.MaxRetryAttempts = 3;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Turn on service discovery by default
|
||||||
|
http.AddServiceDiscovery();
|
||||||
|
});
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static WebApplication UseServiceDefaults(this WebApplication app)
|
||||||
|
{
|
||||||
|
app.UseCustomHealthCheck();
|
||||||
|
app.UseCustomObservability();
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Aspire/src/ServiceDefaults/ServiceDefaults.csproj
Normal file
13
src/Aspire/src/ServiceDefaults/ServiceDefaults.csproj
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\..\BuildingBlocks\BuildingBlocks.csproj" IsAspireProjectResource="false" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@ -1,110 +1,110 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Ardalis.GuardClauses" Version="4.6.0" />
|
<PackageReference Include="Ardalis.GuardClauses" Version="5.0.0" />
|
||||||
<PackageReference Include="Asp.Versioning.Abstractions" Version="8.1.0" />
|
<PackageReference Include="Asp.Versioning.Abstractions" Version="8.1.0" />
|
||||||
<PackageReference Include="Asp.Versioning.Http" Version="8.1.0" />
|
<PackageReference Include="Asp.Versioning.Http" Version="8.1.1" />
|
||||||
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.0" />
|
<PackageReference Include="Asp.Versioning.Mvc" Version="8.1.1" />
|
||||||
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.0" />
|
<PackageReference Include="Asp.Versioning.Mvc.ApiExplorer" Version="8.1.1" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="8.0.8" />
|
<PackageReference Include="Figgle.Fonts" Version="0.6.5" />
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="8.0.2" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="8.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="8.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.Elasticsearch" Version="8.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.EventStore" Version="8.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="8.0.2" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="8.0.2" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.UI.SQLite.Storage" Version="8.0.1" />
|
|
||||||
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="8.1.0" />
|
|
||||||
<PackageReference Include="Grpc.Core.Testing" Version="2.46.6" />
|
<PackageReference Include="Grpc.Core.Testing" Version="2.46.6" />
|
||||||
<PackageReference Include="EasyCaching.Core" Version="1.9.2" />
|
<PackageReference Include="EasyCaching.Core" Version="1.9.2" />
|
||||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
|
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
|
||||||
<PackageReference Include="EasyNetQ.Management.Client" Version="3.0.0" />
|
<PackageReference Include="EasyNetQ.Management.Client" Version="3.0.1" />
|
||||||
<PackageReference Include="EFCore.NamingConventions" Version="8.0.3" />
|
<PackageReference Include="EFCore.NamingConventions" Version="10.0.1" />
|
||||||
<PackageReference Include="Figgle" Version="0.5.1" />
|
<PackageReference Include="Figgle" Version="0.6.5" />
|
||||||
<PackageReference Include="FluentValidation" Version="11.10.0" />
|
<PackageReference Include="FluentValidation" Version="12.1.1" />
|
||||||
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.0" />
|
<PackageReference Include="FluentValidation.AspNetCore" Version="11.3.1" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="10.0.3" />
|
||||||
<PackageReference Include="Npgsql" Version="8.0.4" />
|
<PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="10.3.0" />
|
||||||
<PackageReference Include="NSubstitute" Version="5.1.0" />
|
<PackageReference Include="Microsoft.Extensions.ServiceDiscovery" Version="10.3.0" />
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.9.0-beta.2" />
|
<PackageReference Include="Npgsql" Version="10.0.1" />
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.9.0-beta.1" />
|
<PackageReference Include="NSubstitute" Version="5.3.0" />
|
||||||
<PackageReference Include="Polly" Version="8.4.1" />
|
<PackageReference Include="Polly" Version="8.6.5" />
|
||||||
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
|
<PackageReference Include="Humanizer.Core" Version="3.0.1" />
|
||||||
<PackageReference Include="IdGen" Version="3.0.7" />
|
<PackageReference Include="IdGen" Version="3.0.7" />
|
||||||
<PackageReference Include="Mapster" Version="7.4.0" />
|
<PackageReference Include="Mapster" Version="7.4.0" />
|
||||||
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />
|
<PackageReference Include="Mapster.DependencyInjection" Version="1.0.1" />
|
||||||
<PackageReference Include="MediatR" Version="12.4.1" />
|
<PackageReference Include="MediatR" Version="14.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.8" />
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
|
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="8.0.8" />
|
<PackageReference Include="Microsoft.Extensions.Http.Polly" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
|
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.3" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="2.28.0" />
|
<PackageReference Include="MongoDB.Driver" Version="3.6.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
|
||||||
<PackageReference Include="OpenTelemetry.Contrib.Instrumentation.MassTransit" Version="1.0.0-beta2" />
|
<PackageReference Include="Scalar.AspNetCore" Version="2.12.38" />
|
||||||
<PackageReference Include="Scrutor" Version="4.2.2" />
|
<PackageReference Include="Scrutor" Version="7.0.0" />
|
||||||
<PackageReference Include="Sentry.Serilog" Version="4.10.2" />
|
|
||||||
<PackageReference Include="Serilog" Version="4.0.1" />
|
|
||||||
<PackageReference Include="Serilog.AspNetCore" Version="8.0.2" />
|
|
||||||
<PackageReference Include="Serilog.Enrichers.Span" Version="3.1.0" />
|
|
||||||
<PackageReference Include="Serilog.Exceptions" Version="8.4.0" />
|
|
||||||
<PackageReference Include="Serilog.Formatting.Elasticsearch" Version="10.0.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Console" Version="6.0.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Elasticsearch" Version="10.0.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.Seq" Version="8.0.0" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.SpectreConsole" Version="0.3.3" />
|
|
||||||
<PackageReference Include="Serilog.Sinks.XUnit" Version="3.0.5" />
|
|
||||||
<PackageReference Include="Sieve" Version="2.5.5" />
|
<PackageReference Include="Sieve" Version="2.5.5" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.7.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="10.1.2" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.7.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="10.1.2" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.3" />
|
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="10.1.2" />
|
||||||
<PackageReference Include="MassTransit" Version="8.2.5" />
|
<PackageReference Include="MassTransit" Version="8.5.8" />
|
||||||
<PackageReference Include="MassTransit.RabbitMQ" Version="8.2.5" />
|
<PackageReference Include="MassTransit.RabbitMQ" Version="8.5.8" />
|
||||||
<PackageReference Include="Duende.IdentityServer" Version="7.0.6" />
|
<PackageReference Include="Duende.IdentityServer" Version="7.4.5" />
|
||||||
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="7.0.6" />
|
<PackageReference Include="Duende.IdentityServer.AspNetIdentity" Version="7.4.5" />
|
||||||
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="7.0.6" />
|
<PackageReference Include="Duende.IdentityServer.EntityFramework" Version="7.4.5" />
|
||||||
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="7.0.6" />
|
<PackageReference Include="Duende.IdentityServer.EntityFramework.Storage" Version="7.4.5" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.3" />
|
||||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
<PackageReference Include="System.Linq.Async" Version="7.0.0" />
|
||||||
<PackageReference Include="System.Linq.Async.Queryable" Version="6.0.1" />
|
<PackageReference Include="System.Linq.Async.Queryable" Version="7.0.0" />
|
||||||
<PackageReference Include="Testcontainers" Version="3.10.0" />
|
<PackageReference Include="Testcontainers" Version="4.9.0" />
|
||||||
<PackageReference Include="Testcontainers.EventStoreDb" Version="3.10.0" />
|
<PackageReference Include="Testcontainers.EventStoreDb" Version="4.9.0" />
|
||||||
<PackageReference Include="Testcontainers.MongoDb" Version="3.10.0" />
|
<PackageReference Include="Testcontainers.MongoDb" Version="4.9.0" />
|
||||||
<PackageReference Include="Testcontainers.PostgreSql" Version="3.10.0" />
|
<PackageReference Include="Testcontainers.PostgreSql" Version="4.9.0" />
|
||||||
<PackageReference Include="Testcontainers.RabbitMq" Version="3.10.0" />
|
<PackageReference Include="Testcontainers.RabbitMq" Version="4.9.0" />
|
||||||
<PackageReference Include="Unchase.Swashbuckle.AspNetCore.Extensions" Version="2.7.1" />
|
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
|
||||||
<PackageReference Include="Yarp.ReverseProxy" Version="2.2.0" />
|
<PackageReference Include="xunit.extensibility.core" Version="2.9.3" />
|
||||||
|
<PackageReference Include="Xunit.Extensions.Logging" Version="1.1.0" />
|
||||||
|
<PackageReference Include="Yarp.ReverseProxy" Version="2.3.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Diagnostics.HealthChecks" Version="10.0.3" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.Client" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.UI.InMemory.Storage" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.EventStore" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.NpgSql" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.Rabbitmq" Version="9.0.0" />
|
||||||
|
<PackageReference Include="AspNetCore.HealthChecks.MongoDb" Version="9.0.0" />
|
||||||
|
|
||||||
<PackageReference Include="prometheus-net" Version="8.2.1" />
|
<PackageReference Include="Npgsql.OpenTelemetry" Version="10.0.1" />
|
||||||
<PackageReference Include="prometheus-net.AspNetCore" Version="8.2.1" />
|
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.15.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.15.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.15.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.GrpcNetClient" Version="1.15.0-beta.1" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.15.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.Process" Version="1.15.0-beta.1" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Instrumentation.Runtime" Version="1.15.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Exporter.Prometheus.AspNetCore" Version="1.15.0-beta.1" />
|
||||||
|
<PackageReference Include="Grafana.OpenTelemetry" Version="1.5.2" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Exporter.Console" Version="1.15.0" />
|
||||||
|
<PackageReference Include="OpenTelemetry.Exporter.Zipkin" Version="1.15.0" />
|
||||||
|
|
||||||
<PackageReference Include="OpenTelemetry" Version="1.9.0" />
|
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.9" />
|
||||||
<PackageReference Include="OpenTelemetry.Exporter.Jaeger" Version="1.6.0-rc.1" />
|
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="10.0.0" />
|
||||||
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.9.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.9.0" />
|
|
||||||
<PackageReference Include="OpenTelemetry.Instrumentation.Http" Version="1.9.0" />
|
|
||||||
|
|
||||||
<PackageReference Include="EventStore.Client.Grpc.Streams" Version="23.3.5" />
|
|
||||||
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.4" />
|
|
||||||
|
|
||||||
<PackageReference Include="AutoBogus" Version="2.13.1" />
|
<PackageReference Include="AutoBogus" Version="2.13.1" />
|
||||||
<PackageReference Include="Bogus" Version="35.6.1" />
|
<PackageReference Include="Bogus" Version="35.6.5" />
|
||||||
<PackageReference Include="FluentAssertions" Version="6.12.1" />
|
<PackageReference Include="FluentAssertions" Version="8.8.0" />
|
||||||
<PackageReference Include="Respawn" Version="6.2.1" />
|
<PackageReference Include="Respawn" Version="7.0.0" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="10.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.3" />
|
||||||
<PackageReference Include="WebMotions.Fake.Authentication.JwtBearer" Version="8.0.1" />
|
<PackageReference Include="WebMotions.Fake.Authentication.JwtBearer" Version="10.0.0" />
|
||||||
|
|
||||||
<PackageReference Include="Google.Protobuf" Version="3.28.1" />
|
<PackageReference Include="Google.Protobuf" Version="3.33.5" />
|
||||||
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.65.0" />
|
<PackageReference Include="Grpc.Net.ClientFactory" Version="2.76.0" />
|
||||||
|
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.3">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
10
src/BuildingBlocks/Constants/IdentityConstant.cs
Normal file
10
src/BuildingBlocks/Constants/IdentityConstant.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace BuildingBlocks.Constants;
|
||||||
|
|
||||||
|
public static class IdentityConstant
|
||||||
|
{
|
||||||
|
public static class Role
|
||||||
|
{
|
||||||
|
public const string Admin = "admin";
|
||||||
|
public const string User = "user";
|
||||||
|
}
|
||||||
|
}
|
||||||
37
src/BuildingBlocks/Core/CompositeEventMapper.cs
Normal file
37
src/BuildingBlocks/Core/CompositeEventMapper.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using BuildingBlocks.Core.Event;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.Core;
|
||||||
|
|
||||||
|
public class CompositeEventMapper : IEventMapper
|
||||||
|
{
|
||||||
|
private readonly IEnumerable<IEventMapper> _mappers;
|
||||||
|
|
||||||
|
public CompositeEventMapper(IEnumerable<IEventMapper> mappers)
|
||||||
|
{
|
||||||
|
_mappers = mappers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IIntegrationEvent? MapToIntegrationEvent(IDomainEvent @event)
|
||||||
|
{
|
||||||
|
foreach (var mapper in _mappers)
|
||||||
|
{
|
||||||
|
var integrationEvent = mapper.MapToIntegrationEvent(@event);
|
||||||
|
if (integrationEvent is not null)
|
||||||
|
return integrationEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IInternalCommand? MapToInternalCommand(IDomainEvent @event)
|
||||||
|
{
|
||||||
|
foreach (var mapper in _mappers)
|
||||||
|
{
|
||||||
|
var internalCommand = mapper.MapToInternalCommand(@event);
|
||||||
|
if (internalCommand is not null)
|
||||||
|
return internalCommand;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -9,30 +9,17 @@ using MessageEnvelope = BuildingBlocks.Core.Event.MessageEnvelope;
|
|||||||
|
|
||||||
namespace BuildingBlocks.Core;
|
namespace BuildingBlocks.Core;
|
||||||
|
|
||||||
public sealed class EventDispatcher : IEventDispatcher
|
public sealed class EventDispatcher(
|
||||||
|
IServiceScopeFactory serviceScopeFactory,
|
||||||
|
IEventMapper eventMapper,
|
||||||
|
ILogger<EventDispatcher> logger,
|
||||||
|
IPersistMessageProcessor persistMessageProcessor,
|
||||||
|
IHttpContextAccessor httpContextAccessor
|
||||||
|
)
|
||||||
|
: IEventDispatcher
|
||||||
{
|
{
|
||||||
private readonly IEventMapper _eventMapper;
|
|
||||||
private readonly ILogger<EventDispatcher> _logger;
|
|
||||||
private readonly IPersistMessageProcessor _persistMessageProcessor;
|
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
|
||||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
|
||||||
|
|
||||||
public EventDispatcher(IServiceScopeFactory serviceScopeFactory,
|
|
||||||
IEventMapper eventMapper,
|
|
||||||
ILogger<EventDispatcher> logger,
|
|
||||||
IPersistMessageProcessor persistMessageProcessor,
|
|
||||||
IHttpContextAccessor httpContextAccessor)
|
|
||||||
{
|
|
||||||
_serviceScopeFactory = serviceScopeFactory;
|
|
||||||
_eventMapper = eventMapper;
|
|
||||||
_logger = logger;
|
|
||||||
_persistMessageProcessor = persistMessageProcessor;
|
|
||||||
_httpContextAccessor = httpContextAccessor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public async Task SendAsync<T>(IReadOnlyList<T> events, Type type = null,
|
public async Task SendAsync<T>(IReadOnlyList<T> events, Type type = null,
|
||||||
CancellationToken cancellationToken = default)
|
CancellationToken cancellationToken = default)
|
||||||
where T : IEvent
|
where T : IEvent
|
||||||
{
|
{
|
||||||
if (events.Count > 0)
|
if (events.Count > 0)
|
||||||
@ -45,7 +32,7 @@ public sealed class EventDispatcher : IEventDispatcher
|
|||||||
{
|
{
|
||||||
foreach (var integrationEvent in integrationEvents)
|
foreach (var integrationEvent in integrationEvents)
|
||||||
{
|
{
|
||||||
await _persistMessageProcessor.PublishMessageAsync(
|
await persistMessageProcessor.PublishMessageAsync(
|
||||||
new MessageEnvelope(integrationEvent, SetHeaders()),
|
new MessageEnvelope(integrationEvent, SetHeaders()),
|
||||||
cancellationToken);
|
cancellationToken);
|
||||||
}
|
}
|
||||||
@ -74,7 +61,7 @@ public sealed class EventDispatcher : IEventDispatcher
|
|||||||
|
|
||||||
foreach (var internalMessage in internalMessages)
|
foreach (var internalMessage in internalMessages)
|
||||||
{
|
{
|
||||||
await _persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken);
|
await persistMessageProcessor.AddInternalMessageAsync(internalMessage, cancellationToken);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,20 +76,20 @@ public sealed class EventDispatcher : IEventDispatcher
|
|||||||
private Task<IReadOnlyList<IIntegrationEvent>> MapDomainEventToIntegrationEventAsync(
|
private Task<IReadOnlyList<IIntegrationEvent>> MapDomainEventToIntegrationEventAsync(
|
||||||
IReadOnlyList<IDomainEvent> events)
|
IReadOnlyList<IDomainEvent> events)
|
||||||
{
|
{
|
||||||
_logger.LogTrace("Processing integration events start...");
|
logger.LogTrace("Processing integration events start...");
|
||||||
|
|
||||||
var wrappedIntegrationEvents = GetWrappedIntegrationEvents(events.ToList())?.ToList();
|
var wrappedIntegrationEvents = GetWrappedIntegrationEvents(events.ToList())?.ToList();
|
||||||
if (wrappedIntegrationEvents?.Count > 0)
|
if (wrappedIntegrationEvents?.Count > 0)
|
||||||
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(wrappedIntegrationEvents);
|
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(wrappedIntegrationEvents);
|
||||||
|
|
||||||
var integrationEvents = new List<IIntegrationEvent>();
|
var integrationEvents = new List<IIntegrationEvent>();
|
||||||
using var scope = _serviceScopeFactory.CreateScope();
|
using var scope = serviceScopeFactory.CreateScope();
|
||||||
foreach (var @event in events)
|
foreach (var @event in events)
|
||||||
{
|
{
|
||||||
var eventType = @event.GetType();
|
var eventType = @event.GetType();
|
||||||
_logger.LogTrace($"Handling domain event: {eventType.Name}");
|
logger.LogTrace($"Handling domain event: {eventType.Name}");
|
||||||
|
|
||||||
var integrationEvent = _eventMapper.MapToIntegrationEvent(@event);
|
var integrationEvent = eventMapper.MapToIntegrationEvent(@event);
|
||||||
|
|
||||||
if (integrationEvent is null)
|
if (integrationEvent is null)
|
||||||
continue;
|
continue;
|
||||||
@ -110,7 +97,7 @@ public sealed class EventDispatcher : IEventDispatcher
|
|||||||
integrationEvents.Add(integrationEvent);
|
integrationEvents.Add(integrationEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogTrace("Processing integration events done...");
|
logger.LogTrace("Processing integration events done...");
|
||||||
|
|
||||||
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(integrationEvents);
|
return Task.FromResult<IReadOnlyList<IIntegrationEvent>>(integrationEvents);
|
||||||
}
|
}
|
||||||
@ -119,16 +106,16 @@ public sealed class EventDispatcher : IEventDispatcher
|
|||||||
private Task<IReadOnlyList<IInternalCommand>> MapDomainEventToInternalCommandAsync(
|
private Task<IReadOnlyList<IInternalCommand>> MapDomainEventToInternalCommandAsync(
|
||||||
IReadOnlyList<IDomainEvent> events)
|
IReadOnlyList<IDomainEvent> events)
|
||||||
{
|
{
|
||||||
_logger.LogTrace("Processing internal message start...");
|
logger.LogTrace("Processing internal message start...");
|
||||||
|
|
||||||
var internalCommands = new List<IInternalCommand>();
|
var internalCommands = new List<IInternalCommand>();
|
||||||
using var scope = _serviceScopeFactory.CreateScope();
|
using var scope = serviceScopeFactory.CreateScope();
|
||||||
foreach (var @event in events)
|
foreach (var @event in events)
|
||||||
{
|
{
|
||||||
var eventType = @event.GetType();
|
var eventType = @event.GetType();
|
||||||
_logger.LogTrace($"Handling domain event: {eventType.Name}");
|
logger.LogTrace($"Handling domain event: {eventType.Name}");
|
||||||
|
|
||||||
var integrationEvent = _eventMapper.MapToInternalCommand(@event);
|
var integrationEvent = eventMapper.MapToInternalCommand(@event);
|
||||||
|
|
||||||
if (integrationEvent is null)
|
if (integrationEvent is null)
|
||||||
continue;
|
continue;
|
||||||
@ -136,7 +123,7 @@ public sealed class EventDispatcher : IEventDispatcher
|
|||||||
internalCommands.Add(integrationEvent);
|
internalCommands.Add(integrationEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.LogTrace("Processing internal message done...");
|
logger.LogTrace("Processing internal message done...");
|
||||||
|
|
||||||
return Task.FromResult<IReadOnlyList<IInternalCommand>>(internalCommands);
|
return Task.FromResult<IReadOnlyList<IInternalCommand>>(internalCommands);
|
||||||
}
|
}
|
||||||
@ -159,9 +146,9 @@ public sealed class EventDispatcher : IEventDispatcher
|
|||||||
private IDictionary<string, object> SetHeaders()
|
private IDictionary<string, object> SetHeaders()
|
||||||
{
|
{
|
||||||
var headers = new Dictionary<string, object>();
|
var headers = new Dictionary<string, object>();
|
||||||
headers.Add("CorrelationId", _httpContextAccessor?.HttpContext?.GetCorrelationId());
|
headers.Add("CorrelationId", httpContextAccessor?.HttpContext?.GetCorrelationId());
|
||||||
headers.Add("UserId", _httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier));
|
headers.Add("UserId", httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.NameIdentifier));
|
||||||
headers.Add("UserName", _httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.Name));
|
headers.Add("UserName", httpContextAccessor?.HttpContext?.User?.FindFirstValue(ClaimTypes.Name));
|
||||||
|
|
||||||
return headers;
|
return headers;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,14 @@
|
|||||||
namespace BuildingBlocks.EFCore;
|
|
||||||
|
|
||||||
using System.Collections.Immutable;
|
using System.Collections.Immutable;
|
||||||
using Core.Event;
|
using BuildingBlocks.Core.Event;
|
||||||
using Core.Model;
|
using BuildingBlocks.Core.Model;
|
||||||
|
using BuildingBlocks.Web;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Storage;
|
using Microsoft.EntityFrameworkCore.Storage;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Web;
|
|
||||||
using Exception = System.Exception;
|
|
||||||
using IsolationLevel = System.Data.IsolationLevel;
|
using IsolationLevel = System.Data.IsolationLevel;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.EFCore;
|
||||||
|
|
||||||
public abstract class AppDbContextBase : DbContext, IDbContext
|
public abstract class AppDbContextBase : DbContext, IDbContext
|
||||||
{
|
{
|
||||||
private readonly ICurrentUserProvider? _currentUserProvider;
|
private readonly ICurrentUserProvider? _currentUserProvider;
|
||||||
@ -174,9 +173,9 @@ public abstract class AppDbContextBase : DbContext, IDbContext
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (System.Exception ex)
|
||||||
{
|
{
|
||||||
throw new Exception("try for find IAggregate", ex);
|
throw new System.Exception("try for find IAggregate", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.IO;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.EntityFrameworkCore.Design;
|
using Microsoft.EntityFrameworkCore.Design;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
|
|||||||
@ -1,50 +1,39 @@
|
|||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using System.Transactions;
|
||||||
using BuildingBlocks.Core;
|
using BuildingBlocks.Core;
|
||||||
|
using BuildingBlocks.PersistMessageProcessor;
|
||||||
|
using BuildingBlocks.Polly;
|
||||||
using MediatR;
|
using MediatR;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace BuildingBlocks.EFCore;
|
namespace BuildingBlocks.EFCore;
|
||||||
|
|
||||||
using System.Transactions;
|
|
||||||
using PersistMessageProcessor;
|
|
||||||
using Polly;
|
|
||||||
|
|
||||||
public class EfTxBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
|
public class EfTxBehavior<TRequest, TResponse>(
|
||||||
where TRequest : notnull, IRequest<TResponse>
|
ILogger<EfTxBehavior<TRequest, TResponse>> logger,
|
||||||
where TResponse : notnull
|
IDbContext dbContextBase,
|
||||||
|
IPersistMessageDbContext persistMessageDbContext,
|
||||||
|
IEventDispatcher eventDispatcher
|
||||||
|
)
|
||||||
|
: IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : notnull, IRequest<TResponse>
|
||||||
|
where TResponse : notnull
|
||||||
{
|
{
|
||||||
private readonly ILogger<EfTxBehavior<TRequest, TResponse>> _logger;
|
|
||||||
private readonly IDbContext _dbContextBase;
|
|
||||||
private readonly IPersistMessageDbContext _persistMessageDbContext;
|
|
||||||
private readonly IEventDispatcher _eventDispatcher;
|
|
||||||
|
|
||||||
public EfTxBehavior(
|
|
||||||
ILogger<EfTxBehavior<TRequest, TResponse>> logger,
|
|
||||||
IDbContext dbContextBase,
|
|
||||||
IPersistMessageDbContext persistMessageDbContext,
|
|
||||||
IEventDispatcher eventDispatcher)
|
|
||||||
{
|
|
||||||
_logger = logger;
|
|
||||||
_dbContextBase = dbContextBase;
|
|
||||||
_persistMessageDbContext = persistMessageDbContext;
|
|
||||||
_eventDispatcher = eventDispatcher;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
|
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
_logger.LogInformation(
|
logger.LogInformation(
|
||||||
"{Prefix} Handled command {MediatrRequest}",
|
"{Prefix} Handled command {MediatrRequest}",
|
||||||
nameof(EfTxBehavior<TRequest, TResponse>),
|
nameof(EfTxBehavior<TRequest, TResponse>),
|
||||||
typeof(TRequest).FullName);
|
typeof(TRequest).FullName);
|
||||||
|
|
||||||
_logger.LogDebug(
|
logger.LogDebug(
|
||||||
"{Prefix} Handled command {MediatrRequest} with content {RequestContent}",
|
"{Prefix} Handled command {MediatrRequest} with content {RequestContent}",
|
||||||
nameof(EfTxBehavior<TRequest, TResponse>),
|
nameof(EfTxBehavior<TRequest, TResponse>),
|
||||||
typeof(TRequest).FullName,
|
typeof(TRequest).FullName,
|
||||||
JsonSerializer.Serialize(request));
|
JsonSerializer.Serialize(request));
|
||||||
|
|
||||||
_logger.LogInformation(
|
logger.LogInformation(
|
||||||
"{Prefix} Open the transaction for {MediatrRequest}",
|
"{Prefix} Open the transaction for {MediatrRequest}",
|
||||||
nameof(EfTxBehavior<TRequest, TResponse>),
|
nameof(EfTxBehavior<TRequest, TResponse>),
|
||||||
typeof(TRequest).FullName);
|
typeof(TRequest).FullName);
|
||||||
@ -56,32 +45,32 @@ public class EfTxBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TRe
|
|||||||
|
|
||||||
var response = await next();
|
var response = await next();
|
||||||
|
|
||||||
_logger.LogInformation(
|
logger.LogInformation(
|
||||||
"{Prefix} Executed the {MediatrRequest} request",
|
"{Prefix} Executed the {MediatrRequest} request",
|
||||||
nameof(EfTxBehavior<TRequest, TResponse>),
|
nameof(EfTxBehavior<TRequest, TResponse>),
|
||||||
typeof(TRequest).FullName);
|
typeof(TRequest).FullName);
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
var domainEvents = _dbContextBase.GetDomainEvents();
|
var domainEvents = dbContextBase.GetDomainEvents();
|
||||||
|
|
||||||
if (domainEvents is null || !domainEvents.Any())
|
if (domainEvents is null || !domainEvents.Any())
|
||||||
{
|
{
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
await _eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken);
|
await eventDispatcher.SendAsync(domainEvents.ToArray(), typeof(TRequest), cancellationToken);
|
||||||
|
|
||||||
// Save data to database with some retry policy in distributed transaction
|
// Save data to database with some retry policy in distributed transaction
|
||||||
await _dbContextBase.RetryOnFailure(async () =>
|
await dbContextBase.RetryOnFailure(async () =>
|
||||||
{
|
{
|
||||||
await _dbContextBase.SaveChangesAsync(cancellationToken);
|
await dbContextBase.SaveChangesAsync(cancellationToken);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Save data to database with some retry policy in distributed transaction
|
// Save data to database with some retry policy in distributed transaction
|
||||||
await _persistMessageDbContext.RetryOnFailure(async () =>
|
await persistMessageDbContext.RetryOnFailure(async () =>
|
||||||
{
|
{
|
||||||
await _persistMessageDbContext.SaveChangesAsync(cancellationToken);
|
await persistMessageDbContext.SaveChangesAsync(cancellationToken);
|
||||||
});
|
});
|
||||||
|
|
||||||
scope.Complete();
|
scope.Complete();
|
||||||
|
|||||||
@ -1,67 +1,65 @@
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using BuildingBlocks.Core.Model;
|
using BuildingBlocks.Core.Model;
|
||||||
using BuildingBlocks.Web;
|
using BuildingBlocks.Web;
|
||||||
|
using Humanizer;
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Diagnostics;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
using Microsoft.EntityFrameworkCore.Query;
|
using Microsoft.EntityFrameworkCore.Query;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace BuildingBlocks.EFCore;
|
namespace BuildingBlocks.EFCore;
|
||||||
|
|
||||||
using Ardalis.GuardClauses;
|
|
||||||
using Humanizer;
|
|
||||||
using Microsoft.EntityFrameworkCore.Metadata;
|
|
||||||
|
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddCustomDbContext<TContext>(this IServiceCollection services)
|
public static IServiceCollection AddCustomDbContext<TContext>(this WebApplicationBuilder builder, string? connectionName = "")
|
||||||
where TContext : DbContext, IDbContext
|
where TContext : DbContext, IDbContext
|
||||||
{
|
{
|
||||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", true);
|
||||||
|
|
||||||
services.AddValidateOptions<PostgresOptions>();
|
builder.Services.AddValidateOptions<PostgresOptions>();
|
||||||
|
|
||||||
services.AddDbContext<TContext>(
|
builder.Services.AddDbContext<TContext>(
|
||||||
(sp, options) =>
|
(sp, options) =>
|
||||||
{
|
{
|
||||||
var postgresOptions = sp.GetRequiredService<PostgresOptions>();
|
var aspireConnectionString = builder.Configuration.GetConnectionString(connectionName.Kebaberize());
|
||||||
|
var connectionString = aspireConnectionString ?? sp.GetRequiredService<PostgresOptions>().ConnectionString;
|
||||||
|
|
||||||
Guard.Against.Null(options, nameof(postgresOptions));
|
ArgumentException.ThrowIfNullOrEmpty(connectionString);
|
||||||
|
|
||||||
options.UseNpgsql(
|
options.UseNpgsql(
|
||||||
postgresOptions?.ConnectionString,
|
connectionString,
|
||||||
dbOptions =>
|
dbOptions =>
|
||||||
{
|
{
|
||||||
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
|
dbOptions.MigrationsAssembly(typeof(TContext).Assembly.GetName().Name);
|
||||||
})
|
})
|
||||||
// https://github.com/efcore/EFCore.NamingConventions
|
|
||||||
.UseSnakeCaseNamingConvention();
|
.UseSnakeCaseNamingConvention();
|
||||||
|
|
||||||
|
// Suppress warnings for pending model changes
|
||||||
|
options.ConfigureWarnings(
|
||||||
|
w => w.Ignore(RelationalEventId.PendingModelChangesWarning));
|
||||||
});
|
});
|
||||||
|
|
||||||
services.AddScoped<IDbContext>(provider => provider.GetService<TContext>());
|
builder.Services.AddScoped<ISeedManager, SeedManager>();
|
||||||
|
builder.Services.AddScoped<IDbContext>(sp => sp.GetRequiredService<TContext>());
|
||||||
|
|
||||||
return services;
|
return builder.Services;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IApplicationBuilder UseMigration<TContext>(
|
|
||||||
this IApplicationBuilder app,
|
public static IApplicationBuilder UseMigration<TContext>(this IApplicationBuilder app)
|
||||||
IWebHostEnvironment env
|
|
||||||
)
|
|
||||||
where TContext : DbContext, IDbContext
|
where TContext : DbContext, IDbContext
|
||||||
{
|
{
|
||||||
MigrateDatabaseAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();
|
MigrateAsync<TContext>(app.ApplicationServices).GetAwaiter().GetResult();
|
||||||
|
|
||||||
if (!env.IsEnvironment("test"))
|
SeedAsync(app.ApplicationServices).GetAwaiter().GetResult();
|
||||||
{
|
|
||||||
SeedDataAsync(app.ApplicationServices).GetAwaiter().GetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ref: https://github.com/pdevito3/MessageBusTestingInMemHarness/blob/main/RecipeManagement/src/RecipeManagement/Databases/RecipesDbContext.cs
|
// ref: https://github.com/pdevito3/MessageBusTestingInMemHarness/blob/main/RecipeManagement/src/RecipeManagement/Databases/RecipesDbContext.cs
|
||||||
public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder)
|
public static void FilterSoftDeletedProperties(this ModelBuilder modelBuilder)
|
||||||
{
|
{
|
||||||
@ -114,23 +112,30 @@ public static class Extensions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task MigrateDatabaseAsync<TContext>(IServiceProvider serviceProvider)
|
private static async Task MigrateAsync<TContext>(IServiceProvider serviceProvider)
|
||||||
where TContext : DbContext, IDbContext
|
where TContext : DbContext, IDbContext
|
||||||
{
|
{
|
||||||
using var scope = serviceProvider.CreateScope();
|
await using var scope = serviceProvider.CreateAsyncScope();
|
||||||
|
|
||||||
var context = scope.ServiceProvider.GetRequiredService<TContext>();
|
var context = scope.ServiceProvider.GetRequiredService<TContext>();
|
||||||
await context.Database.MigrateAsync();
|
var logger = scope.ServiceProvider.GetRequiredService<ILogger<TContext>>();
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task SeedDataAsync(IServiceProvider serviceProvider)
|
var pendingMigrations = await context.Database.GetPendingMigrationsAsync();
|
||||||
{
|
|
||||||
using var scope = serviceProvider.CreateScope();
|
|
||||||
var seeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
|
||||||
|
|
||||||
foreach (var seeder in seeders)
|
if (pendingMigrations.Any())
|
||||||
{
|
{
|
||||||
await seeder.SeedAllAsync();
|
logger.LogInformation("Applying {Count} pending migrations...", pendingMigrations.Count());
|
||||||
|
|
||||||
|
await context.Database.MigrateAsync();
|
||||||
|
logger.LogInformation("Migrations applied successfully.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static async Task SeedAsync(IServiceProvider serviceProvider)
|
||||||
|
{
|
||||||
|
await using var scope = serviceProvider.CreateAsyncScope();
|
||||||
|
|
||||||
|
var seedersManager = scope.ServiceProvider.GetRequiredService<ISeedManager>();
|
||||||
|
|
||||||
|
await seedersManager.ExecuteSeedAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -4,4 +4,9 @@ namespace BuildingBlocks.EFCore
|
|||||||
{
|
{
|
||||||
Task SeedAllAsync();
|
Task SeedAllAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface ITestDataSeeder
|
||||||
|
{
|
||||||
|
Task SeedAllAsync();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
7
src/BuildingBlocks/EFCore/ISeedManager.cs
Normal file
7
src/BuildingBlocks/EFCore/ISeedManager.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace BuildingBlocks.EFCore;
|
||||||
|
|
||||||
|
public interface ISeedManager
|
||||||
|
{
|
||||||
|
Task ExecuteSeedAsync();
|
||||||
|
Task ExecuteTestSeedAsync();
|
||||||
|
}
|
||||||
42
src/BuildingBlocks/EFCore/SeedManagers.cs
Normal file
42
src/BuildingBlocks/EFCore/SeedManagers.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.EFCore;
|
||||||
|
|
||||||
|
public class SeedManager(
|
||||||
|
ILogger<SeedManager> logger,
|
||||||
|
IWebHostEnvironment env,
|
||||||
|
IServiceProvider serviceProvider
|
||||||
|
) : ISeedManager
|
||||||
|
{
|
||||||
|
public async Task ExecuteSeedAsync()
|
||||||
|
{
|
||||||
|
if (!env.IsEnvironment("test"))
|
||||||
|
{
|
||||||
|
await using var scope = serviceProvider.CreateAsyncScope();
|
||||||
|
var dataSeeders = scope.ServiceProvider.GetServices<IDataSeeder>();
|
||||||
|
|
||||||
|
foreach (var seeder in dataSeeders)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Seed {SeederName} is started.", seeder.GetType().Name);
|
||||||
|
await seeder.SeedAllAsync();
|
||||||
|
logger.LogInformation("Seed {SeederName} is completed.", seeder.GetType().Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExecuteTestSeedAsync()
|
||||||
|
{
|
||||||
|
await using var scope = serviceProvider.CreateAsyncScope();
|
||||||
|
var testDataSeeders = scope.ServiceProvider.GetServices<ITestDataSeeder>();
|
||||||
|
|
||||||
|
foreach (var testSeeder in testDataSeeders)
|
||||||
|
{
|
||||||
|
logger.LogInformation("Seed {SeederName} is started.", testSeeder.GetType().Name);
|
||||||
|
await testSeeder.SeedAllAsync();
|
||||||
|
logger.LogInformation("Seed {SeederName} is completed.", testSeeder.GetType().Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -24,15 +24,16 @@ public record EventStoreDBOptions(
|
|||||||
|
|
||||||
public static class EventStoreDBConfigExtensions
|
public static class EventStoreDBConfigExtensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddEventStoreDB(this IServiceCollection services, IConfiguration config,
|
public static IServiceCollection AddEventStoreDB(this IServiceCollection services, IConfiguration configuration,
|
||||||
EventStoreDBOptions? options = null)
|
EventStoreDBOptions? options = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
services
|
services
|
||||||
.AddSingleton(x =>
|
.AddSingleton(x =>
|
||||||
{
|
{
|
||||||
|
var aspireConnectionString = configuration.GetConnectionString("eventstore");
|
||||||
var eventStoreOptions = services.GetOptions<EventStoreOptions>(nameof(EventStoreOptions));
|
var eventStoreOptions = services.GetOptions<EventStoreOptions>(nameof(EventStoreOptions));
|
||||||
return new EventStoreClient(EventStoreClientSettings.Create(eventStoreOptions.ConnectionString));
|
return new EventStoreClient(EventStoreClientSettings.Create(aspireConnectionString ?? eventStoreOptions.ConnectionString));
|
||||||
})
|
})
|
||||||
.AddScoped(typeof(IEventStoreDBRepository<>), typeof(EventStoreDBRepository<>))
|
.AddScoped(typeof(IEventStoreDBRepository<>), typeof(EventStoreDBRepository<>))
|
||||||
.AddTransient<EventStoreDBSubscriptionToAll, EventStoreDBSubscriptionToAll>();
|
.AddTransient<EventStoreDBSubscriptionToAll, EventStoreDBSubscriptionToAll>();
|
||||||
|
|||||||
@ -25,4 +25,3 @@ namespace BuildingBlocks.EventStoreDB.Events
|
|||||||
public virtual void When(object @event) { }
|
public virtual void When(object @event) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,8 +3,6 @@ using BuildingBlocks.Core.Model;
|
|||||||
|
|
||||||
namespace BuildingBlocks.EventStoreDB.Events
|
namespace BuildingBlocks.EventStoreDB.Events
|
||||||
{
|
{
|
||||||
using Microsoft.FSharp.Control;
|
|
||||||
|
|
||||||
public interface IAggregateEventSourcing : IProjection, IEntity
|
public interface IAggregateEventSourcing : IProjection, IEntity
|
||||||
{
|
{
|
||||||
IReadOnlyList<IDomainEvent> DomainEvents { get; }
|
IReadOnlyList<IDomainEvent> DomainEvents { get; }
|
||||||
|
|||||||
@ -27,4 +27,3 @@ public class StreamEvent<T> : StreamEvent where T : notnull
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,23 +1,14 @@
|
|||||||
using System.Net;
|
using System.Net;
|
||||||
using OpenTelemetry.Trace;
|
|
||||||
|
|
||||||
namespace BuildingBlocks.Exception;
|
namespace BuildingBlocks.Exception;
|
||||||
|
|
||||||
public class AppException : CustomException
|
public class AppException : CustomException
|
||||||
{
|
{
|
||||||
public AppException(string message, int? code = null) : base(message, code: code)
|
public AppException(string message, HttpStatusCode statusCode = HttpStatusCode.BadRequest, int? code = null) : base(message, statusCode, code: code)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public AppException() : base()
|
public AppException(string message, System.Exception innerException, HttpStatusCode statusCode = HttpStatusCode.BadRequest, int? code = null) : base(message, innerException, statusCode, code)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public AppException(string message, HttpStatusCode statusCode, int? code = null) : base(message, statusCode, code)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public AppException(string message, System.Exception innerException, int? code = null) : base(message, innerException, code: code)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,10 +1,11 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace BuildingBlocks.Exception
|
namespace BuildingBlocks.Exception
|
||||||
{
|
{
|
||||||
public class BadRequestException : CustomException
|
public class BadRequestException : CustomException
|
||||||
{
|
{
|
||||||
public BadRequestException(string message, int? code = null) : base(message, code: code)
|
public BadRequestException(string message, int? code = null) : base(message, HttpStatusCode.BadRequest, code: code)
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace BuildingBlocks.Exception
|
namespace BuildingBlocks.Exception
|
||||||
{
|
{
|
||||||
public class ConflictException : CustomException
|
public class ConflictException : CustomException
|
||||||
{
|
{
|
||||||
public ConflictException(string message, int? code = null) : base(message, code: code)
|
public ConflictException(string message, int? code = null) : base(message, HttpStatusCode.Conflict, code: code)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ public class CustomException : System.Exception
|
|||||||
{
|
{
|
||||||
public CustomException(
|
public CustomException(
|
||||||
string message,
|
string message,
|
||||||
HttpStatusCode statusCode = HttpStatusCode.BadRequest,
|
HttpStatusCode statusCode = HttpStatusCode.InternalServerError,
|
||||||
int? code = null) : base(message)
|
int? code = null) : base(message)
|
||||||
{
|
{
|
||||||
StatusCode = statusCode;
|
StatusCode = statusCode;
|
||||||
@ -16,7 +16,7 @@ public class CustomException : System.Exception
|
|||||||
public CustomException(
|
public CustomException(
|
||||||
string message,
|
string message,
|
||||||
System.Exception innerException,
|
System.Exception innerException,
|
||||||
HttpStatusCode statusCode = HttpStatusCode.BadRequest,
|
HttpStatusCode statusCode = HttpStatusCode.InternalServerError,
|
||||||
int? code = null) : base(message, innerException)
|
int? code = null) : base(message, innerException)
|
||||||
{
|
{
|
||||||
StatusCode = statusCode;
|
StatusCode = statusCode;
|
||||||
@ -24,7 +24,7 @@ public class CustomException : System.Exception
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CustomException(
|
public CustomException(
|
||||||
HttpStatusCode statusCode = HttpStatusCode.BadRequest,
|
HttpStatusCode statusCode = HttpStatusCode.InternalServerError,
|
||||||
int? code = null) : base()
|
int? code = null) : base()
|
||||||
{
|
{
|
||||||
StatusCode = statusCode;
|
StatusCode = statusCode;
|
||||||
|
|||||||
16
src/BuildingBlocks/Exception/DomainException.cs
Normal file
16
src/BuildingBlocks/Exception/DomainException.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
using System.Net;
|
||||||
|
using BuildingBlocks.Exception;
|
||||||
|
|
||||||
|
namespace SmartCharging.Infrastructure.Exceptions
|
||||||
|
{
|
||||||
|
public class DomainException : CustomException
|
||||||
|
{
|
||||||
|
public DomainException(string message, HttpStatusCode statusCode = HttpStatusCode.BadRequest) : base(message, statusCode)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DomainException(string message, Exception innerException, HttpStatusCode statusCode = HttpStatusCode.BadRequest, int? code = null) : base(message, innerException, statusCode, code)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
|
||||||
@ -8,7 +7,7 @@ namespace BuildingBlocks.Exception
|
|||||||
{
|
{
|
||||||
public InternalServerException() : base() { }
|
public InternalServerException() : base() { }
|
||||||
|
|
||||||
public InternalServerException(string message, int? code) : base(message, HttpStatusCode.InternalServerError, code: code) { }
|
public InternalServerException(string message, int? code) : base(message, code: code) { }
|
||||||
|
|
||||||
public InternalServerException(string message, int? code = null, params object[] args)
|
public InternalServerException(string message, int? code = null, params object[] args)
|
||||||
: base(message: String.Format(CultureInfo.CurrentCulture, message, args, HttpStatusCode.InternalServerError, code))
|
: base(message: String.Format(CultureInfo.CurrentCulture, message, args, HttpStatusCode.InternalServerError, code))
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
|
using System.Net;
|
||||||
|
|
||||||
namespace BuildingBlocks.Exception
|
namespace BuildingBlocks.Exception
|
||||||
{
|
{
|
||||||
public class NotFoundException : CustomException
|
public class NotFoundException : CustomException
|
||||||
{
|
{
|
||||||
public NotFoundException(string message, int? code = null) : base(message, code: code)
|
public NotFoundException(string message, int? code = null) : base(message, HttpStatusCode.NotFound, code: code)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
using BuildingBlocks.Validation;
|
using System.Net;
|
||||||
|
|
||||||
namespace BuildingBlocks.Exception
|
namespace BuildingBlocks.Exception
|
||||||
{
|
{
|
||||||
public class ValidationException : CustomException
|
public class ValidationException : CustomException
|
||||||
{
|
{
|
||||||
public ValidationException(string message, int? code = null) : base(message, code: code)
|
public ValidationException(string message, int? code = null) : base(message, HttpStatusCode.BadRequest, code: code)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,50 +1,71 @@
|
|||||||
using BuildingBlocks.EFCore;
|
using BuildingBlocks.EFCore;
|
||||||
using BuildingBlocks.Logging;
|
using BuildingBlocks.EventStoreDB;
|
||||||
using BuildingBlocks.MassTransit;
|
using BuildingBlocks.MassTransit;
|
||||||
using BuildingBlocks.Mongo;
|
using BuildingBlocks.Mongo;
|
||||||
using BuildingBlocks.Web;
|
using BuildingBlocks.Web;
|
||||||
using HealthChecks.UI.Client;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
using Microsoft.Extensions.Diagnostics.HealthChecks;
|
||||||
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using MongoDB.Driver;
|
||||||
|
using RabbitMQ.Client;
|
||||||
|
|
||||||
namespace BuildingBlocks.HealthCheck;
|
namespace BuildingBlocks.HealthCheck;
|
||||||
|
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
|
private const string HealthEndpointPath = "/health";
|
||||||
|
private const string AlivenessEndpointPath = "/alive";
|
||||||
|
|
||||||
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services)
|
public static IServiceCollection AddCustomHealthCheck(this IServiceCollection services)
|
||||||
{
|
{
|
||||||
var healthOptions = services.GetOptions<HealthOptions>(nameof(HealthOptions));
|
var healthOptions = services.GetOptions<HealthOptions>(nameof(HealthOptions));
|
||||||
|
|
||||||
if (!healthOptions.Enabled)
|
if (healthOptions.Enabled)
|
||||||
return services;
|
|
||||||
|
|
||||||
var appOptions = services.GetOptions<AppOptions>(nameof(AppOptions));
|
|
||||||
var postgresOptions = services.GetOptions<PostgresOptions>(nameof(PostgresOptions));
|
|
||||||
var rabbitMqOptions = services.GetOptions<RabbitMqOptions>(nameof(RabbitMqOptions));
|
|
||||||
var mongoOptions = services.GetOptions<MongoOptions>(nameof(MongoOptions));
|
|
||||||
var logOptions = services.GetOptions<LogOptions>(nameof(LogOptions));
|
|
||||||
|
|
||||||
var healthChecksBuilder = services.AddHealthChecks()
|
|
||||||
.AddRabbitMQ(
|
|
||||||
rabbitConnectionString:
|
|
||||||
$"amqp://{rabbitMqOptions.UserName}:{rabbitMqOptions.Password}@{rabbitMqOptions.HostName}")
|
|
||||||
.AddElasticsearch(logOptions.Elastic.ElasticServiceUrl);
|
|
||||||
|
|
||||||
if (mongoOptions.ConnectionString is not null)
|
|
||||||
healthChecksBuilder.AddMongoDb(mongoOptions.ConnectionString);
|
|
||||||
|
|
||||||
if (postgresOptions.ConnectionString is not null)
|
|
||||||
healthChecksBuilder.AddNpgSql(postgresOptions.ConnectionString);
|
|
||||||
|
|
||||||
services.AddHealthChecksUI(setup =>
|
|
||||||
{
|
{
|
||||||
setup.SetEvaluationTimeInSeconds(60); // time in seconds between check
|
var appOptions = services.GetOptions<AppOptions>(nameof(AppOptions));
|
||||||
setup.AddHealthCheckEndpoint($"Basic Health Check - {appOptions.Name}", "/healthz");
|
var postgresOptions = services.GetOptions<PostgresOptions>(nameof(PostgresOptions));
|
||||||
}).AddInMemoryStorage();
|
var rabbitMqOptions = services.GetOptions<RabbitMqOptions>(nameof(RabbitMqOptions));
|
||||||
|
var eventStoreOptions = services.GetOptions<EventStoreOptions>(nameof(EventStoreOptions));
|
||||||
|
var mongoOptions = services.GetOptions<MongoOptions>(nameof(MongoOptions));
|
||||||
|
|
||||||
|
var healthChecksBuilder = services.AddHealthChecks()
|
||||||
|
// Add a default liveness check to ensure app is responsive
|
||||||
|
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"])
|
||||||
|
.AddRabbitMQ(
|
||||||
|
serviceProvider =>
|
||||||
|
{
|
||||||
|
var factory = new ConnectionFactory
|
||||||
|
{
|
||||||
|
Uri = new Uri($"amqp://{rabbitMqOptions.UserName}:{rabbitMqOptions.Password}@{rabbitMqOptions.HostName}"),
|
||||||
|
};
|
||||||
|
return factory.CreateConnectionAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(mongoOptions.ConnectionString))
|
||||||
|
{
|
||||||
|
healthChecksBuilder.AddMongoDb(
|
||||||
|
clientFactory: _ => new MongoClient(mongoOptions.ConnectionString),
|
||||||
|
name: "MongoDB-Health",
|
||||||
|
failureStatus: HealthStatus.Unhealthy,
|
||||||
|
timeout: TimeSpan.FromSeconds(10));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(postgresOptions.ConnectionString))
|
||||||
|
healthChecksBuilder.AddNpgSql(postgresOptions.ConnectionString);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(eventStoreOptions.ConnectionString))
|
||||||
|
healthChecksBuilder.AddEventStore(eventStoreOptions.ConnectionString);
|
||||||
|
|
||||||
|
services.AddHealthChecksUI(setup =>
|
||||||
|
{
|
||||||
|
setup.SetEvaluationTimeInSeconds(60); // time in seconds between check
|
||||||
|
setup.AddHealthCheckEndpoint($"Self Check - {appOptions.Name}", HealthEndpointPath);
|
||||||
|
}).AddInMemoryStorage();
|
||||||
|
}
|
||||||
|
|
||||||
|
services.AddHealthChecks().AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,26 +73,17 @@ public static class Extensions
|
|||||||
{
|
{
|
||||||
var healthOptions = app.Configuration.GetOptions<HealthOptions>(nameof(HealthOptions));
|
var healthOptions = app.Configuration.GetOptions<HealthOptions>(nameof(HealthOptions));
|
||||||
|
|
||||||
if (!healthOptions.Enabled)
|
if (app.Environment.IsDevelopment())
|
||||||
return app;
|
{
|
||||||
|
app.MapHealthChecks(HealthEndpointPath);
|
||||||
app.UseHealthChecks("/healthz",
|
app.MapHealthChecks(AlivenessEndpointPath, new HealthCheckOptions
|
||||||
new HealthCheckOptions
|
|
||||||
{
|
|
||||||
Predicate = _ => true,
|
|
||||||
ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse,
|
|
||||||
ResultStatusCodes =
|
|
||||||
{
|
|
||||||
[HealthStatus.Healthy] = StatusCodes.Status200OK,
|
|
||||||
[HealthStatus.Degraded] = StatusCodes.Status500InternalServerError,
|
|
||||||
[HealthStatus.Unhealthy] = StatusCodes.Status503ServiceUnavailable
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.UseHealthChecksUI(options =>
|
|
||||||
{
|
{
|
||||||
options.ApiPath = "/healthcheck";
|
Predicate = r => r.Tags.Contains("live"),
|
||||||
options.UIPath = "/healthcheck-ui";
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (healthOptions.Enabled)
|
||||||
|
app.MapHealthChecksUI(options => options.UIPath = "/health-ui");
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,47 +1,77 @@
|
|||||||
|
using BuildingBlocks.Constants;
|
||||||
using BuildingBlocks.Web;
|
using BuildingBlocks.Web;
|
||||||
|
using Duende.IdentityServer.EntityFramework.Entities;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace BuildingBlocks.Jwt;
|
|
||||||
|
|
||||||
using Duende.IdentityServer.EntityFramework.Entities;
|
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
|
|
||||||
public static class JwtExtensions
|
namespace BuildingBlocks.Jwt
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddJwt(this IServiceCollection services)
|
public static class JwtExtensions
|
||||||
{
|
{
|
||||||
var jwtOptions = services.GetOptions<JwtBearerOptions>("Jwt");
|
public static IServiceCollection AddJwt(this IServiceCollection services)
|
||||||
|
|
||||||
services.AddAuthentication(o =>
|
|
||||||
{
|
{
|
||||||
o.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
// Bind Jwt settings from configuration
|
||||||
o.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
var jwtOptions = services.GetOptions<JwtBearerOptions>("Jwt");
|
||||||
})
|
|
||||||
.AddCookie(cfg => cfg.SlidingExpiration = true)
|
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
|
.AddJwtBearer(options =>
|
||||||
{
|
{
|
||||||
options.Authority = jwtOptions.Authority;
|
options.Authority = jwtOptions.Authority;
|
||||||
|
options.Audience = jwtOptions.Audience;
|
||||||
|
options.RequireHttpsMetadata = false;
|
||||||
|
|
||||||
options.TokenValidationParameters = new TokenValidationParameters
|
options.TokenValidationParameters = new TokenValidationParameters
|
||||||
{
|
{
|
||||||
ValidateAudience = false,
|
ValidateIssuer = true,
|
||||||
ClockSkew = TimeSpan.FromSeconds(2) // For prevent add default value (5min) to life time token!
|
ValidIssuers = [jwtOptions.Authority],
|
||||||
|
ValidateAudience = true,
|
||||||
|
ValidAudiences = [jwtOptions.Audience],
|
||||||
|
ValidateLifetime = true,
|
||||||
|
ClockSkew = TimeSpan.FromSeconds(2), // Reduce default clock skew
|
||||||
|
// For IdentityServer4/Duende, we should also validate the signing key
|
||||||
|
ValidateIssuerSigningKey = true,
|
||||||
|
NameClaimType = "name", // Map "name" claim to User.Identity.Name
|
||||||
|
RoleClaimType = "role", // Map "role" claim to User.IsInRole()
|
||||||
};
|
};
|
||||||
options.RequireHttpsMetadata = jwtOptions.RequireHttpsMetadata;
|
|
||||||
options.MetadataAddress = jwtOptions.MetadataAddress;
|
// Preserve ALL claims from the token (including "sub")
|
||||||
|
options.MapInboundClaims = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(jwtOptions.Audience))
|
|
||||||
{
|
|
||||||
services.AddAuthorization(options =>
|
|
||||||
options.AddPolicy(nameof(ApiScope), policy =>
|
|
||||||
{
|
|
||||||
policy.RequireAuthenticatedUser();
|
|
||||||
policy.RequireClaim("scope", jwtOptions.Audience);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return services;
|
services.AddAuthorization(
|
||||||
|
options =>
|
||||||
|
{
|
||||||
|
options.AddPolicy(
|
||||||
|
nameof(ApiScope),
|
||||||
|
policy =>
|
||||||
|
{
|
||||||
|
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
|
||||||
|
policy.RequireAuthenticatedUser();
|
||||||
|
policy.RequireClaim("scope", jwtOptions.Audience);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Role-based policies
|
||||||
|
options.AddPolicy(
|
||||||
|
IdentityConstant.Role.Admin,
|
||||||
|
x =>
|
||||||
|
{
|
||||||
|
x.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
|
||||||
|
x.RequireRole(IdentityConstant.Role.Admin);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
options.AddPolicy(
|
||||||
|
IdentityConstant.Role.User,
|
||||||
|
x =>
|
||||||
|
{
|
||||||
|
x.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
|
||||||
|
x.RequireRole(IdentityConstant.Role.User);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,8 +0,0 @@
|
|||||||
namespace BuildingBlocks.Logging;
|
|
||||||
|
|
||||||
public class ElasticOptions
|
|
||||||
{
|
|
||||||
public bool Enabled { get; set; }
|
|
||||||
public string ElasticServiceUrl { get; set; }
|
|
||||||
public string ElasticSearchIndex { get; set; }
|
|
||||||
}
|
|
||||||
@ -1,89 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Text;
|
|
||||||
using BuildingBlocks.Web;
|
|
||||||
using Microsoft.AspNetCore.Builder;
|
|
||||||
using Microsoft.AspNetCore.Hosting;
|
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Serilog;
|
|
||||||
using Serilog.Events;
|
|
||||||
using Serilog.Exceptions;
|
|
||||||
using Serilog.Sinks.Elasticsearch;
|
|
||||||
using Serilog.Sinks.SpectreConsole;
|
|
||||||
|
|
||||||
namespace BuildingBlocks.Logging
|
|
||||||
{
|
|
||||||
public static class Extensions
|
|
||||||
{
|
|
||||||
public static WebApplicationBuilder AddCustomSerilog(this WebApplicationBuilder builder, IWebHostEnvironment env)
|
|
||||||
{
|
|
||||||
builder.Host.UseSerilog((context, services, loggerConfiguration) =>
|
|
||||||
{
|
|
||||||
var environment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
|
|
||||||
var logOptions = context.Configuration.GetSection(nameof(LogOptions)).Get<LogOptions>();
|
|
||||||
var appOptions = context.Configuration.GetSection(nameof(AppOptions)).Get<AppOptions>();
|
|
||||||
|
|
||||||
|
|
||||||
var logLevel = Enum.TryParse<LogEventLevel>(logOptions.Level, true, out var level)
|
|
||||||
? level
|
|
||||||
: LogEventLevel.Information;
|
|
||||||
|
|
||||||
loggerConfiguration
|
|
||||||
.MinimumLevel.Is(logLevel)
|
|
||||||
.WriteTo.SpectreConsole(logOptions.LogTemplate, logLevel)
|
|
||||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Information)
|
|
||||||
// Only show ef-core information in error level
|
|
||||||
.MinimumLevel.Override("Microsoft.EntityFrameworkCore", LogEventLevel.Error)
|
|
||||||
// Filter out ASP.NET Core infrastructure logs that are Information and below
|
|
||||||
.MinimumLevel.Override("Microsoft.AspNetCore", LogEventLevel.Warning)
|
|
||||||
.Enrich.WithExceptionDetails()
|
|
||||||
.Enrich.FromLogContext()
|
|
||||||
.ReadFrom.Configuration(context.Configuration);
|
|
||||||
|
|
||||||
if (logOptions.Elastic is { Enabled: true })
|
|
||||||
{
|
|
||||||
loggerConfiguration.WriteTo.Elasticsearch(
|
|
||||||
new ElasticsearchSinkOptions(new Uri(logOptions.Elastic.ElasticServiceUrl))
|
|
||||||
{
|
|
||||||
AutoRegisterTemplate = true,
|
|
||||||
IndexFormat = $"{appOptions.Name}-{environment?.ToLower(CultureInfo.CurrentCulture)}"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (logOptions?.Sentry is { Enabled: true })
|
|
||||||
{
|
|
||||||
var minimumBreadcrumbLevel = Enum.TryParse<LogEventLevel>(logOptions.Level, true, out var minBreadcrumbLevel)
|
|
||||||
? minBreadcrumbLevel
|
|
||||||
: LogEventLevel.Information;
|
|
||||||
|
|
||||||
var minimumEventLevel = Enum.TryParse<LogEventLevel>(logOptions.Sentry.MinimumEventLevel, true, out var minEventLevel)
|
|
||||||
? minEventLevel
|
|
||||||
: LogEventLevel.Error;
|
|
||||||
|
|
||||||
loggerConfiguration.WriteTo.Sentry(o =>
|
|
||||||
{
|
|
||||||
o.Dsn = logOptions.Sentry.Dsn;
|
|
||||||
o.MinimumBreadcrumbLevel = minimumBreadcrumbLevel;
|
|
||||||
o.MinimumEventLevel = minimumEventLevel;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logOptions.File is { Enabled: true })
|
|
||||||
{
|
|
||||||
var root = env.ContentRootPath;
|
|
||||||
Directory.CreateDirectory(Path.Combine(root, "logs"));
|
|
||||||
|
|
||||||
var path = string.IsNullOrWhiteSpace(logOptions.File.Path) ? "logs/.txt" : logOptions.File.Path;
|
|
||||||
if (!Enum.TryParse<RollingInterval>(logOptions.File.Interval, true, out var interval))
|
|
||||||
{
|
|
||||||
interval = RollingInterval.Day;
|
|
||||||
}
|
|
||||||
|
|
||||||
loggerConfiguration.WriteTo.File(path, rollingInterval: interval, encoding: Encoding.UTF8, outputTemplate: logOptions.LogTemplate);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return builder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,8 +0,0 @@
|
|||||||
namespace BuildingBlocks.Logging;
|
|
||||||
|
|
||||||
public class FileOptions
|
|
||||||
{
|
|
||||||
public bool Enabled { get; set; }
|
|
||||||
public string Path { get; set; }
|
|
||||||
public string Interval { get; set; }
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
using System.Security.Claims;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Serilog;
|
|
||||||
|
|
||||||
namespace BuildingBlocks.Logging;
|
|
||||||
|
|
||||||
public static class LogEnrichHelper
|
|
||||||
{
|
|
||||||
//ref: https://andrewlock.net/using-serilog-aspnetcore-in-asp-net-core-3-logging-the-selected-endpoint-name-with-serilog/
|
|
||||||
public static void EnrichFromRequest(IDiagnosticContext diagnosticContext, HttpContext httpContext)
|
|
||||||
{
|
|
||||||
var request = httpContext.Request;
|
|
||||||
|
|
||||||
// Set all the common properties available for every request
|
|
||||||
diagnosticContext.Set("Host", request.Host);
|
|
||||||
diagnosticContext.Set("Protocol", request.Protocol);
|
|
||||||
diagnosticContext.Set("Scheme", request.Scheme);
|
|
||||||
|
|
||||||
// Only set it if available. You're not sending sensitive data in a querystring right?!
|
|
||||||
if (request.QueryString.HasValue)
|
|
||||||
{
|
|
||||||
diagnosticContext.Set("QueryString", request.QueryString.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the content-type of the Response at this point
|
|
||||||
diagnosticContext.Set("ContentType", httpContext.Response.ContentType);
|
|
||||||
|
|
||||||
// Retrieve the IEndpointFeature selected for the request
|
|
||||||
var endpoint = httpContext.GetEndpoint();
|
|
||||||
if (endpoint is object) // endpoint != null
|
|
||||||
{
|
|
||||||
diagnosticContext.Set("EndpointName", endpoint.DisplayName);
|
|
||||||
}
|
|
||||||
|
|
||||||
diagnosticContext.Set("ClientIP", httpContext.Connection.RemoteIpAddress);
|
|
||||||
|
|
||||||
diagnosticContext.Set("UserId", request.HttpContext.User.FindFirstValue(ClaimTypes.NameIdentifier));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
namespace BuildingBlocks.Logging
|
|
||||||
{
|
|
||||||
public class LogOptions
|
|
||||||
{
|
|
||||||
public string Level { get; set; }
|
|
||||||
public ElasticOptions Elastic { get; set; }
|
|
||||||
|
|
||||||
public SentryOptions Sentry { get; set; }
|
|
||||||
public FileOptions File { get; set; }
|
|
||||||
public string LogTemplate { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
namespace BuildingBlocks.Logging;
|
|
||||||
|
|
||||||
public class SentryOptions
|
|
||||||
{
|
|
||||||
public bool Enabled { get; set; }
|
|
||||||
public string Dsn { get; set; }
|
|
||||||
public string MinimumBreadcrumbLevel { get; set; }
|
|
||||||
public string MinimumEventLevel { get; set; }
|
|
||||||
}
|
|
||||||
@ -7,10 +7,10 @@ namespace BuildingBlocks.Mapster;
|
|||||||
|
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddCustomMapster(this IServiceCollection services, Assembly assembly)
|
public static IServiceCollection AddCustomMapster(this IServiceCollection services, params Assembly[] assemblies)
|
||||||
{
|
{
|
||||||
var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
|
var typeAdapterConfig = TypeAdapterConfig.GlobalSettings;
|
||||||
typeAdapterConfig.Scan(assembly);
|
typeAdapterConfig.Scan(assemblies);
|
||||||
var mapperConfig = new Mapper(typeAdapterConfig);
|
var mapperConfig = new Mapper(typeAdapterConfig);
|
||||||
services.AddSingleton<IMapper>(mapperConfig);
|
services.AddSingleton<IMapper>(mapperConfig);
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +1,8 @@
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using BuildingBlocks.Core.Event;
|
|
||||||
using BuildingBlocks.Web;
|
using BuildingBlocks.Web;
|
||||||
using Humanizer;
|
|
||||||
using MassTransit;
|
using MassTransit;
|
||||||
using Microsoft.AspNetCore.Hosting;
|
using Microsoft.AspNetCore.Hosting;
|
||||||
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
|
||||||
@ -13,48 +12,99 @@ using Exception;
|
|||||||
|
|
||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddCustomMassTransit(this IServiceCollection services,
|
public static IServiceCollection AddCustomMassTransit(
|
||||||
IWebHostEnvironment env, Assembly assembly)
|
this IServiceCollection services,
|
||||||
|
IWebHostEnvironment env,
|
||||||
|
TransportType transportType,
|
||||||
|
params Assembly[] assembly
|
||||||
|
)
|
||||||
{
|
{
|
||||||
services.AddValidateOptions<RabbitMqOptions>();
|
services.AddValidateOptions<RabbitMqOptions>();
|
||||||
|
|
||||||
if (env.IsEnvironment("test"))
|
if (env.IsEnvironment("test"))
|
||||||
{
|
{
|
||||||
services.AddMassTransitTestHarness(configure =>
|
services.AddMassTransitTestHarness(
|
||||||
{
|
configure =>
|
||||||
SetupMasstransitConfigurations(services, configure, assembly);
|
{
|
||||||
});
|
SetupMasstransitConfigurations(services, configure, transportType, assembly);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
services.AddMassTransit(configure => { SetupMasstransitConfigurations(services, configure, assembly); });
|
services.AddMassTransit(
|
||||||
|
configure =>
|
||||||
|
{
|
||||||
|
SetupMasstransitConfigurations(services, configure, transportType, assembly);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return services;
|
return services;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void SetupMasstransitConfigurations(IServiceCollection services,
|
private static void SetupMasstransitConfigurations(
|
||||||
IBusRegistrationConfigurator configure, Assembly assembly)
|
IServiceCollection services,
|
||||||
|
IBusRegistrationConfigurator configure,
|
||||||
|
TransportType transportType,
|
||||||
|
params Assembly[] assembly
|
||||||
|
)
|
||||||
{
|
{
|
||||||
configure.AddConsumers(assembly);
|
configure.AddConsumers(assembly);
|
||||||
configure.AddSagaStateMachines(assembly);
|
configure.AddSagaStateMachines(assembly);
|
||||||
configure.AddSagas(assembly);
|
configure.AddSagas(assembly);
|
||||||
configure.AddActivities(assembly);
|
configure.AddActivities(assembly);
|
||||||
|
|
||||||
configure.UsingRabbitMq((context, configurator) =>
|
switch (transportType)
|
||||||
{
|
{
|
||||||
var rabbitMqOptions = services.GetOptions<RabbitMqOptions>(nameof(RabbitMqOptions));
|
case TransportType.RabbitMq:
|
||||||
|
configure.UsingRabbitMq(
|
||||||
|
(context, configurator) =>
|
||||||
|
{
|
||||||
|
var configuration = context.GetRequiredService<IConfiguration>();
|
||||||
|
|
||||||
configurator.Host(rabbitMqOptions?.HostName, rabbitMqOptions?.Port ?? 5672, "/", h =>
|
var aspireConnectionString = configuration.GetConnectionString("rabbitmq");
|
||||||
{
|
|
||||||
h.Username(rabbitMqOptions?.UserName);
|
|
||||||
h.Password(rabbitMqOptions?.Password);
|
|
||||||
});
|
|
||||||
|
|
||||||
configurator.ConfigureEndpoints(context);
|
if (!string.IsNullOrEmpty(aspireConnectionString))
|
||||||
|
{
|
||||||
|
configurator.Host(new Uri(aspireConnectionString));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var rabbitMqOptions = services.GetOptions<RabbitMqOptions>(nameof(RabbitMqOptions));
|
||||||
|
|
||||||
configurator.UseMessageRetry(AddRetryConfiguration);
|
ArgumentNullException.ThrowIfNull(rabbitMqOptions);
|
||||||
});
|
|
||||||
|
configurator.Host(
|
||||||
|
rabbitMqOptions?.HostName,
|
||||||
|
rabbitMqOptions?.Port ?? 5672,
|
||||||
|
"/",
|
||||||
|
h =>
|
||||||
|
{
|
||||||
|
h.Username(rabbitMqOptions.UserName);
|
||||||
|
h.Password(rabbitMqOptions.Password);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
configurator.ConfigureEndpoints(context);
|
||||||
|
|
||||||
|
configurator.UseMessageRetry(AddRetryConfiguration);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
case TransportType.InMemory:
|
||||||
|
configure.UsingInMemory(
|
||||||
|
(context, configurator) =>
|
||||||
|
{
|
||||||
|
configurator.ConfigureEndpoints(context);
|
||||||
|
configurator.UseMessageRetry(AddRetryConfiguration);
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(
|
||||||
|
nameof(transportType),
|
||||||
|
transportType,
|
||||||
|
message: null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void AddRetryConfiguration(IRetryConfigurator retryConfigurator)
|
private static void AddRetryConfiguration(IRetryConfigurator retryConfigurator)
|
||||||
@ -64,6 +114,7 @@ public static class Extensions
|
|||||||
TimeSpan.FromMilliseconds(200),
|
TimeSpan.FromMilliseconds(200),
|
||||||
TimeSpan.FromMinutes(120),
|
TimeSpan.FromMinutes(120),
|
||||||
TimeSpan.FromMilliseconds(200))
|
TimeSpan.FromMilliseconds(200))
|
||||||
.Ignore<ValidationException>(); // don't retry if we have invalid data and message goes to _error queue masstransit
|
.Ignore<
|
||||||
|
ValidationException>(); // don't retry if we have invalid data and message goes to _error queue masstransit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
7
src/BuildingBlocks/MassTransit/TransportType.cs
Normal file
7
src/BuildingBlocks/MassTransit/TransportType.cs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
namespace BuildingBlocks.MassTransit;
|
||||||
|
|
||||||
|
public enum TransportType
|
||||||
|
{
|
||||||
|
RabbitMq,
|
||||||
|
InMemory
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
using Microsoft.Extensions.Configuration;
|
using Microsoft.Extensions.Configuration;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
@ -8,18 +9,25 @@ namespace BuildingBlocks.Mongo
|
|||||||
public static class Extensions
|
public static class Extensions
|
||||||
{
|
{
|
||||||
public static IServiceCollection AddMongoDbContext<TContext>(
|
public static IServiceCollection AddMongoDbContext<TContext>(
|
||||||
this IServiceCollection services, IConfiguration configuration, Action<MongoOptions>? configurator = null)
|
this WebApplicationBuilder builder, Action<MongoOptions>? configurator = null)
|
||||||
where TContext : MongoDbContext
|
where TContext : MongoDbContext
|
||||||
{
|
{
|
||||||
return services.AddMongoDbContext<TContext, TContext>(configuration, configurator);
|
return builder.Services.AddMongoDbContext<TContext, TContext>(builder.Configuration, configurator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IServiceCollection AddMongoDbContext<TContextService, TContextImplementation>(
|
public static IServiceCollection AddMongoDbContext<TContextService, TContextImplementation>(
|
||||||
this IServiceCollection services, IConfiguration configuration, Action<MongoOptions>? configurator = null)
|
this IServiceCollection services, IConfiguration configuration, Action<MongoOptions>? configurator = null)
|
||||||
where TContextService : IMongoDbContext
|
where TContextService : IMongoDbContext
|
||||||
where TContextImplementation : MongoDbContext, TContextService
|
where TContextImplementation : MongoDbContext, TContextService
|
||||||
{
|
{
|
||||||
services.Configure<MongoOptions>(configuration.GetSection(nameof(MongoOptions)));
|
// Configure MongoOptions with Aspire-aware defaults
|
||||||
|
services.AddOptions<MongoOptions>()
|
||||||
|
.Bind(configuration.GetSection(nameof(MongoOptions)))
|
||||||
|
.PostConfigure(options =>
|
||||||
|
{
|
||||||
|
var aspireConnectionString = configuration.GetConnectionString("mongo");
|
||||||
|
options.ConnectionString = aspireConnectionString ?? options.ConnectionString;
|
||||||
|
});
|
||||||
|
|
||||||
if (configurator is { })
|
if (configurator is { })
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using MongoDB.Bson;
|
using MongoDB.Bson;
|
||||||
|
using MongoDB.Bson.Serialization;
|
||||||
using MongoDB.Bson.Serialization.Conventions;
|
using MongoDB.Bson.Serialization.Conventions;
|
||||||
|
using MongoDB.Bson.Serialization.Serializers;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
|
|
||||||
namespace BuildingBlocks.Mongo;
|
namespace BuildingBlocks.Mongo;
|
||||||
@ -14,6 +16,12 @@ public class MongoDbContext : IMongoDbContext
|
|||||||
public IMongoDatabase Database { get; }
|
public IMongoDatabase Database { get; }
|
||||||
public IMongoClient MongoClient { get; }
|
public IMongoClient MongoClient { get; }
|
||||||
protected readonly IList<Func<Task>> _commands;
|
protected readonly IList<Func<Task>> _commands;
|
||||||
|
private static readonly bool _isSerializerRegisterd;
|
||||||
|
|
||||||
|
static MongoDbContext()
|
||||||
|
{
|
||||||
|
BsonSerializer.RegisterSerializer(new GuidSerializer(BsonType.String));
|
||||||
|
}
|
||||||
|
|
||||||
public MongoDbContext(IOptions<MongoOptions> options)
|
public MongoDbContext(IOptions<MongoOptions> options)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using BuildingBlocks.Core.Model;
|
using BuildingBlocks.Core.Model;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
|
using MongoDB.Driver.Linq;
|
||||||
|
|
||||||
namespace BuildingBlocks.Mongo;
|
namespace BuildingBlocks.Mongo;
|
||||||
|
|
||||||
|
|||||||
@ -29,4 +29,3 @@ public class MongoUnitOfWork<TContext> : IMongoUnitOfWork<TContext>, ITransactio
|
|||||||
|
|
||||||
public void Dispose() => Context.Dispose();
|
public void Dispose() => Context.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
55
src/BuildingBlocks/OpenApi/Extensions.cs
Normal file
55
src/BuildingBlocks/OpenApi/Extensions.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using Microsoft.AspNetCore.Builder;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Scalar.AspNetCore;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenApi
|
||||||
|
{
|
||||||
|
public static class Extensions
|
||||||
|
{
|
||||||
|
// ref: https://github.com/dotnet/eShop/blob/main/src/eShop.ServiceDefaults/OpenApi.Extensions.cs
|
||||||
|
public static IServiceCollection AddAspnetOpenApi(this IServiceCollection services)
|
||||||
|
{
|
||||||
|
string[] versions = ["v1"];
|
||||||
|
|
||||||
|
foreach (var description in versions)
|
||||||
|
{
|
||||||
|
services.AddOpenApi(
|
||||||
|
description,
|
||||||
|
options =>
|
||||||
|
{
|
||||||
|
options.AddDocumentTransformer<SecuritySchemeDocumentTransformer>();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return services;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IApplicationBuilder UseAspnetOpenApi(this WebApplication app)
|
||||||
|
{
|
||||||
|
app.MapOpenApi();
|
||||||
|
|
||||||
|
app.UseSwaggerUI(
|
||||||
|
options =>
|
||||||
|
{
|
||||||
|
var descriptions = app.DescribeApiVersions();
|
||||||
|
|
||||||
|
// build a swagger endpoint for each discovered API version
|
||||||
|
foreach (var description in descriptions)
|
||||||
|
{
|
||||||
|
var openApiUrl = $"/openapi/{description.GroupName}.json";
|
||||||
|
var name = description.GroupName.ToUpperInvariant();
|
||||||
|
options.SwaggerEndpoint(openApiUrl, name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add scalar ui
|
||||||
|
app.MapScalarApiReference(
|
||||||
|
redocOptions =>
|
||||||
|
{
|
||||||
|
redocOptions.WithOpenApiRoutePattern("/openapi/{documentName}.json");
|
||||||
|
});
|
||||||
|
|
||||||
|
return app;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
using Microsoft.AspNetCore.OpenApi;
|
||||||
|
using Microsoft.OpenApi;
|
||||||
|
|
||||||
|
public class SecuritySchemeDocumentTransformer : IOpenApiDocumentTransformer
|
||||||
|
{
|
||||||
|
public Task TransformAsync(
|
||||||
|
OpenApiDocument document,
|
||||||
|
OpenApiDocumentTransformerContext context,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
|
{
|
||||||
|
document.Components ??= new OpenApiComponents();
|
||||||
|
|
||||||
|
// Initialize with the correct interface type
|
||||||
|
document.Components.SecuritySchemes ??= new Dictionary<string, IOpenApiSecurityScheme>();
|
||||||
|
|
||||||
|
var securitySchemes = new Dictionary<string, IOpenApiSecurityScheme>
|
||||||
|
{
|
||||||
|
["Bearer"] = new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Name = "Authorization",
|
||||||
|
Type = SecuritySchemeType.Http,
|
||||||
|
Scheme = "bearer",
|
||||||
|
BearerFormat = "JWT",
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Description =
|
||||||
|
"Enter 'Bearer' [space] and your token in the text input below.\n\nExample: 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'",
|
||||||
|
},
|
||||||
|
["ApiKey"] = new OpenApiSecurityScheme
|
||||||
|
{
|
||||||
|
Name = "X-API-KEY",
|
||||||
|
Type = SecuritySchemeType.ApiKey,
|
||||||
|
In = ParameterLocation.Header,
|
||||||
|
Description = "Enter your API key in the text input below.\n\nExample: '12345-abcdef'",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
foreach (var (key, scheme) in securitySchemes)
|
||||||
|
{
|
||||||
|
if (!document.Components.SecuritySchemes.ContainsKey(key))
|
||||||
|
{
|
||||||
|
document.Components.SecuritySchemes.Add(key, scheme);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,44 +0,0 @@
|
|||||||
using BuildingBlocks.Utils;
|
|
||||||
using BuildingBlocks.Web;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
|
||||||
using OpenTelemetry.Resources;
|
|
||||||
using OpenTelemetry.Trace;
|
|
||||||
|
|
||||||
namespace BuildingBlocks.OpenTelemetry;
|
|
||||||
|
|
||||||
using global::OpenTelemetry.Metrics;
|
|
||||||
|
|
||||||
public static class Extensions
|
|
||||||
{
|
|
||||||
public static IServiceCollection AddCustomOpenTelemetry(this IServiceCollection services)
|
|
||||||
{
|
|
||||||
services.AddOpenTelemetry()
|
|
||||||
.WithTracing(builder => builder
|
|
||||||
.AddGrpcClientInstrumentation()
|
|
||||||
.AddMassTransitInstrumentation()
|
|
||||||
.AddAspNetCoreInstrumentation()
|
|
||||||
.AddHttpClientInstrumentation()
|
|
||||||
.SetResourceBuilder(ResourceBuilder.CreateDefault()
|
|
||||||
.AddService(services.GetOptions<AppOptions>("AppOptions").Name))
|
|
||||||
.AddJaegerExporter())
|
|
||||||
.WithMetrics(builder =>
|
|
||||||
{
|
|
||||||
builder.AddPrometheusExporter();
|
|
||||||
builder.AddMeter(
|
|
||||||
"Microsoft.AspNetCore.Hosting",
|
|
||||||
"Microsoft.AspNetCore.Server.Kestrel"
|
|
||||||
);
|
|
||||||
builder.AddView("request-duration",
|
|
||||||
new ExplicitBucketHistogramConfiguration
|
|
||||||
{
|
|
||||||
Boundaries = new[]
|
|
||||||
{
|
|
||||||
0, 0.005, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return services;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
175
src/BuildingBlocks/OpenTelemetryCollector/ActivityExtensions.cs
Normal file
175
src/BuildingBlocks/OpenTelemetryCollector/ActivityExtensions.cs
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenTelemetryCollector;
|
||||||
|
|
||||||
|
internal static class ActivityExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Retrieves the tags from the parent of the current Activity, if available.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="activity">The current Activity.</param>
|
||||||
|
/// <returns>A dictionary containing the parent tags, or an empty dictionary if no parent tags are available.</returns>
|
||||||
|
public static Dictionary<string, object?> GetParentTags(this Activity activity)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(activity);
|
||||||
|
|
||||||
|
var parentTags = new Dictionary<string, object?>();
|
||||||
|
|
||||||
|
// Check if the current activity has a parent
|
||||||
|
var parentActivity = activity.Parent;
|
||||||
|
|
||||||
|
if (parentActivity != null)
|
||||||
|
{
|
||||||
|
foreach (var tag in parentActivity.Tags)
|
||||||
|
{
|
||||||
|
parentTags[tag.Key] = tag.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If no parent Activity is available, check for links
|
||||||
|
foreach (var link in activity.Links)
|
||||||
|
{
|
||||||
|
// Extract tags from the first link's context (assuming it's the parent-like context)
|
||||||
|
if (link.Tags != null)
|
||||||
|
{
|
||||||
|
foreach (var tag in link.Tags)
|
||||||
|
{
|
||||||
|
parentTags[tag.Key] = tag.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break after processing the first link, as there should only be one parent context.
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return parentTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts important information from an Activity into an ActivityInfo object.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="activity">The Activity from which to extract information.</param>
|
||||||
|
/// <returns>An ActivityInfo object containing the extracted information.</returns>
|
||||||
|
public static ActivityInfo ExtractImportantInformation(this Activity activity)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(activity);
|
||||||
|
|
||||||
|
var activityInfo = new ActivityInfo
|
||||||
|
{
|
||||||
|
Name = activity.DisplayName,
|
||||||
|
StartTime = activity.StartTimeUtc,
|
||||||
|
Duration = activity.Duration,
|
||||||
|
Status =
|
||||||
|
activity.Tags.FirstOrDefault(tag => tag.Key == TelemetryTags.Tracing.Otel.StatusCode).Value
|
||||||
|
?? "Unknown",
|
||||||
|
StatusDescription = activity
|
||||||
|
.Tags.FirstOrDefault(tag => tag.Key == TelemetryTags.Tracing.Otel.StatusDescription)
|
||||||
|
.Value,
|
||||||
|
Tags = activity.Tags.ToDictionary(tag => tag.Key, tag => tag.Value),
|
||||||
|
Events = activity
|
||||||
|
.Events.Select(e => new ActivityEventInfo
|
||||||
|
{
|
||||||
|
Name = e.Name,
|
||||||
|
Timestamp = e.Timestamp,
|
||||||
|
Attributes = e.Tags.ToDictionary(tag => tag.Key, tag => tag.Value),
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
|
TraceId = activity.TraceId.ToString(),
|
||||||
|
SpanId = activity.SpanId.ToString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return activityInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets an "OK" status on the provided Activity, indicating a successful operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="activity">The Activity to update.</param>
|
||||||
|
/// <param name="description">An optional description of the successful operation.</param>
|
||||||
|
/// <returns>The updated Activity with the status and tags set.</returns>
|
||||||
|
public static Activity SetOkStatus(this Activity activity, string? description = null)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(activity);
|
||||||
|
|
||||||
|
// Set the status of the activity to "OK"
|
||||||
|
activity.SetStatus(ActivityStatusCode.Ok, description);
|
||||||
|
|
||||||
|
// Add telemetry tags for status
|
||||||
|
activity.SetTag(
|
||||||
|
TelemetryTags.Tracing.Otel.StatusCode,
|
||||||
|
nameof(ActivityStatusCode.Ok).ToUpper(CultureInfo.InvariantCulture)
|
||||||
|
);
|
||||||
|
if (!string.IsNullOrEmpty(description))
|
||||||
|
activity.SetTag(TelemetryTags.Tracing.Otel.StatusDescription, description);
|
||||||
|
|
||||||
|
return activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets an "Unset" status on the provided Activity, indicating no explicit status was applied.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="activity">The Activity to update.</param>
|
||||||
|
/// <param name="description">An optional description of the unset status.</param>
|
||||||
|
/// <returns>The updated Activity with the status and tags set.</returns>
|
||||||
|
public static Activity SetUnsetStatus(this Activity activity, string? description = null)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(activity);
|
||||||
|
|
||||||
|
// Set the status of the activity to "Unset"
|
||||||
|
activity.SetStatus(ActivityStatusCode.Unset, description);
|
||||||
|
|
||||||
|
// Add telemetry tags for status
|
||||||
|
activity.SetTag(
|
||||||
|
TelemetryTags.Tracing.Otel.StatusCode,
|
||||||
|
nameof(ActivityStatusCode.Unset).ToUpper(CultureInfo.InvariantCulture)
|
||||||
|
);
|
||||||
|
if (!string.IsNullOrEmpty(description))
|
||||||
|
activity.SetTag(TelemetryTags.Tracing.Otel.StatusDescription, description);
|
||||||
|
|
||||||
|
return activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets an "Error" status on the provided Activity, indicating a failed operation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="activity">The Activity to update.</param>
|
||||||
|
/// <param name="exception">The exception associated with the error, if available.</param>
|
||||||
|
/// <param name="description">An optional description of the error.</param>
|
||||||
|
/// <returns>The updated Activity with the status, error details, and tags set.</returns>
|
||||||
|
public static Activity SetErrorStatus(this Activity activity, System.Exception? exception, string? description = null)
|
||||||
|
{
|
||||||
|
ArgumentNullException.ThrowIfNull(activity);
|
||||||
|
|
||||||
|
// Add telemetry tags for status
|
||||||
|
activity.SetTag(
|
||||||
|
TelemetryTags.Tracing.Otel.StatusCode,
|
||||||
|
nameof(ActivityStatusCode.Error).ToUpper(CultureInfo.InvariantCulture)
|
||||||
|
);
|
||||||
|
if (!string.IsNullOrEmpty(description))
|
||||||
|
activity.SetTag(TelemetryTags.Tracing.Otel.StatusDescription, description);
|
||||||
|
|
||||||
|
// Add detailed exception tags, if an exception is provided
|
||||||
|
return activity.SetExceptionTags(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
// See https://opentelemetry.io/docs/specs/otel/trace/semantic_conventions/exceptions/
|
||||||
|
public static Activity SetExceptionTags(this Activity activity, System.Exception? ex)
|
||||||
|
{
|
||||||
|
if (ex is null)
|
||||||
|
{
|
||||||
|
return activity;
|
||||||
|
}
|
||||||
|
|
||||||
|
activity.SetStatus(ActivityStatusCode.Error);
|
||||||
|
activity.AddException(ex);
|
||||||
|
|
||||||
|
activity.AddTag(TelemetryTags.Tracing.Exception.Message, ex.Message);
|
||||||
|
activity.AddTag(TelemetryTags.Tracing.Exception.Stacktrace, ex.ToString());
|
||||||
|
activity.AddTag(TelemetryTags.Tracing.Exception.Type, ex.GetType().FullName);
|
||||||
|
|
||||||
|
return activity;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
src/BuildingBlocks/OpenTelemetryCollector/ActivityInfo.cs
Normal file
29
src/BuildingBlocks/OpenTelemetryCollector/ActivityInfo.cs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenTelemetryCollector;
|
||||||
|
|
||||||
|
public class ActivityInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
public DateTime StartTime { get; set; }
|
||||||
|
public TimeSpan Duration { get; set; }
|
||||||
|
public string Status { get; set; } = default!;
|
||||||
|
public string? StatusDescription { get; set; }
|
||||||
|
public IDictionary<string, string?> Tags { get; set; } = new Dictionary<string, string?>();
|
||||||
|
public IList<ActivityEventInfo> Events { get; set; } = new List<ActivityEventInfo>();
|
||||||
|
public string TraceId { get; set; } = default!;
|
||||||
|
public string SpanId { get; set; } = default!;
|
||||||
|
|
||||||
|
public string? ParentId { get; set; }
|
||||||
|
|
||||||
|
public ActivityContext? Parent { get; set; }
|
||||||
|
|
||||||
|
public ActivityKind Kind { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ActivityEventInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = default!;
|
||||||
|
public DateTimeOffset Timestamp { get; set; }
|
||||||
|
public IDictionary<string, object?> Attributes { get; set; } = new Dictionary<string, object?>();
|
||||||
|
}
|
||||||
@ -0,0 +1,85 @@
|
|||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
|
using BuildingBlocks.OpenTelemetryCollector.CoreDiagnostics.Commands;
|
||||||
|
using BuildingBlocks.OpenTelemetryCollector.CoreDiagnostics.Query;
|
||||||
|
using MediatR;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenTelemetryCollector.Behaviors;
|
||||||
|
|
||||||
|
public class ObservabilityPipelineBehavior<TRequest, TResponse>(
|
||||||
|
CommandHandlerActivity commandActivity,
|
||||||
|
CommandHandlerMetrics commandMetrics,
|
||||||
|
QueryHandlerActivity queryActivity,
|
||||||
|
QueryHandlerMetrics queryMetrics
|
||||||
|
) : IPipelineBehavior<TRequest, TResponse>
|
||||||
|
where TRequest : IRequest<TResponse>
|
||||||
|
where TResponse : notnull
|
||||||
|
{
|
||||||
|
public async Task<TResponse> Handle(TRequest message, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
var isCommand = message is IQuery<TResponse>;
|
||||||
|
var isQuery = message is ICommand<TResponse>;
|
||||||
|
|
||||||
|
if (isCommand)
|
||||||
|
{
|
||||||
|
commandMetrics.StartExecuting<TRequest>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isQuery)
|
||||||
|
{
|
||||||
|
queryMetrics.StartExecuting<TRequest>();
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (isCommand)
|
||||||
|
{
|
||||||
|
var commandResult = await commandActivity.Execute<TRequest, TResponse>(
|
||||||
|
async (activity, ct) =>
|
||||||
|
{
|
||||||
|
var response = await next();
|
||||||
|
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
commandMetrics.FinishExecuting<TRequest>();
|
||||||
|
|
||||||
|
return commandResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isQuery)
|
||||||
|
{
|
||||||
|
var queryResult = await queryActivity.Execute<TRequest, TResponse>(
|
||||||
|
async (activity, ct) =>
|
||||||
|
{
|
||||||
|
var response = await next();
|
||||||
|
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
|
||||||
|
queryMetrics.FinishExecuting<TRequest>();
|
||||||
|
|
||||||
|
return queryResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (System.Exception)
|
||||||
|
{
|
||||||
|
if (isQuery)
|
||||||
|
{
|
||||||
|
queryMetrics.FailedCommand<TRequest>();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCommand)
|
||||||
|
{
|
||||||
|
commandMetrics.FailedCommand<TRequest>();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await next();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,86 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
|
using BuildingBlocks.OpenTelemetryCollector.DiagnosticsProvider;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenTelemetryCollector.CoreDiagnostics.Commands;
|
||||||
|
|
||||||
|
public class CommandHandlerActivity(IDiagnosticsProvider diagnosticsProvider)
|
||||||
|
{
|
||||||
|
public async Task Execute<TCommand>(
|
||||||
|
Func<Activity?, CancellationToken, Task> action,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var commandName = typeof(TCommand).Name;
|
||||||
|
var handlerType = typeof(TCommand)
|
||||||
|
.Assembly.GetTypes()
|
||||||
|
.FirstOrDefault(t =>
|
||||||
|
t.GetInterfaces()
|
||||||
|
.Any(i =>
|
||||||
|
i.IsGenericType
|
||||||
|
&& i.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)
|
||||||
|
&& i.GetGenericArguments()[0] == typeof(TCommand)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var commandHandlerName = handlerType?.Name;
|
||||||
|
|
||||||
|
// usually we use class/methodName
|
||||||
|
var activityName = $"{ObservabilityConstant.Components.CommandHandler}.{commandHandlerName}/{commandName}";
|
||||||
|
|
||||||
|
await diagnosticsProvider.ExecuteActivityAsync(
|
||||||
|
new CreateActivityInfo
|
||||||
|
{
|
||||||
|
Name = activityName,
|
||||||
|
ActivityKind = ActivityKind.Consumer,
|
||||||
|
Tags = new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.Command, commandName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandType, typeof(TCommand).FullName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandler, commandHandlerName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandlerType, handlerType?.FullName },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TResult> Execute<TCommand, TResult>(
|
||||||
|
Func<Activity?, CancellationToken, Task<TResult>> action,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var commandName = typeof(TCommand).Name;
|
||||||
|
var handlerType = typeof(TCommand)
|
||||||
|
.Assembly.GetTypes()
|
||||||
|
.FirstOrDefault(t =>
|
||||||
|
t.GetInterfaces()
|
||||||
|
.Any(i =>
|
||||||
|
i.IsGenericType
|
||||||
|
&& i.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)
|
||||||
|
&& i.GetGenericArguments()[0] == typeof(TCommand)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var commandHandlerName = handlerType?.Name;
|
||||||
|
|
||||||
|
// usually we use class/methodName
|
||||||
|
var activityName = $"{ObservabilityConstant.Components.CommandHandler}.{commandHandlerName}/{commandName}";
|
||||||
|
|
||||||
|
return await diagnosticsProvider.ExecuteActivityAsync(
|
||||||
|
new CreateActivityInfo
|
||||||
|
{
|
||||||
|
Name = activityName,
|
||||||
|
ActivityKind = ActivityKind.Consumer,
|
||||||
|
Tags = new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.Command, commandName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandType, typeof(TCommand).FullName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandler, commandHandlerName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandlerType, handlerType?.FullName },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,157 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.Metrics;
|
||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
|
using BuildingBlocks.OpenTelemetryCollector;
|
||||||
|
using BuildingBlocks.OpenTelemetryCollector.DiagnosticsProvider;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenTelemetryCollector.CoreDiagnostics.Commands;
|
||||||
|
|
||||||
|
public class CommandHandlerMetrics
|
||||||
|
{
|
||||||
|
private readonly UpDownCounter<long> _activeCommandsCounter;
|
||||||
|
private readonly Counter<long> _totalCommandsNumber;
|
||||||
|
private readonly Counter<long> _successCommandsNumber;
|
||||||
|
private readonly Counter<long> _failedCommandsNumber;
|
||||||
|
private readonly Histogram<double> _handlerDuration;
|
||||||
|
|
||||||
|
private Stopwatch _timer;
|
||||||
|
|
||||||
|
public CommandHandlerMetrics(IDiagnosticsProvider diagnosticsProvider)
|
||||||
|
{
|
||||||
|
_activeCommandsCounter = diagnosticsProvider.Meter.CreateUpDownCounter<long>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.ActiveCount,
|
||||||
|
unit: "{active_commands}",
|
||||||
|
description: "Number of commands currently being handled"
|
||||||
|
);
|
||||||
|
|
||||||
|
_totalCommandsNumber = diagnosticsProvider.Meter.CreateCounter<long>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.TotalExecutedCount,
|
||||||
|
unit: "{total_commands}",
|
||||||
|
description: "Total number of executed command that sent to command handlers"
|
||||||
|
);
|
||||||
|
|
||||||
|
_successCommandsNumber = diagnosticsProvider.Meter.CreateCounter<long>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.SuccessCount,
|
||||||
|
unit: "{success_commands}",
|
||||||
|
description: "Number commands that handled successfully"
|
||||||
|
);
|
||||||
|
|
||||||
|
_failedCommandsNumber = diagnosticsProvider.Meter.CreateCounter<long>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.FaildCount,
|
||||||
|
unit: "{failed_commands}",
|
||||||
|
description: "Number commands that handled with errors"
|
||||||
|
);
|
||||||
|
|
||||||
|
_handlerDuration = diagnosticsProvider.Meter.CreateHistogram<double>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.HandlerDuration,
|
||||||
|
unit: "s",
|
||||||
|
description: "Measures the duration of command handler"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartExecuting<TCommand>()
|
||||||
|
{
|
||||||
|
var commandName = typeof(TCommand).Name;
|
||||||
|
var handlerType = typeof(TCommand)
|
||||||
|
.Assembly.GetTypes()
|
||||||
|
.FirstOrDefault(t =>
|
||||||
|
t.GetInterfaces()
|
||||||
|
.Any(i =>
|
||||||
|
i.IsGenericType
|
||||||
|
&& i.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)
|
||||||
|
&& i.GetGenericArguments()[0] == typeof(TCommand)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var commandHandlerName = handlerType?.Name;
|
||||||
|
|
||||||
|
var tags = new TagList
|
||||||
|
{
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.Command, commandName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandType, typeof(TCommand).FullName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandler, commandHandlerName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandlerType, handlerType?.FullName },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_activeCommandsCounter.Enabled)
|
||||||
|
{
|
||||||
|
_activeCommandsCounter.Add(1, tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_totalCommandsNumber.Enabled)
|
||||||
|
{
|
||||||
|
_totalCommandsNumber.Add(1, tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
_timer = Stopwatch.StartNew();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FinishExecuting<TCommand>()
|
||||||
|
{
|
||||||
|
var commandName = typeof(TCommand).Name;
|
||||||
|
var handlerType = typeof(TCommand)
|
||||||
|
.Assembly.GetTypes()
|
||||||
|
.FirstOrDefault(t =>
|
||||||
|
t.GetInterfaces()
|
||||||
|
.Any(i =>
|
||||||
|
i.IsGenericType
|
||||||
|
&& i.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)
|
||||||
|
&& i.GetGenericArguments()[0] == typeof(TCommand)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var commandHandlerName = handlerType?.Name;
|
||||||
|
|
||||||
|
var tags = new TagList
|
||||||
|
{
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.Command, commandName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandType, typeof(TCommand).FullName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandler, commandHandlerName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandlerType, handlerType?.FullName },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_activeCommandsCounter.Enabled)
|
||||||
|
{
|
||||||
|
_activeCommandsCounter.Add(-1, tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_handlerDuration.Enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var elapsedTimeSeconds = _timer.Elapsed.Seconds;
|
||||||
|
|
||||||
|
_handlerDuration.Record(elapsedTimeSeconds, tags);
|
||||||
|
|
||||||
|
if (_successCommandsNumber.Enabled)
|
||||||
|
{
|
||||||
|
_successCommandsNumber.Add(1, tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FailedCommand<TCommand>()
|
||||||
|
{
|
||||||
|
var commandName = typeof(TCommand).Name;
|
||||||
|
var handlerType = typeof(TCommand)
|
||||||
|
.Assembly.GetTypes()
|
||||||
|
.FirstOrDefault(t =>
|
||||||
|
t.GetInterfaces()
|
||||||
|
.Any(i =>
|
||||||
|
i.IsGenericType
|
||||||
|
&& i.GetGenericTypeDefinition() == typeof(ICommandHandler<,>)
|
||||||
|
&& i.GetGenericArguments()[0] == typeof(TCommand)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var commandHandlerName = handlerType?.Name;
|
||||||
|
|
||||||
|
var tags = new TagList
|
||||||
|
{
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.Command, commandName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandType, typeof(TCommand).FullName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandler, commandHandlerName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Commands.CommandHandlerType, handlerType?.FullName },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_failedCommandsNumber.Enabled)
|
||||||
|
{
|
||||||
|
_failedCommandsNumber.Add(1, tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
|
using BuildingBlocks.OpenTelemetryCollector.DiagnosticsProvider;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenTelemetryCollector.CoreDiagnostics.Query;
|
||||||
|
|
||||||
|
public class QueryHandlerActivity(IDiagnosticsProvider diagnosticsProvider)
|
||||||
|
{
|
||||||
|
public async Task<TResult> Execute<TQuery, TResult>(
|
||||||
|
Func<Activity?, CancellationToken, Task<TResult>> action,
|
||||||
|
CancellationToken cancellationToken
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var queryName = typeof(TQuery).Name;
|
||||||
|
var handlerType = typeof(TQuery)
|
||||||
|
.Assembly.GetTypes()
|
||||||
|
.FirstOrDefault(t =>
|
||||||
|
t.GetInterfaces()
|
||||||
|
.Any(i =>
|
||||||
|
i.IsGenericType
|
||||||
|
&& i.GetGenericTypeDefinition() == typeof(IQueryHandler<,>)
|
||||||
|
&& i.GetGenericArguments()[0] == typeof(TQuery)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var queryHandlerName = handlerType?.Name;
|
||||||
|
|
||||||
|
// usually we use class/methodName
|
||||||
|
var activityName = $"{ObservabilityConstant.Components.QueryHandler}.{queryHandlerName}/{queryName}";
|
||||||
|
|
||||||
|
return await diagnosticsProvider.ExecuteActivityAsync(
|
||||||
|
new CreateActivityInfo
|
||||||
|
{
|
||||||
|
Name = activityName,
|
||||||
|
ActivityKind = ActivityKind.Consumer,
|
||||||
|
Tags = new Dictionary<string, object?>
|
||||||
|
{
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.Query, queryName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryType, typeof(TQuery).FullName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryHandler, queryHandlerName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryHandlerType, handlerType?.FullName },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
action,
|
||||||
|
cancellationToken
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,156 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.Metrics;
|
||||||
|
using BuildingBlocks.Core.CQRS;
|
||||||
|
using BuildingBlocks.OpenTelemetryCollector.DiagnosticsProvider;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenTelemetryCollector.CoreDiagnostics.Query;
|
||||||
|
|
||||||
|
public class QueryHandlerMetrics
|
||||||
|
{
|
||||||
|
private readonly UpDownCounter<long> _activeQueriesCounter;
|
||||||
|
private readonly Counter<long> _totalQueriesNumber;
|
||||||
|
private readonly Counter<long> _successQueriesNumber;
|
||||||
|
private readonly Counter<long> _failedQueriesNumber;
|
||||||
|
private readonly Histogram<double> _handlerDuration;
|
||||||
|
|
||||||
|
private Stopwatch _timer;
|
||||||
|
|
||||||
|
public QueryHandlerMetrics(IDiagnosticsProvider diagnosticsProvider)
|
||||||
|
{
|
||||||
|
_activeQueriesCounter = diagnosticsProvider.Meter.CreateUpDownCounter<long>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.ActiveCount,
|
||||||
|
unit: "{active_queries}",
|
||||||
|
description: "Number of queries currently being handled"
|
||||||
|
);
|
||||||
|
|
||||||
|
_totalQueriesNumber = diagnosticsProvider.Meter.CreateCounter<long>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.TotalExecutedCount,
|
||||||
|
unit: "{total_queries}",
|
||||||
|
description: "Total number of executed query that sent to query handlers"
|
||||||
|
);
|
||||||
|
|
||||||
|
_successQueriesNumber = diagnosticsProvider.Meter.CreateCounter<long>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.SuccessCount,
|
||||||
|
unit: "{success_queries}",
|
||||||
|
description: "Number queries that handled successfully"
|
||||||
|
);
|
||||||
|
|
||||||
|
_failedQueriesNumber = diagnosticsProvider.Meter.CreateCounter<long>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.FaildCount,
|
||||||
|
unit: "{failed_queries}",
|
||||||
|
description: "Number queries that handled with errors"
|
||||||
|
);
|
||||||
|
|
||||||
|
_handlerDuration = diagnosticsProvider.Meter.CreateHistogram<double>(
|
||||||
|
TelemetryTags.Metrics.Application.Commands.HandlerDuration,
|
||||||
|
unit: "s",
|
||||||
|
description: "Measures the duration of query handler"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartExecuting<TQuery>()
|
||||||
|
{
|
||||||
|
var queryName = typeof(TQuery).Name;
|
||||||
|
var handlerType = typeof(TQuery)
|
||||||
|
.Assembly.GetTypes()
|
||||||
|
.FirstOrDefault(t =>
|
||||||
|
t.GetInterfaces()
|
||||||
|
.Any(i =>
|
||||||
|
i.IsGenericType
|
||||||
|
&& i.GetGenericTypeDefinition() == typeof(IQueryHandler<,>)
|
||||||
|
&& i.GetGenericArguments()[0] == typeof(TQuery)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var queryHandlerName = handlerType?.Name;
|
||||||
|
|
||||||
|
var tags = new TagList
|
||||||
|
{
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.Query, queryName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryType, typeof(TQuery).FullName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryHandler, queryHandlerName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryHandlerType, handlerType?.FullName },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_activeQueriesCounter.Enabled)
|
||||||
|
{
|
||||||
|
_activeQueriesCounter.Add(1, tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_totalQueriesNumber.Enabled)
|
||||||
|
{
|
||||||
|
_totalQueriesNumber.Add(1, tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
_timer = Stopwatch.StartNew();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FinishExecuting<TQuery>()
|
||||||
|
{
|
||||||
|
var queryName = typeof(TQuery).Name;
|
||||||
|
var handlerType = typeof(TQuery)
|
||||||
|
.Assembly.GetTypes()
|
||||||
|
.FirstOrDefault(t =>
|
||||||
|
t.GetInterfaces()
|
||||||
|
.Any(i =>
|
||||||
|
i.IsGenericType
|
||||||
|
&& i.GetGenericTypeDefinition() == typeof(IQueryHandler<,>)
|
||||||
|
&& i.GetGenericArguments()[0] == typeof(TQuery)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var queryHandlerName = handlerType?.Name;
|
||||||
|
|
||||||
|
var tags = new TagList
|
||||||
|
{
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.Query, queryName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryType, typeof(TQuery).FullName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryHandler, queryHandlerName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryHandlerType, handlerType?.FullName },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_activeQueriesCounter.Enabled)
|
||||||
|
{
|
||||||
|
_activeQueriesCounter.Add(-1, tags);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_handlerDuration.Enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var elapsedTimeSeconds = _timer.Elapsed.Seconds;
|
||||||
|
|
||||||
|
_handlerDuration.Record(elapsedTimeSeconds, tags);
|
||||||
|
|
||||||
|
if (_successQueriesNumber.Enabled)
|
||||||
|
{
|
||||||
|
_successQueriesNumber.Add(1, tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void FailedCommand<TQuery>()
|
||||||
|
{
|
||||||
|
var queryName = typeof(TQuery).Name;
|
||||||
|
var handlerType = typeof(TQuery)
|
||||||
|
.Assembly.GetTypes()
|
||||||
|
.FirstOrDefault(t =>
|
||||||
|
t.GetInterfaces()
|
||||||
|
.Any(i =>
|
||||||
|
i.IsGenericType
|
||||||
|
&& i.GetGenericTypeDefinition() == typeof(IQueryHandler<,>)
|
||||||
|
&& i.GetGenericArguments()[0] == typeof(TQuery)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
var queryHandlerName = handlerType?.Name;
|
||||||
|
|
||||||
|
var tags = new TagList
|
||||||
|
{
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.Query, queryName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryType, typeof(TQuery).FullName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryHandler, queryHandlerName },
|
||||||
|
{ TelemetryTags.Tracing.Application.Queries.QueryHandlerType, handlerType?.FullName },
|
||||||
|
};
|
||||||
|
|
||||||
|
if (_failedQueriesNumber.Enabled)
|
||||||
|
{
|
||||||
|
_failedQueriesNumber.Add(1, tags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenTelemetryCollector;
|
||||||
|
|
||||||
|
public class CreateActivityInfo
|
||||||
|
{
|
||||||
|
public required string Name { get; set; }
|
||||||
|
public IDictionary<string, object?> Tags { get; set; } = new Dictionary<string, object?>();
|
||||||
|
public string? ParentId { get; set; }
|
||||||
|
public ActivityContext? Parent { get; set; }
|
||||||
|
public required ActivityKind ActivityKind = ActivityKind.Internal;
|
||||||
|
}
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using System.Diagnostics.Metrics;
|
||||||
|
using System.Reflection;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace BuildingBlocks.OpenTelemetryCollector.DiagnosticsProvider;
|
||||||
|
|
||||||
|
public class CustomeDiagnosticsProvider(IMeterFactory meterFactory, IOptions<ObservabilityOptions> options)
|
||||||
|
: IDiagnosticsProvider
|
||||||
|
{
|
||||||
|
private readonly Version? _version = Assembly.GetCallingAssembly().GetName().Version;
|
||||||
|
private ActivitySource? _activitySource;
|
||||||
|
private ActivityListener? _listener;
|
||||||
|
private Meter? _meter;
|
||||||
|
|
||||||
|
public string InstrumentationName { get; } = options.Value.InstrumentationName ?? throw new ArgumentException("InstrumentationName cannot be null or empty.");
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/dotnet/core/diagnostics/distributed-tracing-instrumentation-walkthroughs
|
||||||
|
public ActivitySource ActivitySource
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_activitySource != null)
|
||||||
|
return _activitySource;
|
||||||
|
|
||||||
|
_activitySource = new(InstrumentationName, _version?.ToString());
|
||||||
|
|
||||||
|
_listener = new ActivityListener
|
||||||
|
{
|
||||||
|
ShouldListenTo = x => true,
|
||||||
|
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllDataAndRecorded,
|
||||||
|
};
|
||||||
|
ActivitySource.AddActivityListener(_listener);
|
||||||
|
|
||||||
|
return _activitySource;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics-instrumentation
|
||||||
|
public Meter Meter
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (_meter != null)
|
||||||
|
return _meter;
|
||||||
|
|
||||||
|
_meter = meterFactory.Create(InstrumentationName, _version?.ToString());
|
||||||
|
|
||||||
|
return _meter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task ExecuteActivityAsync(
|
||||||
|
CreateActivityInfo createActivityInfo,
|
||||||
|
Func<Activity?, CancellationToken, Task> action,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!options.Value.TracingEnabled)
|
||||||
|
{
|
||||||
|
await action(null, cancellationToken);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
using var activity =
|
||||||
|
ActivitySource
|
||||||
|
.CreateActivity(
|
||||||
|
name: $"{InstrumentationName}.{createActivityInfo.Name}",
|
||||||
|
kind: createActivityInfo.ActivityKind,
|
||||||
|
parentContext: createActivityInfo.Parent ?? default,
|
||||||
|
idFormat: ActivityIdFormat.W3C,
|
||||||
|
tags: createActivityInfo.Tags
|
||||||
|
)
|
||||||
|
?.Start() ?? Activity.Current;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await action(activity!, cancellationToken);
|
||||||
|
activity?.SetOkStatus();
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
activity?.SetErrorStatus(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<TResult?> ExecuteActivityAsync<TResult>(
|
||||||
|
CreateActivityInfo createActivityInfo,
|
||||||
|
Func<Activity?, CancellationToken, Task<TResult>> action,
|
||||||
|
CancellationToken cancellationToken = default
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (!options.Value.TracingEnabled)
|
||||||
|
{
|
||||||
|
return await action(null, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
using var activity =
|
||||||
|
ActivitySource
|
||||||
|
.CreateActivity(
|
||||||
|
name: $"{InstrumentationName}.{createActivityInfo.Name}",
|
||||||
|
kind: createActivityInfo.ActivityKind,
|
||||||
|
parentContext: createActivityInfo.Parent ?? default,
|
||||||
|
idFormat: ActivityIdFormat.W3C,
|
||||||
|
tags: createActivityInfo.Tags
|
||||||
|
)
|
||||||
|
?.Start() ?? Activity.Current;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = await action(activity!, cancellationToken);
|
||||||
|
|
||||||
|
activity?.SetOkStatus();
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (System.Exception ex)
|
||||||
|
{
|
||||||
|
activity?.SetErrorStatus(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_listener?.Dispose();
|
||||||
|
_meter?.Dispose();
|
||||||
|
_activitySource?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user