diff --git a/.gitignore b/.gitignore index 4ce6fdd..9ba4100 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ ## ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore +*.tar + # User-specific files *.rsuser *.suo diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..6bfba01 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/FlightSystem2/bin/Debug/netcoreapp3.0/FlightSystem.Api.dll", + "args": [], + "cwd": "${workspaceFolder}/FlightSystem2", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/.vscode/solution-explorer/class.cs-template b/.vscode/solution-explorer/class.cs-template new file mode 100644 index 0000000..015da46 --- /dev/null +++ b/.vscode/solution-explorer/class.cs-template @@ -0,0 +1,8 @@ +using System; + +namespace {{namespace}} +{ + public class {{name}} + { + } +} diff --git a/.vscode/solution-explorer/class.ts-template b/.vscode/solution-explorer/class.ts-template new file mode 100644 index 0000000..ff2edef --- /dev/null +++ b/.vscode/solution-explorer/class.ts-template @@ -0,0 +1,3 @@ +export class {{name}} { + +} \ No newline at end of file diff --git a/.vscode/solution-explorer/class.vb-template b/.vscode/solution-explorer/class.vb-template new file mode 100644 index 0000000..38ef67f --- /dev/null +++ b/.vscode/solution-explorer/class.vb-template @@ -0,0 +1,9 @@ +Imports System + +Namespace {{namespace}} + + Public Class {{name}} + + End Class + +End Namespace diff --git a/.vscode/solution-explorer/default.ts-template b/.vscode/solution-explorer/default.ts-template new file mode 100644 index 0000000..04af870 --- /dev/null +++ b/.vscode/solution-explorer/default.ts-template @@ -0,0 +1,3 @@ +export default {{name}} { + +} \ No newline at end of file diff --git a/.vscode/solution-explorer/enum.cs-template b/.vscode/solution-explorer/enum.cs-template new file mode 100644 index 0000000..7d4cdee --- /dev/null +++ b/.vscode/solution-explorer/enum.cs-template @@ -0,0 +1,8 @@ +using System; + +namespace {{namespace}} +{ + public enum {{name}} + { + } +} diff --git a/.vscode/solution-explorer/interface.cs-template b/.vscode/solution-explorer/interface.cs-template new file mode 100644 index 0000000..6b5dec1 --- /dev/null +++ b/.vscode/solution-explorer/interface.cs-template @@ -0,0 +1,8 @@ +using System; + +namespace {{namespace}} +{ + public interface {{name}} + { + } +} diff --git a/.vscode/solution-explorer/interface.ts-template b/.vscode/solution-explorer/interface.ts-template new file mode 100644 index 0000000..3ea404b --- /dev/null +++ b/.vscode/solution-explorer/interface.ts-template @@ -0,0 +1,3 @@ +export interface {{name}} { + +} \ No newline at end of file diff --git a/.vscode/solution-explorer/template-list.json b/.vscode/solution-explorer/template-list.json new file mode 100644 index 0000000..2849622 --- /dev/null +++ b/.vscode/solution-explorer/template-list.json @@ -0,0 +1,46 @@ +{ + "templates": [ + { + "name": "Class", + "extension": "cs", + "file": "./class.cs-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Interface", + "extension": "cs", + "file": "./interface.cs-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Enum", + "extension": "cs", + "file": "./enum.cs-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Class", + "extension": "ts", + "file": "./class.ts-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Interface", + "extension": "ts", + "file": "./interface.ts-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Default", + "extension": "ts", + "file": "./default.ts-template", + "parameters": "./template-parameters.js" + }, + { + "name": "Class", + "extension": "vb", + "file": "./class.vb-template", + "parameters": "./template-parameters.js" + } + ] +} \ No newline at end of file diff --git a/.vscode/solution-explorer/template-parameters.js b/.vscode/solution-explorer/template-parameters.js new file mode 100644 index 0000000..daba8b2 --- /dev/null +++ b/.vscode/solution-explorer/template-parameters.js @@ -0,0 +1,17 @@ +var path = require("path"); + +module.exports = function(filename, projectPath, folderPath) { + var namespace = "Unknown"; + if (projectPath) { + namespace = path.basename(projectPath, path.extname(projectPath)); + if (folderPath) { + namespace += "." + folderPath.replace(path.dirname(projectPath), "").substring(1).replace(/[\\\/]/g, "."); + } + namespace = namespace.replace(/[\\\-]/g, "_"); + } + + return { + namespace: namespace, + name: path.basename(filename, path.extname(filename)) + } +}; \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..deb2b4b --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/FlightSystem2/FlightSystem.Api.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/FlightSystem2/FlightSystem.Api.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/FlightSystem2/FlightSystem.Api.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/FlightSystem2.ndproj b/FlightSystem2.ndproj deleted file mode 100644 index 3cba6e7..0000000 --- a/FlightSystem2.ndproj +++ /dev/null @@ -1,12517 +0,0 @@ - - - .\NDependOut - - - - - - - C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.0.0 - C:\Program Files\dotnet\sdk\NuGetFallbackFolder - C:\Users\Paulius\.nuget\packages - - True - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1 - 1 - 0 - 0 - $ManDay$ - 50 - USD - After - 18 - 240 - 8 - 5 - 10 - 20 - 50 - 1200000000 - 12000000000 - 72000000000 - 360000000000 - - - - - Quality Gates Evolution -from qg in QualityGates -let qgBaseline = qg.OlderVersion() -let relyOnDiff = qgBaseline == null -let evolution = relyOnDiff ? (TrendIcon?)null : - // When a quality gate relies on diff between now and baseline - // it is not executed against the baseline - qg.ValueDiff() == 0d ? - TrendIcon.Constant : - (qg.ValueDiff() > 0 ? - ( qg.MoreIsBad ? TrendIcon.RedUp: TrendIcon.GreenUp) : - (!qg.MoreIsBad ? TrendIcon.RedDown: TrendIcon.GreenDown)) -select new { qg, - Evolution = evolution, - - BaselineStatus = relyOnDiff? (QualityGateStatus?) null : qgBaseline.Status, - Status = qg.Status, - - BaselineValue = relyOnDiff? (null) : qgBaseline.ValueString, - Value = qg.ValueString, -} - -// -// Show quality gates evolution between baseline and now. -// -// When a quality gate relies on diff between now and baseline (like *New Debt since Baseline*) -// it is not executed against the baseline and as a consequence its evolution is not available. -// -// Double-click a quality gate for editing. -// ]]> - -failif value < 70% -warnif value < 80% -codeBase.PercentageCoverage - -// -// Code coverage is a measure used to describe the degree to which the source code of a program -// is tested by a particular test suite. A program with high code coverage, measured as a percentage, -// has had more of its source code executed during testing which suggests it has a lower chance of -// containing undetected software bugs compared to a program with low code coverage. -// -// Code coverage is certainly the most important quality code metric. But coverage is not enough -// the team needs to ensure that results are checked at test-time. These checks can be done both -// in test code, and in application code through assertions. The important part is that a test -// must fail explicitely when a check gets unvalidated during the test execution. -// -// This quality gate define a warn threshold (70%) and a fail threshold (80%). These are -// indicative thresholds and in practice the more the better. To achieve high coverage and -// low risk, make sure that new and refactored classes gets 100% covered by tests and that -// the application and test code contains as many checks/assertions as possible. -//]]> - -failif value < 70% -warnif value < 80% -let newMethods = Application.Methods.Where(m => m.WasAdded() && m.NbLinesOfCode > 0) -let locCovered = newMethods.Sum(m => m.NbLinesOfCodeCovered) -let loc = newMethods.Sum(m => m.NbLinesOfCode) -select 100d * locCovered / loc - -// -// *New Code* is defined as methods added since the baseline. -// -// To achieve high code coverage it is essential that new code gets properly -// tested and covered by tests. It is advised that all non-UI new classes gets -// 100% covered. -// -// Typically 90% of a class is easy to cover by tests and 10% is hard to reach -// through tests. It means that this 10% remaining is not easily testable, which -// means it is not well designed, which often means that this code is especially -// **error-prone**. This is the reason why it is important to reach 100% coverage -// for a class, to make sure that potentially *error-prone* code gets tested. -// -]]> - -failif value < 70% -warnif value < 80% -let newMethods = Application.Methods.Where(m => m.CodeWasChanged() && m.NbLinesOfCode > 0) -let locCovered = newMethods.Sum(m => m.NbLinesOfCodeCovered) -let loc = newMethods.Sum(m => m.NbLinesOfCode) -select 100d * locCovered / loc - -// -// *Refactored Code* is defined as methods where *code was changed* since the baseline. -// -// Comment changes and formatting changes are not considerd as refactoring. -// -// To achieve high code coverage it is essential that refactored code gets properly -// tested and covered by tests. It is advised that when refactoring a class -// or a method, it is important to also write tests to make sure it gets 100% covered. -// -// Typically 90% of a class is easy to cover by tests and 10% is hard to reach -// through tests. It means that this 10% remaining is not easily testable, which -// means it is not well designed, which often means that this code is especially -// **error-prone**. This is the reason why it is important to reach 100% coverage -// for a class, to make sure that potentially *error-prone* code gets tested. -// -]]> - -failif count > 0 issues -from i in Issues -where i.Severity == Severity.Blocker -select new { i, i.Severity, i.Debt, i.AnnualInterest } - -// -// An issue with the severity **Blocker** cannot move to production, it must be fixed. -// -// The severity of an issue is either defined explicitely in the rule source code, -// either inferred from the issue *annual interest* and thresholds defined in the -// NDepend Project Properties > Issue and Debt. -// - -]]> - -failif count > 10 issues -warnif count > 0 issues - -from i in Issues -where i.Severity == Severity.Critical -select new { i, i.Severity, i.Debt, i.AnnualInterest } - -// -// An issue with a severity level **Critical** shouldn't move to production. -// It still can for business imperative needs purposes, but at worst it must -// be fixed during the next iterations. -// -// The severity of an issue is either defined explicitely in the rule source code, -// either inferred from the issue *annual interest* and thresholds defined in the -// NDepend Project Properties > Issue and Debt. -//]]> - -failif count > 0 issues -from i in Issues -where i.Severity.EqualsAny(Severity.Blocker, Severity.Critical, Severity.High) && - // Count both the new issues and the issues that became at least Critical - (i.WasAdded() || i.OlderVersion().Severity < Severity.High) -select new { i, i.Severity, i.Debt, i.AnnualInterest } - - -// -// An issue with the severity **Blocker** cannot move to production, it must be fixed. -// -// An issue with a severity level **Critical** shouldn't move to production. -// It still can for business imperative needs purposes, but at worth it must be fixed -// during the next iterations. -// -// An issue with a severity level **High** should be fixed quickly, but can wait until -// the next scheduled interval. -// -// The severity of an issue is either defined explicitely in the rule source code, -// either inferred from the issue *annual interest* and thresholds defined in the -// NDepend Project Properties > Issue and Debt. -// -]]> - -failif count > 0 rules -from r in Rules where r.IsCritical && r.IsViolated() -select new { r, issues = r.Issues() } - -// -// The concept of critical rule is useful to pinpoint certain rules that -// should not be violated. -// -// A rule can be made critical just by checking the *Critical button* in the -// rule edition control and then saving the rule. -// -// This quality gate fails if any critical rule gets any violations. -// -// When no baseline is available, rules that rely on diff are not counted. -// If you observe that this quality gate count slightly decreases with no apparent reason, -// the reason is certainly that rules that rely on diff are not counted -// because the baseline is not defined. -//]]> - -failif value > 30% -warnif value > 20% -let timeToDev = codeBase.EffortToDevelop() -let debt = Issues.Sum(i => i.Debt) -select 100d * debt.ToManDay() / timeToDev.ToManDay() - -// -// % Debt total is defined as a percentage on: -// -// • the estimated total effort to develop the code base -// -// • and the the estimated total time to fix all issues (the Debt) -// -// Estimated total effort to develop the code base is inferred from -// # lines of code of the code base and from the -// *Estimated number of man-day to develop 1000 logicial lines of code* -// setting found in NDepend Project Properties > Issue and Debt. -// -// Debt documentation: https://www.ndepend.com/docs/technical-debt#Debt -// -// This quality gates fails if the estimated debt is more than 30% -// of the estimated effort to develop the code base, and warns if the -// estimated debt is more than 20% of the estimated effort to develop -// the code base -// ]]> - -failif value > 50 man-days -warnif value > 30 man-days -Issues.Sum(i => i.Debt).ToManDay() - -// -// This Quality Gate is disabled per default because the fail and warn -// thresholds of unacceptable Debt in man-days can only depend on the -// project size, number of developers and overall context. -// -// However you can refer to the default Quality Gate **Percentage Debt**. -// -// The Debt is defined as the sum of estimated effort to fix all issues. -// Debt documentation: https://www.ndepend.com/docs/technical-debt#Debt -//]]> - -failif value > 2 man-days -warnif value > 0 man-days -let debt = Issues.Sum(i => i.Debt) -let debtInBaseline = IssuesInBaseline.Sum(i => i.Debt) -select (debt - debtInBaseline).ToManDay() - - -// -// This Quality Gate fails if the estimated effort to fix new or worsened -// issues (what is called the *New Debt since Baseline*) is higher -// than 2 man-days. -// -// This Quality Gate warns if this estimated effort is positive. -// -// Debt documentation: https://www.ndepend.com/docs/technical-debt#Debt -//]]> - -failif count > 0 namespaces - -from n in Application.Namespaces -where n.DebtRating() != null && - n.DebtRating().Value.EqualsAny(DebtRating.E, DebtRating.D) -select new { - n, - debtRating = n.DebtRating(), - debtRatio = n.DebtRatio(), // % of debt from which DebtRating is inferred - devTimeInManDay = n.EffortToDevelop().ToDebt(), - debtInManDay = n.AllDebt(), - issues = n.AllIssues() -} - -// -// Forbid namespaces with a poor Debt Rating equals to **E** or **D**. -// -// The **Debt Rating** for a code element is estimated by the value of the **Debt Ratio** -// and from the various rating thresholds defined in this project *Debt Settings*. -// -// The **Debt Ratio** of a code element is a percentage of **Debt Amount** (in floating man-days) -// compared to the **estimated effort to develop the code element** (also in floating man-days). -// -// The **estimated effort to develop the code element** is inferred from the code elements -// number of lines of code, and from the project *Debt Settings* parameters -// *estimated number of man-days to develop 1000* **logical lines of code**. -// -// The **logical lines of code** corresponds to the number of debug breakpoints in a method -// and doesn't depend on code formatting nor comments. -// -// The Quality Gate can be modified to match assemblies, types or methods -// with a poor Debt Rating, instead of matching namespaces. -// ]]> - -failif value > 50 man-days -warnif value > 30 man-days -Issues.Sum(i => i.AnnualInterest).ToManDay() - - -// -// This Quality Gate is disabled per default because the fail and warn -// thresholds of unacceptable Annual-Interest in man-days can only depend -// on the project size, number of developers and overall context. -// -// However you can refer to the default Quality Gate -// **New Annual Interest since Baseline**. -// -// The Annual-Interest is defined as the sum of estimated annual cost -// in man-days, to leave all issues unfixed. -// -// Each rule can either provide a formula to compute the Annual-Interest -// per issue, or assign a **Severity** level for each issue. Some thresholds -// defined in *Project Properties > Issue and Debt > Annual Interest* are -// used to infer an Annual-Interest value from a Severity level. -// Annual Interest documentation: https://www.ndepend.com/docs/technical-debt#AnnualInterest -//]]> - -failif value > 2 man-days -warnif value > 0 man-days -let ai = Issues.Sum(i => i.AnnualInterest) -let aiInBaseline = IssuesInBaseline.Sum(i => i.AnnualInterest) -select (ai - aiInBaseline).ToManDay() - -// -// This Quality Gate fails if the estimated annual cost to leave all issues -// unfixed, increased from more than 2 man-days since the baseline. -// -// This Quality Gate warns if this estimated annual cost is positive. -// -// This estimated annual cost is named the **Annual-Interest**. -// -// Each rule can either provide a formula to compute the Annual-Interest -// per issue, or assign a **Severity** level for each issue. Some thresholds -// defined in *Project Properties > Issue and Debt > Annual Interest* are -// used to infer an Annual-Interest value from a Severity level. -// Annual Interest documentation: https://www.ndepend.com/docs/technical-debt#AnnualInterest -//]]> - - - Types Hot Spots -from t in JustMyCode.Types -where t.AllDebt() > Debt.Zero && - t.AllAnnualInterest() > AnnualInterest.Zero -orderby t.AllDebt().Value.TotalMinutes descending -select new { t, - Debt = t.AllDebt(), - Issues = t.AllIssues(), // AllIssues = {types issues} union {members issues} - AnnualInterest = t.AllAnnualInterest(), - BreakingPoint = t.AllBreakingPoint(), - t.NbLinesOfCode, - // t.PercentageCoverage, to uncomment if coverage data is imported - DebtRating = t.DebtRating(), - DebtRatio = t.DebtRatio() -} - -// -// This query lists **types with most Debt**, -// or in other words, types with issues that would need -// the largest effort to get fixed. -// -// Both issues on the type and its members are -// taken account. -// -// Since untested code often generates a lot of -// Debt, the type size and percentage coverage is shown -// (just uncomment *t.PercentageCoverage* in the query -// source code once you've imported the coverage data). -// -// The *Debt Rating* and *Debt Ratio* are also shown -// for informational purpose. -// -// -- -// -// The amount of *Debt* is not a measure to prioritize -// the effort to fix issues, it is an estimation of how far -// the team is from clean code that abides by the rules set. -// -// For each issue the *Annual Interest* estimates the annual -// cost to leave the issues unfixed. The *Severity* of an issue -// is estimated through thresholds from the *Annual Interest*. -// -// The **Debt Breaking Point** represents the duration -// from now when the estimated cost to leave the issue unfixed -// costs as much as the estimated effort to fix it. -// -// Hence the shorter the **Debt Breaking Point** -// the largest the **Return on Investment** for fixing -// the issue. The **Breaking Point is the right metric -// to prioritize issues fix**. -//]]> - Types to Fix Priority -from t in JustMyCode.Types -where t.AllBreakingPoint() > TimeSpan.Zero && - t.AllDebt().Value > 30.ToMinutes() -orderby t.AllBreakingPoint().TotalMinutes ascending -select new { t, - BreakingPoint = t.AllBreakingPoint(), - Debt = t.AllDebt(), - AnnualInterest = t.AllAnnualInterest(), - Issues = t.AllIssues(), - t.NbLinesOfCode, - // t.PercentageCoverage, to uncomment if coverage data is imported - DebtRating = t.DebtRating(), - DebtRatio = t.DebtRatio() -} - -// -// This query lists types per increasing -// **Debt Breaking Point**. -// -// For each issue the *Debt* estimates the -// effort to fix the issue, and the *Annual Interest* -// estimates the annual cost to leave the issue unfixed. -// The *Severity* of an issue is estimated through -// thresholds from the *Annual Interest* of the issue. -// -// The **Debt Breaking Point** represents the duration -// from now when the estimated cost to leave the issue unfixed -// costs as much as the estimated effort to fix it. -// -// Hence the shorter the **Debt Breaking Point** -// the largest the **Return on Investment** for fixing -// the issues. -// -// Often new and refactored types since baseline will be -// listed first, because issues on these types get a -// higher *Annual Interest* because it is important to -// focus first on new issues. -// -// -// -- -// -// Both issues on the type and its members are -// taken account. -// -// Only types with at least 30 minutes of Debt are listed -// to avoid parasiting the list with the numerous -// types with small *Debt*, on which the *Breaking Point* -// value makes less sense. -// -// The *Annual Interest* estimates the cost per year -// in man-days to leave these issues unfixed. -// -// Since untested code often generates a lot of -// Debt, the type size and percentage coverage is shown -// (just uncomment *t.PercentageCoverage* in the query -// source code once you've imported the coverage data). -// -// The *Debt Rating* and *Debt Ratio* are also shown -// for informational purpose. -//]]> - Issues to Fix Priority -from i in Issues -// Don't show first issues with BreakingPoint equals to zero. -orderby i.BreakingPoint != TimeSpan.Zero ? i.BreakingPoint : TimeSpan.MaxValue -select new { i, - Debt = i.Debt, - AnnualInterest = i.AnnualInterest, - BreakingPoint = i.BreakingPoint, - CodeElement = i.CodeElement -} - -// -// This query lists issues per increasing -// **Debt Breaking Point**. -// -// Double-click an issue to edit its rule and -// select the issue in the rule result. This way -// you can view all information concerning the issue. -// -// For each issue the *Debt* estimates the -// effort to fix the issue, and the *Annual Interest* -// estimates the annual cost to leave the issue unfixed. -// The *Severity* of an issue is estimated through -// thresholds from the *Annual Interest* of the issue. -// -// The **Debt Breaking Point** represents the duration -// from now when the estimated cost to leave the issue unfixed -// costs as much as the estimated effort to fix it. -// -// Hence the shorter the **Debt Breaking Point** -// the largest the **Return on Investment** for fixing -// the issue. -// -// Often issues on new and refactored code elements since -// baseline will be listed first, because such issues get a -// higher *Annual Interest* because it is important to -// focus first on new issues on recent code. -// -// More documentation: https://www.ndepend.com/docs/technical-debt -//]]> - Debt and Issues per Rule -from r in Rules -where r.IsViolated() -orderby r.Debt().Value descending -select new { - r, - Issues = r.Issues(), - Debt = r.Debt(), - AnnualInterest = r.AnnualInterest(), - BreakingPoint = r.BreakingPoint(), - Category = r.Category -} - -// -// This query lists violated rules with most *Debt* first. -// -// A rule violated has issues. For each issue the *Debt* -// estimates the effort to fix the issue. -// -// -- -// -// The amount of *Debt* is not a measure to prioritize -// the effort to fix issues, it is an estimation of how far -// the team is from clean code that abides by the rules set. -// -// For each issue the *Annual Interest* estimates the annual -// cost to leave the issues unfixed. The *Severity* of an issue -// is estimated through thresholds from the *Annual Interest*. -// -// The **Debt Breaking Point** represents the duration -// from now when the estimated cost to leave the issue unfixed -// costs as much as the estimated effort to fix it. -// -// Hence the shorter the **Debt Breaking Point** -// the largest the **Return on Investment** for fixing -// the issue. The **Breaking Point is the right metric -// to prioritize issues fix**. -// -// -- -// -// Notice that rules can be grouped in *Rule Category*. This -// way you'll see categories that generate most *Debt*. -// -// Typically the rules that generate most *Debt* are the -// ones related to *Code Coverage by Tests*, *Architecture* -// and *Code Smells*. -// -// More documentation: https://www.ndepend.com/docs/technical-debt -//]]> - New Debt and Issues per Rule -from r in Rules -where r.IsViolated() && r.IssuesAdded().Count() > 0 -orderby r.DebtDiff().Value descending -select new { - r, - IssuesAdded = r.IssuesAdded(), - IssuesFixed = r.IssuesFixed(), - Issues = r.Issues(), - Debt = r.Debt(), - DebtDiff = r.DebtDiff(), - Category = r.Category -} - -// -// This query lists violated rules that have new issues -// since baseline, with most **new Debt** first. -// -// A rule violated has issues. For each issue the *Debt* -// estimates the effort to fix the issue. -// -// -- -// -// New issues since the baseline are consequence of recent code -// refactoring sessions. They represent good opportunities -// of fix because the code recently refactored is fresh in -// the developers mind, which means fixing now costs less -// than fixing later. -// -// Fixing issues on recently touched code is also a good way -// to foster practices that will lead to higher code quality -// and maintainability, including writing unit-tests -// and avoiding unnecessary complex code. -// -// -- -// -// Notice that rules can be grouped in *Rule Category*. This -// way you'll see categories that generate most *Debt*. -// -// Typically the rules that generate most *Debt* are the -// ones related to *Code Coverage by Tests*, *Architecture* -// and *Code Smells*. -// -// More documentation: https://www.ndepend.com/docs/technical-debt -//]]> - Debt and Issues per Code Element -from elem in CodeElements -where elem.HasIssue() -orderby elem.Debt().Value descending -select new { - elem, - Issues = elem.Issues(), - Debt = elem.Debt(), - AnnualInterest = elem.AnnualInterest(), - BreakingPoint = elem.BreakingPoint() -} - -// -// This query lists code elements that have issues, -// with most *Debt* first. -// -// For each code element the *Debt* estimates -// the effort to fix the element issues. -// -// The amount of *Debt* is not a measure to prioritize -// the effort to fix issues, it is an estimation of how far -// the team is from clean code that abides by the rules set. -// -// For each element the *Annual Interest* estimates the annual -// cost to leave the elements issues unfixed. The *Severity* of an -// issue is estimated through thresholds from the *Annual Interest* -// of the issue. -// -// The **Debt Breaking Point** represents the duration -// from now when the estimated cost to leave the issues unfixed -// costs as much as the estimated effort to fix it. -// -// Hence the shorter the **Debt Breaking Point** -// the largest the **Return on Investment** for fixing -// the issue. The **Breaking Point is the right metric -// to prioritize issues fix**. -//]]> - New Debt and Issues per Code Element -from elem in CodeElements -where elem.HasIssue() && elem.IssuesAdded().Count() > 0 -orderby elem.DebtDiff().Value descending -select new { - elem, - IssuesAdded = elem.IssuesAdded(), - IssuesFixed = elem.IssuesFixed(), - Issues = elem.Issues(), - Debt = elem.Debt(), - DebtDiff = elem.DebtDiff() -} - // -// This query lists code elements that have new issues -// since baseline, with most **new Debt** first. -// -// For each code element the *Debt* estimates -// the effort to fix the element issues. -// -// New issues since the baseline are consequence of recent code -// refactoring sessions. They represent good opportunities -// of fix because the code recently refactored is fresh in -// the developers mind, which means fixing now costs less -// than fixing later. -// -// Fixing issues on recently touched code is also a good way -// to foster practices that will lead to higher code quality -// and maintainability, including writing unit-tests -// and avoiding unnecessary complex code. -// -]]> - - - Avoid types too big -// ND1000:AvoidTypesTooBig -warnif count > 0 from t in JustMyCode.Types where - - // First filter on type to optimize - t.NbLinesOfCode > 200 - // # IL Instructions is commented, because with LINQ syntax, a few lines of code can compile to hundreds of IL instructions. - // || t.NbILInstructions > 3000 - - // What matters is the # lines of code in JustMyCode - let locJustMyCode = t.MethodsAndConstructors.Where(m => JustMyCode.Contains(m)).Sum(m => m.NbLinesOfCode) - where locJustMyCode > 200 - - let isStaticWithNoMutableState = (t.IsStatic && t.Fields.Any(f => !f.IsImmutable)) - let staticFactor = (isStaticWithNoMutableState ? 0.2 : 1) - - orderby locJustMyCode descending -select new { - t, - locJustMyCode, - t.NbILInstructions, - t.Methods, - t.Fields, - - Debt = (staticFactor*locJustMyCode.Linear(200, 1, 2000, 10)).ToHours().ToDebt(), - - // The annual interest varies linearly from interest for severity major for 300 loc - // to interest for severity critical for 2000 loc - AnnualInterest = staticFactor*(locJustMyCode.Linear( - 200, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 2000, Severity.Critical.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest() -} - -// -// This rule matches types with more than 200 lines of code. -// **Only lines of code in JustMyCode methods are taken account.** -// -// Types where *NbLinesOfCode > 200* are extremely complex -// to develop and maintain. -// See the definition of the NbLinesOfCode metric here -// https://www.ndepend.com/docs/code-metrics#NbLinesOfCode -// -// Maybe you are facing the **God Class** phenomenon: -// A **God Class** is a class that controls way too many other classes -// in the system and has grown beyond all logic to become -// *The Class That Does Everything*. -// - -// -// Types with many lines of code -// should be split in a group of smaller types. -// -// To refactor a *God Class* you'll need patience, -// and you might even need to recreate everything from scratch. -// Here are a few refactoring advices: -// -// • The logic in the *God Class* must be splitted in smaller classes. -// These smaller classes can eventually become private classes nested -// in the original *God Class*, whose instances objects become -// composed of instances of smaller nested classes. -// -// • Smaller classes partitioning should be driven by the multiple -// responsibilities handled by the *God Class*. To identify these -// responsibilities it often helps to look for subsets of methods -// strongly coupled with subsets of fields. -// -// • If the *God Class* contains way more logic than states, a good -// option can be to define one or several static classes that -// contains no static field but only pure static methods. A pure static -// method is a function that computes a result only from inputs -// parameters, it doesn't read nor assign any static or instance field. -// The main advantage of pure static methods is that they are easily -// testable. -// -// • Try to maintain the interface of the *God Class* at first -// and delegate calls to the new extracted classes. -// In the end the *God Class* should be a pure facade without its own logic. -// Then you can keep it for convenience or throw it away and -// start to use the new classes only. -// -// • Unit Tests can help: write tests for each method before extracting it -// to ensure you don't break functionality. -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 1 hour for a 200 lines of code type, -// up to 10 hours for a type with 2.000 or more lines of code. -// -// In Debt and Interest computation, this rule takes account of the fact -// that static types with no mutable fields are just a collection of -// static methods that can be easily splitted and moved from one type -// to another. -//]]> - Avoid types with too many methods -// ND1001:AvoidTypesWithTooManyMethods -warnif count > 0 from t in JustMyCode.Types - - // Optimization: Fast discard of non-relevant types - where t.Methods.Count() > 20 - - // Don't match these methods - let methods = t.Methods.Where( - m => !(m.IsGeneratedByCompiler || - // m.IsConstructor || m.IsClassConstructor || // ctor/cctor not enumerated through IType.Methods - m.IsPropertyGetter || m.IsPropertySetter || - m.IsEventAdder || m.IsEventRemover)) - - where methods.Count() > 20 - orderby methods.Count() descending - - let isStaticWithNoMutableState = (t.IsStatic && t.Fields.Any(f => !f.IsImmutable)) - let staticFactor = (isStaticWithNoMutableState ? 0.2 : 1) - -select new { - t, - nbMethods = methods.Count(), - instanceMethods = methods.Where(m => !m.IsStatic), - staticMethods = methods.Where(m => m.IsStatic), - - t.NbLinesOfCode, - - Debt = (staticFactor*methods.Count().Linear(20, 1, 200, 10)).ToHours().ToDebt(), - - // The annual interest varies linearly from interest for severity major for 30 methods - // to interest for severity critical for 200 methods - AnnualInterest = (staticFactor*methods.Count().Linear( - 20, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 200, Severity.Critical.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest() -} - -// -// This rule matches types with more than 20 methods. -// Such type might be hard to understand and maintain. -// -// Notice that methods like constructors or property -// and event accessors are not taken account. -// -// Having many methods for a type might be a symptom -// of too many responsibilities implemented. -// -// Maybe you are facing the **God Class** phenomenon: -// A **God Class** is a class that controls way too many other classes -// in the system and has grown beyond all logic to become -// *The Class That Does Everything*. -// - -// -// To refactor properly a *God Class* please read *HowToFix advices* -// from the default rule **Types to Big**. -//// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 1 hour for a type with 20 methods, -// up to 10 hours for a type with 200 or more methods. -// -// In Debt and Interest computation, this rule takes account of the fact -// that static types with no mutable fields are just a collection of -// static methods that can be easily splitted and moved from one type -// to another. -//]]> - Avoid types with too many fields -// ND1002:AvoidTypesWithTooManyFields -warnif count > 0 from t in JustMyCode.Types - - // Optimization: Fast discard of non-relevant types - where !t.IsEnumeration && - t.Fields.Count() > 15 - - // Count instance fields and non-constant static fields - let fields = t.Fields.Where(f => - !f.IsGeneratedByCompiler && - !f.IsLiteral && - !(f.IsStatic && f.IsInitOnly) && - JustMyCode.Contains(f) ) - - where fields.Count() > 15 - - let methodsAssigningFields = fields.SelectMany(f => f.MethodsAssigningMe) - - orderby fields.Count() descending -select new { - t, - instanceFields = fields.Where(f => !f.IsStatic), - staticFields = fields.Where(f => f.IsStatic), -methodsAssigningFields , - - // See definition of Size of Instances metric here: - // https://www.ndepend.com/docs/code-metrics#SizeOfInst - t.SizeOfInst, - - Debt = fields.Count().Linear(15, 1, 200, 10).ToHours().ToDebt(), - - // The annual interest varies linearly from interest for severity major for 30 methods - // to interest for severity critical for 200 methods - AnnualInterest = fields.Count().Linear(15, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 200, Severity.Critical.AnnualInterestThreshold().Value.TotalMinutes).ToMinutes().ToAnnualInterest() -} - -// -// This rule matches types with more than 15 fields. -// Such type might be hard to understand and maintain. -// -// Notice that constant fields and static-readonly fields are not counted. -// Enumerations types are not counted also. -// -// Having many fields for a type might be a symptom -// of too many responsibilities implemented. -// - -// -// To refactor such type and increase code quality and maintainability, -// certainly you'll have to group subsets of fields into smaller types -// and dispatch the logic implemented into the methods -// into these smaller types. -// -// More refactoring advices can be found in the default rule -// **Types to Big**, *HowToFix* section. -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 1 hour for a type with 15 fields, -// to up to 10 hours for a type with 200 or more fields. -//]]> - Avoid methods too big, too complex -// ND1003:AvoidMethodsTooBigTooComplex -warnif count > 0 from m in JustMyCode.Methods where - - // Don't match async methods here to avoid - // false positives because of special compiler tricks. - !m.IsAsync && - - m.ILNestingDepth > 2 && - (m.NbLinesOfCode > 35 || - m.CyclomaticComplexity > 20 || - m.ILCyclomaticComplexity > 60) - - let complexityScore = m.NbLinesOfCode/2 + m.CyclomaticComplexity + m.ILCyclomaticComplexity/3 + 3*m.ILNestingDepth - - orderby complexityScore descending, - m.CyclomaticComplexity descending, - m.ILCyclomaticComplexity descending, - m.ILNestingDepth descending -select new { - m, - m.NbLinesOfCode, - m.CyclomaticComplexity, - m.ILCyclomaticComplexity, - m.ILNestingDepth, - complexityScore, - - Debt = complexityScore.Linear(30, 40, 400, 8*60).ToMinutes().ToDebt(), - - // The annual interest varies linearly from interest for severity minor - // to interest for severity major - AnnualInterest = complexityScore .Linear(30, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 200, 2*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest() - -} - -// -// This rule matches methods where *ILNestingDepth* > 2 -// and (*NbLinesOfCode* > 35 -// or *CyclomaticComplexity* > 20 -// or *ILCyclomaticComplexity* > 60) -// Such method is typically hard to understand and maintain. -// -// Maybe you are facing the **God Method** phenomenon. -// A "God Method" is a method that does way too many processes in the system -// and has grown beyond all logic to become *The Method That Does Everything*. -// When need for new processes increases suddenly some programmers realize: -// why should I create a new method for each processe if I can only add an *if*. -// -// See the definition of the *CyclomaticComplexity* metric here: -// https://www.ndepend.com/docs/code-metrics#CC -// -// See the definition of the *ILCyclomaticComplexity* metric here: -// https://www.ndepend.com/docs/code-metrics#ILCC -// -// See the definition of the *ILNestingDepth* metric here: -// https://www.ndepend.com/docs/code-metrics#ILNestingDepth -// - -// -// A large and complex method should be split in smaller methods, -// or even one or several classes can be created for that. -// -// During this process it is important to question the scope of each -// variable local to the method. This can be an indication if -// such local variable will become an instance field of the newly created class(es). -// -// Large *switch…case* structures might be refactored through the help -// of a set of types that implement a common interface, the interface polymorphism -// playing the role of the *switch cases tests*. -// -// Unit Tests can help: write tests for each method before extracting it -// to ensure you don't break functionality. -// -// The estimated Debt, which means the effort to fix such issue, -// varies from 40 minutes to 8 hours, linearly from a weighted complexity score. -//]]> - Avoid methods with too many parameters -// ND1004:AvoidMethodsWithTooManyParameters -warnif count > 0 from m in JustMyCode.Methods where - m.NbParameters >= 7 && - - // Don't match a method that overrides a third-party method with many parameters - !m.OverriddensBase.Any(mo => mo.IsThirdParty) && - - // Don't match a constructor that calls a base constructor with many parameters - !(m.IsConstructor && m.MethodsCalled.Any( - mc => mc.IsConstructor && - mc.NbParameters >= 7 && - m.ParentType.DeriveFrom(mc.ParentType))) && - - // Don't match DllImport P/invoke methods with many parameters - !m.HasAttribute("System.Runtime.InteropServices.DllImportAttribute".AllowNoMatch()) - - orderby m.NbParameters descending -select new { - m, - m.NbParameters, - Debt = m.NbParameters.Linear(7, 1, 40, 6).ToHours().ToDebt(), - - // The annual interest varies linearly from interest for severity Medium for 7 parameters - // to interest for severity Critical for 40 parameters - AnnualInterest = m.NbParameters.Linear(7, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 40, Severity.Critical.AnnualInterestThreshold().Value.TotalMinutes).ToMinutes().ToAnnualInterest() -} - -// -// This rule matches methods with 7 or more parameters. -// Such method is painful to call and might degrade performance. -// See the definition of the *NbParameters* metric here: -// https://www.ndepend.com/docs/code-metrics#NbParameters -// -// This rule doesn't match a method that overrides a third-party method -// with 7 or more parameters because such situation is the consequence -// of a lower-level problem. -// -// For the same reason, this rule doesn't match a constructor that calls a -// base constructor with 7 or more parameters. -// - -// -// More properties/fields can be added to the declaring type to -// handle numerous states. An alternative is to provide -// a class or a structure dedicated to handle arguments passing. -// For example see the class *System.Diagnostics.ProcessStartInfo* -// and the method *System.Diagnostics.Process.Start(ProcessStartInfo)*. -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 1 hour for a method with 7 parameters, -// up to 6 hours for a methods with 40 or more parameters. -//]]> - Avoid methods with too many overloads -// ND1005:AvoidMethodsWithTooManyOverloads -warnif count > 0 -let max = 6 -let lookup = JustMyCode.Methods.Where(m => - m.NbOverloads >= max && - !m.IsOperator && // Don't report operator overload - - // Don't match overloads due tu the visitor pattern, based on a naming convention. - !m.SimpleName.ToLower().StartsWithAny("visit", "dispatch") -).ToLookup(m => m.ParentType.FullName + "."+ m.SimpleName) - -from @group in lookup -let overloads = @group.ToArray() - -// Prune not fixable situations. -let overloadsPruned = overloads.Where( - m => - // Don't match a method that overrides a third-party methods - !m.OverriddensBase.Any(mo => mo.IsThirdParty) && - - // Don't match a constructor that calls a base constructor. - !(m.IsConstructor && m.MethodsCalled.Any( - mc => mc.IsConstructor && - m.ParentType.DeriveFrom(mc.ParentType))) -).ToArray() - -where overloadsPruned.Length > max - -orderby overloads.Length descending - -select new { - m = @group.First(), - overloadsPruned, - Debt = (3*overloads.Length).ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// Method overloading is the ability to create multiple methods of the same name -// with different implementations, and various set of parameters. -// -// This rule matches sets of methods with 6 overloads or more. -// -// Such method set might be a problem to maintain -// and provokes coupling higher than necessary. -// -// See the definition of the *NbOverloads* metric here -// https://www.ndepend.com/docs/code-metrics#NbOverloads -// -// Notice that this rule doesn't include in the overloads list -// methods that override a third-party method -// nor constructors that call a base constructor. -// Such situations are consequences of lower-level problems. -// - -// -// Typically the *too many overloads* phenomenon appears when an algorithm -// takes a various set of in-parameters. Each overload is presented as -// a facility to provide a various set of in-parameters. -// In such situation, the C# and VB.NET language feature named -// *Named and Optional arguments* should be used. -// -// The *too many overloads* phenomenon can also be a consequence of the usage -// of the **visitor design pattern** http://en.wikipedia.org/wiki/Visitor_pattern -// since a method named *Visit()* must be provided for each sub type. -// For this reason, the default version of this rule doesn't match overloads whose name -// start with "visit" or "dispatch" (case-unsensitive) to avoid match -// overload visitors, and you can adapt this rule to your own naming convention. -// -// Sometime *too many overloads* phenomenon is not the symptom of a problem, -// for example when a *numeric to something conversion* method applies to -// all numeric and nullable numeric types. -// -// The estimated Debt, which means the effort to fix such issue, -// is of 3 minutes per method overload. -//]]> - Avoid methods potentially poorly commented -// ND1006:AvoidMethodsPotentiallyPoorlyCommented -warnif count > 0 from t in JustMyCode.Types where - // Entity Framework ModelSnapshot and DbContext and Migration have large uncommented methods. - !t.DeriveFrom("Microsoft.EntityFrameworkCore.Infrastructure.ModelSnapshot".AllowNoMatch()) && - !t.DeriveFrom("Microsoft.EntityFrameworkCore.DbContext".AllowNoMatch()) && - !t.DeriveFrom("Microsoft.EntityFrameworkCore.Migrations.Migration".AllowNoMatch()) - - -from m in t.Methods where - m.PercentageComment < 10 && - m.NbLinesOfCode > 20 && - JustMyCode.Contains(t) - - let nbLinesOfCodeNotCommented = m.NbLinesOfCode - m.NbLinesOfComment - - orderby nbLinesOfCodeNotCommented descending - -select new { - m, - m.PercentageComment, - m.NbLinesOfCode, - m.NbLinesOfComment, - nbLinesOfCodeNotCommented, - - Debt = nbLinesOfCodeNotCommented .Linear(20, 2, 200, 20).ToMinutes().ToDebt(), - - // The annual interest varies linearly from interest for severity major for 300 loc - // to interest for severity critical for 2000 loc - AnnualInterest = m.PercentageComment.Linear( - 0, 8 *(Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes), - 20, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes).ToMinutes().ToAnnualInterest() -} - -// -// This rule matches methods with less than 10% of comment lines and that have -// at least 20 lines of code. Such method might need to be more commented. -// -// See the definitions of the *Comments metric* here: -// https://www.ndepend.com/docs/code-metrics#PercentageComment -// https://www.ndepend.com/docs/code-metrics#NbLinesOfComment -// -// Notice that only comments about the method implementation -// (comments in method body) are taken account. -// - -// -// Typically add more comment. But code commenting is subject to controversy. -// While poorly written and designed code would needs a lot of comment -// to be understood, clean code doesn't need that much comment, especially -// if variables and methods are properly named and convey enough information. -// Unit-Test code can also play the role of code commenting. -// -// However, even when writing clean and well-tested code, one will have -// to write **hacks** at a point, usually to circumvent some API limitations or bugs. -// A hack is a non-trivial piece of code, that doesn't make sense at first glance, -// and that took time and web research to be found. -// In such situation comments must absolutely be used to express the intention, -// the need for the hacks and the source where the solution has been found. -// -// The estimated Debt, which means the effort to comment such method, -// varies linearly from 2 minutes for 10 lines of code not commented, -// up to 20 minutes for 200 or more, lines of code not commented. -//]]> - Avoid types with poor cohesion -// ND1007:AvoidTypesWithPoorCohesion -warnif count > 0 from t in JustMyCode.Types where - t.LCOM > 0.8 && - t.NbFields > 10 && - t.NbMethods >10 - - let poorCohesionScore = 1/(1.01 - t.LCOM) - orderby poorCohesionScore descending - - select new { - t, - t.LCOM, - t.NbMethods, - t.NbFields, - poorCohesionScore, - - Debt = poorCohesionScore.Linear(5, 5, 50, 4*60).ToMinutes().ToDebt(), - - // The annual interest varies linearly from interest for severity Medium for low poorCohesionScore - // to 4 times interest for severity High for high poorCohesionScore - AnnualInterest = poorCohesionScore.Linear(5, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 50, 4*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest() - -} - -// -// This rule is based on the *LCOM code metric*, -// LCOM stands for **Lack Of Cohesion of Methods**. -// See the definition of the LCOM metric here -// https://www.ndepend.com/docs/code-metrics#LCOM -// -// The LCOM metric measures the fact that most methods are using most fields. -// A class is considered utterly cohesive (which is good) -// if all its methods use all its instance fields. -// -// Only types with enough methods and fields are taken account to avoid bias. -// The LCOM takes its values in the range [0-1]. -// -// This rule matches types with LCOM higher than 0.8. -// Such value generally pinpoints a **poorly cohesive class**. -// - -// -// To refactor a poorly cohesive type and increase code quality and maintainability, -// certainly you'll have to split the type into several smaller and more cohesive types -// that together, implement the same logic. -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 5 minutes for a type with a low poorCohesionScore, -// up to 4 hours for a type with high poorCohesionScore. -//]]> - Avoid methods with too many local variables -// ND1008:AvoidMethodsWithTooManyLocalVariables -warnif count > 0 from m in JustMyCode.Methods where - m.NbVariables > 15 - orderby m.NbVariables descending -select new { - m, - m.NbVariables, - - Debt = m.NbVariables.Linear(15, 1, 80, 6).ToHours().ToDebt(), - - // The annual interest varies linearly from interest for severity Medium for 15 variables - // to interest for severity Critical for 80 variables - AnnualInterest = m.NbVariables.Linear(15, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 80, Severity.Critical.AnnualInterestThreshold().Value.TotalMinutes).ToMinutes().ToAnnualInterest() - -} - -// -// This rule matches methods with more than 15 variables. -// -// Methods where *NbVariables > 8* are hard to understand and maintain. -// Methods where *NbVariables > 15* are extremely complex and must be refactored. -// -// The number of variables is infered from the compiled IL code of the method. -// The C# and VB.NET compiler might introduce some hidden variables -// for language constructs like lambdas, so the default threshold of -// this rule is set to 15 to avoid matching false positives. -// - -// -// To refactor such method and increase code quality and maintainability, -// certainly you'll have to split the method into several smaller methods -// or even create one or several classes to implement the logic. -// -// During this process it is important to question the scope of each -// variable local to the method. This can be an indication if -// such local variable will become an instance field of the newly created class(es). -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 10 minutes for a method with 15 variables, -// up to 2 hours for a methods with 80 or more variables. -//]]> - - - From now, all types added should respect basic quality principles -// ND1100:FromNowAllTypesAddedShouldRespectBasicQualityPrinciples -warnif count > 0 from t in JustMyCode.Types where - -// Only match types added since Baseline. -// Uncomment this line to match also refactored types since Baseline. -// (t.WasAdded() || t.CodeWasChanged()) && - t.WasAdded() && - -// Eliminate interfaces, enumerations or types only with constant fields -// by making sure we are matching type with code. -t.NbLinesOfCode > 10 && - -// Optimization: Fast discard of non-relevant types -(t.Fields.Count() > 20 || t.Methods.Count() > 20) - -// Count instance fields and non-constant static fields -let fields = t.Fields.Where(f => - !f.IsLiteral && - !(f.IsStatic && f.IsInitOnly)) - -// Don't match these methods -let methods = t.Methods.Where( - m => !(// m.IsConstructor || m.IsClassConstructor || // ctor/cctor not enumerated through IType.Methods - m.IsGeneratedByCompiler || - m.IsPropertyGetter || m.IsPropertySetter || - m.IsEventAdder || m.IsEventRemover)) - -where - -// Low Quality types Metrics' definitions are available here: -// https://www.ndepend.com/docs/code-metrics#MetricsOnTypes -( // Types with too many methods - fields.Count() > 20 || - - methods.Count() > 20 || - - // Complex Types that use more than 50 other types - t.NbTypesUsed > 50 -) -select new { - t, - t.NbLinesOfCode, - - instanceMethods = methods.Where(m => !m.IsStatic), - staticMethods = methods.Where(m => m.IsStatic), - - instanceFields = fields.Where(f => !f.IsStatic), - staticFields = fields.Where(f => f.IsStatic), - - t.TypesUsed, - - // Constant Debt estimation, since for such type rules in category "Code Smells" - // accurately estimate the Debt. - Debt = 10.ToMinutes().ToDebt(), - - // The Severity is higher for new types than for refactored types - AnnualInterest= (t.WasAdded() ? 3 : 1) * - Severity.High.AnnualInterestThreshold() -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// This rule operates only on types added since baseline. -// -// This rule can be easily modified to also match types refactored since baseline, -// that don't satisfy all quality criterions. -// -// Types matched by this rule not only have been recently added or refactored, -// but also somehow violate one or several basic quality principles, -// whether it has too many methods, -// it has too many fields, -// or is using too many types. -// Any of these criterions is often a symptom of a type with too many responsibilities. -// -// Notice that to count methods and fields, methods like constructors -// or property and event accessors are not taken account. -// Notice that constants fields and static-readonly fields are not counted. -// Enumerations types are not counted also. -// - -// -// To refactor such type and increase code quality and maintainability, -// certainly you'll have to split the type into several smaller types -// that together, implement the same logic. -// -// Issues of this rule have a constant 10 minutes Debt, because the Debt, -// which means the effort to fix such issue, is already estimated for issues -// of rules in the category **Code Smells**. -// -// However issues of this rule have a **High** severity, with even more -// interests for issues on new types since baseline, because the proper time -// to increase the quality of these types is **now**, before they get commited -// in the next production release. -//]]> - From now, all types added should be 100% covered by tests -// ND1101:FromNowAllTypesAddedShouldBe100PercentCoveredByTests -warnif count > 0 from t in JustMyCode.Types where - -// Only match types added since Baseline. -// Uncomment this line to match also refactored types since Baseline. -// (t.WasAdded() || t.CodeWasChanged()) && - t.WasAdded() && - - // …that are not 100% covered by tests - t.PercentageCoverage < 100 - - let methodsCulprit = t.Methods.Where(m => m.PercentageCoverage < 100) - -select new { - t, - t.PercentageCoverage, - methodsCulprit, - t.NbLinesOfCode, - - // Constant Debt estimation, since for such type rules in category "Coverage" - // accurately estimate the untested code Debt. - Debt = 10.ToMinutes().ToDebt(), - - // The Severity is higher for new types than for refactored types - AnnualInterest= (t.WasAdded() ? 3 : 1) * - Severity.High.AnnualInterestThreshold() -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// This rule operates only on types added since baseline. -// -// This rule can be easily modified to also match types refactored since baseline, -// that are not 100% covered by tests. -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// Often covering 10% of remaining uncovered code of a class, -// requires as much work as covering the first 90%. -// For this reason, typically teams estimate that 90% coverage is enough. -// However *untestable code* usually means *poorly written code* -// which usually leads to *error prone code*. -// So it might be worth refactoring and making sure to cover the 10% remaining code -// because **most tricky bugs might come from this small portion of hard-to-test code**. -// -// Not all classes should be 100% covered by tests (like UI code can be hard to test) -// but you should make sure that most of the logic of your application -// is defined in some *easy-to-test classes*, 100% covered by tests. -// -// In this context, this rule warns when a type added or refactored since the baseline, -// is not fully covered by tests. -// - -// -// Write more unit-tests dedicated to cover code not covered yet. -// If you find some *hard-to-test code*, it is certainly a sign that this code -// is not *well designed* and hence, needs refactoring. -// -// You'll find code impossible to cover by unit-tests, like calls to *MessageBox.Show()*. -// An infrastructure must be defined to be able to *mock* such code at test-time. -// -// Issues of this rule have a constant 10 minutes Debt, because the Debt, -// which means the effort to write tests for the culprit type, is already -// estimated for issues in the category **Code Coverage**. -// -// However issues of this rule have a **High** severity, with even more -// interests for issues on new types since baseline, because the proper time -// to write tests for these types is **now**, before they get commited -// in the next production release. -//]]> - From now, all methods added should respect basic quality principles -// ND1102:FromNowAllMethodsAddedShouldRespectBasicQualityPrinciples -warnif count > 0 from m in JustMyCode.Methods where - - // Only match methods added since Baseline. - // Uncomment this line to match also refactored methods since Baseline. - // (m.WasAdded() || m.CodeWasChanged()) && - m.WasAdded() && - - // Don't match async methods here to avoid - // false positives because of special compiler tricks. - !m.IsAsync && - -// Low Quality methods// Metrics' definitions -( m.NbLinesOfCode > 30 || // https://www.ndepend.com/docs/code-metrics#NbLinesOfCode - m.NbILInstructions > 200 || // https://www.ndepend.com/docs/code-metrics#NbILInstructions - m.CyclomaticComplexity > 20 || // https://www.ndepend.com/docs/code-metrics#CC - m.ILCyclomaticComplexity > 50 || // https://www.ndepend.com/docs/code-metrics#ILCC - m.ILNestingDepth > 4 || // https://www.ndepend.com/docs/code-metrics#ILNestingDepth - m.NbParameters > 5 || // https://www.ndepend.com/docs/code-metrics#NbParameters - m.NbVariables > 8 || // https://www.ndepend.com/docs/code-metrics#NbVariables - m.NbOverloads > 6 ) -select new { - m, - m.NbLinesOfCode, - m.NbILInstructions, - m.CyclomaticComplexity, - m.ILCyclomaticComplexity, - m.ILNestingDepth, - m.NbParameters, - m.NbVariables, - m.NbOverloads, // https://www.ndepend.com/docs/code-metrics#NbOverloads - - // Constant Debt estimation, since for such method rules in category "Code Smells" - // accurately estimate the Debt. - Debt = 5.ToMinutes().ToDebt(), - - // The Severity is higher for new methods than for refactored methods - AnnualInterest= (m.WasAdded() ? 3 : 1) * - Severity.High.AnnualInterestThreshold() -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// This rule operates only on methods added or refactored since the baseline. -// -// This rule can be easily modified to also match methods refactored since baseline, -// that don't satisfy all quality criterions. -// -// Methods matched by this rule not only have been recently added or refactored, -// but also somehow violate one or several basic quality principles, -// whether it is too large (too many *lines of code*), -// too complex (too many *if*, *switch case*, loops…) -// has too many variables, too many parameters -// or has too many overloads. -// - -// -// To refactor such method and increase code quality and maintainability, -// certainly you'll have to split the method into several smaller methods -// or even create one or several classes to implement the logic. -// -// During this process it is important to question the scope of each -// variable local to the method. This can be an indication if -// such local variable will become an instance field of the newly created class(es). -// -// Large *switch…case* structures might be refactored through the help -// of a set of types that implement a common interface, the interface polymorphism -// playing the role of the *switch cases tests*. -// -// Unit Tests can help: write tests for each method before extracting it -// to ensure you don't break functionality. -// -// Issues of this rule have a constant 5 minutes Debt, because the Debt, -// which means the effort to fix such issue, is already estimated for issues -// of rules in the category **Code Smells**. -// -// However issues of this rule have a **High** severity, with even more -// interests for issues on new methods since baseline, because the proper time -// to increase the quality of these methods is **now**, before they get commited -// in the next production release. -//]]> - Avoid decreasing code coverage by tests of types -// ND1103:AvoidDecreasingCodeCoverageByTestsOfTypes -warnif count > 0 -from t in JustMyCode.Types where - t.IsPresentInBothBuilds() && t.CoverageDataAvailable && t.OlderVersion().CoverageDataAvailable -let locDiff = (int)t.NbLinesOfCode.Value - (int)t.OlderVersion().NbLinesOfCode.Value -where locDiff >= 0 -let uncoveredLoc = (int)t.NbLinesOfCodeNotCovered.Value - ((int)t.OlderVersion().NbLinesOfCodeNotCovered.Value + locDiff) -where uncoveredLoc > 0 - -orderby uncoveredLoc descending - -select new { - t, - OldCoveragePercent = t.OlderVersion().PercentageCoverage, - NewCoveragePercent = t.PercentageCoverage, - OldLoc = t.OlderVersion().NbLinesOfCode, - NewLoc = t.NbLinesOfCode, - uncoveredLoc, - - Debt = uncoveredLoc.Linear(1, 15, 100, 3*60).ToMinutes().ToDebt(), - - // The annual interest varies linearly from interest for severity High for one line of code that is not covered by tests anymore - // to interest for severity Critical for 50 lines of code that are not covered by tests anymore - AnnualInterest = uncoveredLoc.Linear(1, Severity.High.AnnualInterestThreshold().Value.TotalMinutes, - 50, 2*Severity.Critical.AnnualInterestThreshold().Value.TotalMinutes).ToMinutes().ToAnnualInterest() - - -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// This rule warns when the number of lines of a type covered by tests -// decreased since the baseline. In case the type faced some refactoring -// since the baseline, this loss in coverage is estimated only for types -// with more lines of code, where # lines of code covered now is lower -// than # lines of code covered in baseline + the extra number of -// lines of code. -// -// Such situation can mean that some tests have been removed -// but more often, this means that the type has been modified, -// and that changes haven't been covered properly by tests. -// -// To visualize changes in code, right-click a matched type and select: -// -// • Compare older and newer versions of source file -// -// • or Compare older and newer versions disassembled with Reflector -// - -// -// Write more unit-tests dedicated to cover changes in matched types -// not covered yet. -// If you find some *hard-to-test code*, it is certainly a sign that this code -// is not *well designed* and hence, needs refactoring. -// -// The estimated Debt, which means the effort to cover by test -// code that used to be covered, varies linearly 15 minutes to 3 hours, -// depending on the number of lines of code that are not covered by tests anymore. -// -// Severity of issues of this rule varies from **High** to **Critical** -// depending on the number of lines of code that are not covered by tests anymore. -// Because the loss in code coverage happened since the baseline, -// the severity is high because it is important to focus on these issues -// **now**, before such code gets released in production. -//]]> - Avoid making complex methods even more complex -// ND1104:AvoidMakingComplexMethodsEvenMoreComplex -warnif count > 0 - -let complexityScoreProc = new Func(m => - (m.CyclomaticComplexity + m.ILCyclomaticComplexity/3 + 5*m.ILNestingDepth).Value) - -from m in JustMyCode.Methods where - - // Don't match async methods here to avoid - // false positives because of special compiler tricks. - !m.IsAsync && - - !m.IsAbstract && - m.IsPresentInBothBuilds() && - m.CodeWasChanged() && - m.OlderVersion().CyclomaticComplexity > 6 - -let complexityScore = complexityScoreProc(m) -let oldComplexityScore = complexityScoreProc(m.OlderVersion()) -where complexityScore > oldComplexityScore - -let complexityScoreDiff = complexityScoreProc(m) - complexityScoreProc(m.OlderVersion()) -orderby complexityScoreDiff descending - -select new { - m, - oldComplexityScore , - complexityScore , - diff= complexityScoreDiff, - - Debt = complexityScoreDiff.Linear(1, 15, 50, 60).ToMinutes().ToDebt(), - - // The annual interest varies linearly from interest for severity Medium for a tiny complexity increment - // to interest for severity critical for 2000 loc - AnnualInterest = complexityScoreDiff.Linear(1, Severity.High.AnnualInterestThreshold().Value.TotalMinutes, - 50, 4*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest() - -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// The method complexity is measured through the code metric -// *Cyclomatic Complexity* defined here: -// https://www.ndepend.com/docs/code-metrics#CC -// -// This rule warns when a method already complex -// (i.e with *Cyclomatic Complexity* higher than 6) -// become even more complex since the baseline. -// -// This rule needs assemblies PDB files and source code -// to be available at analysis time, because the *Cyclomatic Complexity* -// is inferred from the source code and source code location -// is inferred from PDB files. See: -// https://www.ndepend.com/docs/ndepend-analysis-inputs-explanation -// -// To visualize changes in code, right-click a matched method and select: -// -// • Compare older and newer versions of source file -// -// • or Compare older and newer versions disassembled with Reflector -// - -// -// A large and complex method should be split in smaller methods, -// or even one or several classes can be created for that. -// -// During this process it is important to question the scope of each -// variable local to the method. This can be an indication if -// such local variable will become an instance field of the newly created class(es). -// -// Large *switch…case* structures might be refactored through the help -// of a set of types that implement a common interface, the interface polymorphism -// playing the role of the *switch cases tests*. -// -// Unit Tests can help: write tests for each method before extracting it -// to ensure you don't break functionality. -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 15 to 60 minutes depending on the extra complexity added. -// -// Issues of this rule have a **High** severity, because it is important to focus -// on these issues **now**, before such code gets released in production. -//]]> - Avoid making large methods even larger -// ND1105:AvoidMakingLargeMethodsEvenLarger -warnif count > 0 -from m in JustMyCode.Methods where - !m.IsAbstract && - - // Eliminate constructors from match, since they get larger - // as soons as some fields initialization are added. - !m.IsConstructor && - !m.IsClassConstructor && - - // Filter just here for optimization - m.NbLinesOfCode > 15 && - - m.IsPresentInBothBuilds() && - m.CodeWasChanged() - -let oldLoc = m.OlderVersion().NbLinesOfCode -where oldLoc > 15 && m.NbLinesOfCode > oldLoc - -let diff = m.NbLinesOfCode - oldLoc -where diff > 0 -orderby diff descending - -select new { - m, - oldLoc, - newLoc = m.NbLinesOfCode, - diff, - - Debt = diff.Linear(1, 10, 100, 60).ToMinutes().ToDebt(), - - // The annual interest varies linearly from interest for severity Medium for a tiny complexity increment - // to interest for severity critical for 2000 loc - AnnualInterest = diff .Linear(1, Severity.High.AnnualInterestThreshold().Value.TotalMinutes, - 100, 4*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest() - -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This rule warns when a method already large -// (i.e with more than 15 lines of code) -// become even larger since the baseline. -// -// The method size is measured through the code metric -// *# Lines of Code* defined here: -// https://www.ndepend.com/docs/code-metrics#NbLinesOfCode -// -// This rule needs assemblies PDB files -// to be available at analysis time, because the *# Lines of Code* -// is inferred from PDB files. See: -// https://www.ndepend.com/docs/ndepend-analysis-inputs-explanation -// -// To visualize changes in code, right-click a matched method and select: -// -// • Compare older and newer versions of source file -// -// • or Compare older and newer versions disassembled with Reflector -// - -// -// Usually too big methods should be split in smaller methods. -// -// But long methods with no branch conditions, that typically initialize some data, -// are not necessarily a problem to maintain, and might not need refactoring. -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 5 to 20 minutes depending -// on the number of lines of code added. -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 10 to 60 minutes depending on the extra complexity added. -// -// Issues of this rule have a **High** severity, because it is important to focus -// on these issues **now**, before such code gets released in production. -//]]> - Avoid adding methods to a type that already had many methods -// ND1106:AvoidAddingMethodsToATypeThatAlreadyHadManyMethods -warnif count > 0 - -// Don't count constructors and methods generated by the compiler! -let getMethodsProc = new Func>( - t => t.Methods.Where(m => - //!m.IsConstructor && !m.IsClassConstructor && // ctor/cctor not enumerated through IType.Methods - !m.IsGeneratedByCompiler).ToArray()) - - -from t in JustMyCode.Types where - - t.NbMethods > 30 && // Just here for optimization - - t.IsPresentInBothBuilds() - - // Optimization: fast discard of non-relevant types - where t.OlderVersion().NbMethods > 30 - - let oldMethods = getMethodsProc(t.OlderVersion()) - where oldMethods.Count > 30 - - let newMethods = getMethodsProc(t) - where newMethods.Count > oldMethods.Count - - let addedMethods = newMethods.Where(m => m.WasAdded()) - let removedMethods = oldMethods.Where(m => m.WasRemoved()) - - orderby addedMethods.Count() descending - -select new { - t, - nbOldMethods = oldMethods.Count, - nbNewMethods = newMethods.Count, - addedMethods, - removedMethods, - - Debt = (10*addedMethods.Count()).ToMinutes().ToDebt(), - AnnualInterest = addedMethods.Count().Linear( - 1, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 100, 4*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest() -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// Types where number of methods is greater than 15 -// might be hard to understand and maintain. -// -// This rule lists types that already had more than 15 methods -// at the baseline time, and for which new methods have been added. -// -// Having many methods for a type might be a symptom -// of too many responsibilities implemented. -// -// Notice that constructors and methods generated by the compiler -// are not taken account. -// - -// -// To refactor such type and increase code quality and maintainability, -// certainly you'll have to split the type into several smaller types -// that together, implement the same logic. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 10 minutes per method added. -// -// Issues of this rule have a **High** severity, because it is important to focus -// on these issues **now**, before such code gets released in production. -//]]> - Avoid adding instance fields to a type that already had many instance fields -// ND1107:AvoidAddingInstanceFieldsToATypeThatAlreadyHadManyInstanceFields -warnif count > 0 - -let getFieldsProc = new Func>( - t => t.Fields.Where(f => - !f.IsLiteral && - !f.IsGeneratedByCompiler && - !f.IsStatic).ToArray()) - - -from t in JustMyCode.Types where - - !t.IsEnumeration && - t.IsPresentInBothBuilds() - - // Optimization: fast discard of non-relevant types - where t.OlderVersion().NbFields > 15 - - let oldFields = getFieldsProc(t.OlderVersion()) - where oldFields.Count > 15 - - let newFields = getFieldsProc(t) - where newFields.Count > oldFields.Count - - let addedFields = newFields.Where(f => f.WasAdded()) - let removedFields = oldFields.Where(f => f.WasRemoved()) - - orderby addedFields.Count() descending - -select new { - t, - nbOldFields = oldFields.Count, - nbNewFields = newFields.Count, - addedFields, - removedFields, - - Debt = (10*addedFields.Count()).ToMinutes().ToDebt(), - AnnualInterest = addedFields.Count().Linear( - 1, Severity.High.AnnualInterestThreshold().Value.TotalMinutes, - 100, 4*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest() - -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// Types where number of fields is greater than 15 -// might be hard to understand and maintain. -// -// This rule lists types that already had more than 15 fields -// at the baseline time, and for which new fields have been added. -// -// Having many fields for a type might be a symptom -// of too many responsibilities implemented. -// -// Notice that *constants* fields and *static-readonly* fields are not taken account. -// Enumerations types are not taken account also. -// - -// -// To refactor such type and increase code quality and maintainability, -// certainly you'll have to group subsets of fields into smaller types -// and dispatch the logic implemented into the methods -// into these smaller types. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 10 minutes per field added. -// -// Issues of this rule have a **High** severity, because it is important to focus -// on these issues **now**, before such code gets released in production. -//]]> - Avoid transforming an immutable type into a mutable one -// ND1108:AvoidTransformingAnImmutableTypeIntoAMutableOne -warnif count > 0 -from t in Application.Types where - t.CodeWasChanged() && - t.OlderVersion().IsImmutable && - !t.IsImmutable && - // Don't take account of immutable types transformed into static types (not deemed as immutable) - !t.IsStatic - -let culpritFields = t.InstanceFields.Where(f => !f.IsImmutable) -select new { - t, - culpritFields, - Debt = (10 + 10*culpritFields.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// A type is considered as *immutable* if its instance fields -// cannot be modified once an instance has been built by a constructor. -// -// Being immutable has several fortunate consequences for a type. -// For example its instance objects can be used concurrently -// from several threads without the need to synchronize accesses. -// -// Hence users of such type often rely on the fact that the type is immutable. -// If an immutable type becomes mutable, there are chances that this will break -// users code. -// -// This is why this rule warns about such immutable type that become mutable. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 2 minutes per instance field that became mutable. -// - -// -// If being immutable is an important property for a matched type, -// then the code must be refactored to preserve immutability. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 10 minutes plus 10 minutes per instance fields of -// the matched type that is now mutable. -// -// Issues of this rule have a **High** severity, because it is important to focus -// on these issues **now**, before such code gets released in production. -//]]> - - - Avoid interfaces too big -// ND1200:AvoidInterfacesTooBig -warnif count > 0 - -from i in JustMyCode.Types -where i.IsInterface && i.NbMethods >= 10 // Optimization First threshold - -// A get;set; property count as one method -let properties = i.Methods.Where(m => m.SimpleName.Length > 4 && (m.IsPropertyGetter || m.IsPropertySetter)) - .Distinct(m => m.SimpleName.Substring(4, m.SimpleName.Length -4)) - -// An event count as one method -let events = i.Methods.Where(m => (m.IsEventAdder|| m.IsEventRemover)) - .Distinct(m => m.SimpleName.Replace("add_","").Replace("remove_","")) - -let methods = i.Methods.Where(m => !m.IsPropertyGetter && !m.IsPropertySetter && !m.IsEventAdder && !m.IsEventRemover) -let methodsCount = methods.Count() + properties.Count() + events.Count() -where methodsCount >= 10 -let publicFactor = i.IsPubliclyVisible ? 1 : 0.5 -orderby methodsCount descending -select new { - i, - Methods= methods, - Properties = properties, - Events = events, - Debt = (publicFactor*methodsCount.Linear(10, 20, 100, 7*60)).ToMinutes().ToDebt(), - // The annual interest varies linearly from interest for severity Medium for an interface with 10 methods - // to interest for severity Critical for an interface with 100 methods and more - AnnualInterest = (publicFactor*methodsCount.Linear( - 10, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 100, Severity.Critical.AnnualInterestThreshold().Value.TotalMinutes)) - .ToMinutes().ToAnnualInterest() -} - - -// -// This rule matches interfaces with more than 10 methods. -// Interfaces are abstractions and are meant to simplify the code structure. -// An interface should represent a single responsibility. -// Making an interface too large, too complex, necessarily means -// that the interface has too many responsibilities. -// -// A property with getter or setter or both count as one method. -// An event count as one method. -// - -// -// Typically to fix such issue, the interface must be refactored -// in a grape of smaller *single-responsibility* interfaces. -// -// A classic example is a *ISession* large interface, responsible -// for holding states, run commands and offer various accesses -// and facilities. -// -// The classic problem for a large public interface is that it has -// many clients that consume it. As a consequence splitting it in -// smaller interfaces has an important impact and it is not always -// feasible. -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from 20 minutes for an interface with 10 methods, -// up to 7 hours for an interface with 100 or more methods. -// The Debt is divided by two if the interface is not publicly -// visible, because in such situation only the current project is impacted -// by the refactoring. -// -]]> - Base class should not use derivatives -// ND1201:BaseClassShouldNotUseDerivatives -warnif count > 0 -from baseClass in JustMyCode.Types -where baseClass.IsClass && baseClass.NbChildren > 0 // <-- for optimization! -let derivedClassesUsed = baseClass.DerivedTypes.UsedBy(baseClass) - // Don't warn when a base class is using nested private derived class - .Where(derivedClass => - !(derivedClass.IsNested && - derivedClass.Visibility == Visibility.Private && - derivedClass.ParentType == baseClass)) -where derivedClassesUsed.Count() > 0 - -let derivedClassesMemberUsed = derivedClassesUsed.SelectMany(c => c.Members).UsedBy(baseClass) -orderby derivedClassesMemberUsed.Count() descending - -select new { - baseClass, - derivedClassesUsed, - derivedClassesMemberUsed, - - Debt = 3*(derivedClassesUsed.Count()+derivedClassesMemberUsed.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// In *Object-Oriented Programming*, the **open/closed principle** states: -// *software entities (components, classes, methods, etc.) should be open -// for extension, but closed for modification*. -// http://en.wikipedia.org/wiki/Open/closed_principle -// -// Hence a base class should be designed properly to make it easy to derive from, -// this is *extension*. But creating a new derived class, or modifying an -// existing one, shouldn't provoke any *modification* in the base class. -// And if a base class is using some derivative classes somehow, there -// are good chances that such *modification* will be needed. -// -// Extending the base class is not anymore a simple operation, -// this is not good design. -// -// Note that this rule doesn't warn when a base class is using a derived class -// that is nested in the base class and declared as private. In such situation -// we consider that the derived class is an encapsulated implementation -// detail of the base class. -// - -// -// Understand the need for using derivatives, -// then imagine a new design, and then refactor. -// -// Typically an algorithm in the base class needs to access something -// from derived classes. You can try to encapsulate this access behind -// an abstract or a virtual method. -// -// If you see in the base class some conditions on *typeof(DerivedClass)* -// not only *urgent refactoring* is needed. Such condition can easily -// be replaced through an abstract or a virtual method. -// -// Sometime you'll see a base class that creates instance of some derived classes. -// In such situation, certainly using the *factory method pattern* -// http://en.wikipedia.org/wiki/Factory_method_pattern -// or the *abstract factory pattern* -// http://en.wikipedia.org/wiki/Abstract_factory_pattern -// will improve the design. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 3 minutes per derived class used by the base class + -// 3 minutes per member of a derived class used by the base class. -//]]> - Class shouldn't be too deep in inheritance tree -// ND1202:ClassShouldntBeTooDeepInInheritanceTree -warnif count > 0 from t in JustMyCode.Types -where t.IsClass -let baseClasses = t.BaseClasses.ExceptThirdParty() -where baseClasses.Count() >= 3 -orderby baseClasses.Count() descending - -select new { - t, - baseClasses, - // The metric value DepthOfInheritance takes account - // of third-party base classessee its definition here: - // https://www.ndepend.com/docs/code-metrics#DIT - t.DepthOfInheritance, - Debt = (baseClasses.Count() -2)*3.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns about classes having 3 or more base classes. -// Notice that third-party base classes are not counted -// because this rule is about your code design, not -// third-party libraries consumed design. -// -// *In theory*, there is nothing wrong having a *long inheritance chain*, -// if the modelization has been well thought out, -// if each base class is a well-designed refinement of the domain. -// -// *In practice*, modeling properly a domain demands a lot of effort -// and experience and more often than not, a *long inheritance chain* -// is a sign of confused design, that will be hard to work with and maintain. -// - -// -// In *Object-Oriented Programming*, a well-known motto is -// **Favor Composition over Inheritance**. -// -// This is because *inheritance* comes with pitfalls. -// In general, the implementation of a derived class is very bound up with -// the base class implementation. Also a base class exposes implementation -// details to its derived classes, that's why it's often said that -// inheritance breaks encapsulation. -// -// On the other hands, *Composition* favors binding with interfaces -// over binding with implementations. Hence, not only the encapsulation -// is preserved, but the design is clearer, because interfaces make it explicit -// and less coupled. -// -// Hence, to break a *long inheritance chain*, *Composition* is often -// a powerful way to enhance the design of the refactored underlying logic. -// -// You can also read: -// http://en.wikipedia.org/wiki/Composition_over_inheritance and -// http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance -// -// The estimated Debt, which means the effort to fix such issue, -// depends linearly upon the depth of inheritance. -//]]> - Class with no descendant should be sealed if possible -// ND1203:ClassWithNoDescendantShouldBeSealedIfPossible -warnif count > 0 from t in JustMyCode.Types where - t.IsClass && - t.NbChildren ==0 && - !t.IsSealed && - !t.IsStatic && - !t.IsPubliclyVisible // You might want to comment this condition - // if you are developing an application, - // instead of developing a library - // with public classes that are intended to be - // sub-classed by your clients. - orderby t.NbLinesOfCode descending -select new { - t, - t.NbLinesOfCode, - Debt = 30.ToSeconds().ToDebt(), - Severity = Severity.Medium -} - -// -// If a *non-static* class isn't declared with the keyword *sealed*, -// it means that it can be subclassed everywhere the *non-sealed* -// class is visible. -// -// Making a class a *base class* requires significant design effort. -// Subclassing a *non-sealed* class, not initially designed -// to be subclassed, will lead to unanticipated design issue. -// -// Most classes are *non-sealed* because developers don't care about -// the keyword *sealed*, not because the primary intention was to write -// a class that can be subclassed. -// -// There are minor performance gain in declaring a class as *sealed*. -// But the real benefit of doing so, is actually to **express the -// intention**: *this class has not be designed to be a base class, -// hence it is not allowed to subclass it*. -// -// Notice that by default this rule doesn't match *public* class -// to avoid matching classes that are intended to be sub-classed by -// third-party code using your library. -// If you are developing an application and not a library, -// just uncomment the clause *!t.IsPubliclyVisible*. -// - -// -// For each matched class, take the time to assess if it is really -// meant to be subclassed. Certainly most matched class will end up -// being declared as *sealed*. -//]]> - Overrides of Method() should call base.Method() -// ND1204:OverridesOfMethodShouldCallBaseMethod -warnif count > 0 -from t in Types // Take account of third-party base classes also - -// Bother only classes with descendant -where t.IsClass && t.NbChildren > 0 - -from mBase in t.InstanceMethods -where mBase.IsVirtual && - !mBase.IsThirdParty && - !mBase.IsAbstract && - !mBase.IsExplicitInterfaceImpl && - !mBase.IsPropertyGetter && - !mBase.IsPropertySetter && - !mBase.IsIndexerGetter && - !mBase.IsIndexerSetter && - // Don't take account of virtual methods of the Object type. - !mBase.Name.EqualsAny("Equals(Object)","ToString()", "GetHashCode()","Finalize()") - -from mOverride in mBase.OverridesDirectDerived -where !mOverride.IsUsing(mBase) && - JustMyCode.Contains(mOverride) // Don't warn on generated code -select new { - mOverride, - shouldCall = mBase, - definedInBaseClass = mBase.ParentType, - - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// Typically overrides of a base method, should **refine** or **complete** -// the behavior of the base method. If the base method is not called, -// the base behavior is not refined but it is *replaced*. -// -// Violations of this rule are a sign of *design flaw*, -// especially if the actual design provides valid reasons -// that advocates that the base behavior must be replaced and not refined. -// - -// -// You should investigate if *inheritance* is the right choice -// to bind the base class implementation with the derived classes -// implementations. Does presenting the method with polymorphic -// behavior through an interface, would be a better design choice? -// -// In such situation, often using the design pattern **template method** -// http://en.wikipedia.org/wiki/Template_method_pattern might help -// improving the design. -//]]> - Do not hide base class methods -// ND1205:DoNotHideBaseClassMethods -warnif count > 0 - -// Define a lookup table indexing methods by their name including parameters signature. -let lookup = Methods.Where(m => !m.IsConstructor && !m.IsStatic && !m.IsGeneratedByCompiler) - .ToLookup(m1 => m1.Name) - -from t in Application.Types -where !t.IsStatic && t.IsClass && - // Discard classes deriving directly from System.Object - t.DepthOfInheritance > 1 -where t.BaseClasses.Any() - -// For each methods not overriding any methods (new slot), -// let's check if it hides by name some methods defined in base classes. -from m in t.InstanceMethods -where m.IsNewSlot && !m.IsExplicitInterfaceImpl && !m.IsGeneratedByCompiler - -// Notice how lookup is used to quickly retrieve methods with same name as m. -// This makes the query 10 times faster than iterating each base methods to check their name. -let baseMethodsHidden = lookup[m.Name].Where(m1 => m1 != m && t.DeriveFrom(m1.ParentType)) - -where baseMethodsHidden.Count() > 0 -select new { - m, - baseMethodsHidden, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// Method hiding is when a base class has a non-virtual method *M()*, -// and a derived class has also a method *M()* with the same signature. -// In such situation, calling *base.M()* does something different -// than calling *derived.M()*. -// -// Notice that this is not *polymorphic* behavior. With *polymorphic* -// behavior, calling both *base.M()* and *derived.M()* on an instance -// object of *derived*, invoke the same implementation. -// -// This situation should be avoided because it obviously leads to confusion. -// This rule warns about all method hiding cases in the code base. -// - -// -// To fix a violation of this rule, remove or rename the method, -// or change the parameter signature so that the method does -// not hide the base method. -// -// However *method hiding is for those times when you need to have two -// things to have the same name but different behavior*. This is a very -// rare situations, described here: -// http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx -//]]> - A stateless class or structure might be turned into a static type -// ND1206:AStatelessClassOrStructureMightBeTurnedIntoAStaticType -warnif count > 0 - -let testAttributes = ThirdParty.Types - .Where(t => t.IsAttributeClass && t.SimpleName.Contains("Test")).ToArray() - -from t in JustMyCode.Types where - !t.IsStatic && - !t.IsGeneric && - t.InstanceFields.Count() == 0 && - t.SimpleName != "Program" && // Don't warn on Program classes generated by designers - - // Don't match: - // --> types that implement some interfaces. - t.NbInterfacesImplemented == 0 && - - // --> or classes that have sub-classes children. - t.NbChildren == 0 && - - // --> or classes that have a base class - ((t.IsClass && t.DepthOfDeriveFrom("System.Object".AllowNoMatch()) == 1) || - t.IsStructure) && - - // Don't match test classes - !testAttributes.Any(tAttr => t.HasAttribute(tAttr)) - -let methodsUsingMe = t.TypesUsingMe.ChildMethods().Where(m => m.IsUsing(t)) - -select new { - t, - methodsUsingMe, - Debt = (1 + methodsUsingMe.Count()).ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// This rule matches classes and structures that are not static, nor generic, -// that doesn't have any instance fields, that doesn't implement any interface -// nor has a base class (different than *System.Object*). -// -// Such class or structure is a *stateless* collection of *pure* functions, -// that doesn't act on any *this* object data. Such collection of *pure* functions -// is better hosted in a **static class**. Doing so simplifies the client code -// that doesn't have to create an object anymore to invoke the *pure* functions. -// - -// -// Declare all methods as *static* and transform the class or structure -// into a *static* class. -// -// By default issues of this rule have a **Low** severity -// because they reflect more an advice than a problem. -//]]> - Non-static classes should be instantiated or turned to static -// ND1207:NonStaticClassesShouldBeInstantiatedOrTurnedToStatic -warnif count > 0 - -let testAttributes = Types - .Where(t => t.IsAttributeClass && t.SimpleName.Contains("Test")).ToArray() - -from t in JustMyCode.Types -where t.IsClass && - //!t.IsPublic && // if you are developing a framework, - // you might not want to match public classes - !t.IsStatic && - !t.IsAbstract && - !t.IsAttributeClass && // Attributes class are never seen as instantiated - - // Don't suggest to turn to static, classes that implement interfaces - t.InterfacesImplemented.Count() == 0 && - - // Find the first constructor of t called - // match t if none of its constructors is called. - t.Constructors.FirstOrDefault(ctor => ctor.NbMethodsCallingMe > 0) == null && - - // Don't warn on Program classes generated by designers - t.SimpleName != "Program" && - - // Types instantiated through remoting infrastructure - !t.DeriveFrom("System.MarshalByRefObject".AllowNoMatch()) && - - // JSON and XML serialized types might not be seen as instantiated. - !t.IsUsing("Newtonsoft.Json".MatchNamespace().AllowNoMatch()) && - !t.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) && - !t.IsUsing("System.Xml.Serialization.XmlElementAttribute".AllowNoMatch()) && - !t.IsUsing("System.Xml.Serialization.XmlAttributeAttribute".AllowNoMatch()) && - - // Serialized type might never be seen as instantiated. - !t.HasAttribute("System.Runtime.Serialization.DataContractAttribute".AllowNoMatch()) && - !t.IsUsing("System.Runtime.Serialization.DataMemberAttribute".AllowNoMatch()) && - - // ASP.NET Core ViewModel and Repository - !t.SimpleName.EndsWithAny("Model","Repository") && - !t.IsUsing("System.ComponentModel.DataAnnotations".AllowNoMatch().MatchNamespace()) && - - // ASP.NET Classes that are instantiated by the ASP.NET infrastructure. - !t.BaseClasses.Any(bc => bc.ParentNamespace.Name.StartsWithAny("System.Web", "Microsoft.AspNetCore")) && - !(t.Constructors.Count() == 1 && t.Constructors.Single().Name.Contains("(IHostingEnvironment)")) && - - // Entity Framework ModelSnapshot and DbContext and Migration - !t.DeriveFrom("Microsoft.EntityFrameworkCore.Infrastructure.ModelSnapshot".AllowNoMatch()) && - !t.DeriveFrom("Microsoft.EntityFrameworkCore.DbContext".AllowNoMatch()) && - !t.DeriveFrom("Microsoft.EntityFrameworkCore.Migrations.Migration".AllowNoMatch()) && - - // Don't match test classes - !testAttributes.Any(tAttr => t.HasAttribute(tAttr)) && - - // Validtor classes might not be instantiated - !t.SimpleName.EndsWith("Validator") && - - // Don't warn about classes instantiated by Dependency Injection frameworks - !t.TypesUsingMe.Any(t1 => t1.TypesUsed.ParentNamespaces().Any(n => n.Name.StartsWithAny( - "Microsoft.Extensions.DependencyInjection", - "Autofac", "Microsoft.Practices.Unity", "Ninject", - "StructureMap", "SimpleInjector", "Castle.Windsor", - "LightInject", "Spring", "Lamar" - ))) - -select new { - t, - t.Visibility, - Debt = 2.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// Notice that classes only instantiated through reflection, like plug-in root classes -// are matched by this rules. - -// -// If the constructors of a class are never called, the class is -// never instantiated, and should be defined as a *static class*. -// -// However this rule doesn't match instantiation through reflection. -// As a consequence, plug-in root classes, instantiated through reflection -// via *IoC frameworks*, can be *false positives* for this rule. -// -// This rule doesn't match also classes instantiated by the ASP.NET -// infrastructure, ASP.NET view model classes -// and Entity Framework ModelSnapshot, DbContext and Migration classes. -// -// Notice that by default this rule matches also *public* class. -// If you are developing a framework with classes that are intended -// to be instantiated by your clients, just uncomment the line -// *!t.IsPublic*. -// - -// -// First it is important to investigate why the class is never instantiated. -// If the reason is *the class hosts only static methods* then the class -// can be safely declared as *static*. -// -// Others reasons like, *the class is meant to be instantiated via reflection*, -// or *is meant to be instantiated only by client code* should lead to -// adapt this rule code to avoid these matches. -// -]]> - Methods should be declared static if possible -// ND1208:MethodsShouldBeDeclaredStaticIfPossible -warnif count > 0 - -let testAttributes = ThirdParty.Types - .Where(t => t.IsAttributeClass && - ( t.SimpleName.Contains("Test") || - t.SimpleName.Contains("Fact") || - t.SimpleName.Contains("SetUp") || - t.SimpleName.Contains("TearDown") ) - ).ToArray() - -from t in JustMyCode.Types.Where(t => - !t.IsStatic && !t.IsInterface && - !t.IsEnumeration && !t.IsDelegate && - !t.IsGeneratedByCompiler && - - // Don't advise to declare Global ASP.NET or ApiController methods as static - !t.DeriveFrom("System.Web.HttpApplication".AllowNoMatch()) && - !t.DeriveFrom("System.Web.Http.ApiController".AllowNoMatch()) && - !t.DeriveFrom("Microsoft.AspNetCore.Mvc.Controller".AllowNoMatch()) && - - // Don't set as static methods of JSON serialized type - !t.TypesUsed.Any(t1 => t1.IsAttributeClass && t1.ParentNamespace.Name == "Newtonsoft.Json") && - - // Don't set as static methods of XML serialized type - !t.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) && - !t.IsUsing("System.Xml.Serialization.XmlElementAttribute".AllowNoMatch()) && - !t.IsUsing("System.Xml.Serialization.XmlAttributeAttribute".AllowNoMatch())) - -let methodsThatCanBeMadeStatic = - from m in t.InstanceMethods - - // An instance method can be turned to static if it is not virtual, - // not using the this reference and also, not using - // any of its class or base classes instance fields or instance methods. - where !m.IsAbstract && !m.IsVirtual && - !m.AccessThis && !m.IsExplicitInterfaceImpl && - !m.IsProtected && // Protected method access doesn't match well with static methods - - // Don't warn about not yet implemented methods. - !m.CreateA("System.NotImplementedException".AllowNoMatch()) && - - // Optimization: Using FirstOrDefault() avoid to check all members, - // as soon as one member is found - // we know the method m cannot be made static. - m.MembersUsed.FirstOrDefault( - mUsed => !mUsed.IsStatic && - (mUsed.ParentType == t || - t.DeriveFrom(mUsed.ParentType)) - ) == null - - // Don't match test methods - && !testAttributes.Any(tAttr => m.HasAttribute(tAttr)) - select m - -from m in methodsThatCanBeMadeStatic -let staticFieldsUsed = m.ParentType.StaticFields.UsedBy(m).Where(f => !f.IsGeneratedByCompiler) -let methodsCallingMe = m.MethodsCallingMe - -// All callers of the method must be in JustMyCode, -// else having a method declared as static would break the call from the code generated -// like when a WPF Connect() method is binding a method to an event. -where methodsCallingMe.All(m1 => JustMyCode.Contains(m1)) - -select new { - m, - staticFieldsUsed, - methodsCallingMe, - Debt = (1 + methodsCallingMe.Count())*30.ToSeconds().ToDebt(), - Severity = Severity.Medium -} - -// -// When an instance method can be *safely* declared as static you should declare it as static. -// -// Whenever you write a method, you fulfill a contract in a given scope. -// The narrower the scope is, the smaller the chance is that you write a bug. -// -// When a method is static, you can't access non-static members; hence, your scope is -// narrower. So, if you don't need and will never need (even in subclasses) instance -// fields to fulfill your contract, why give access to these fields to your method? -// Declaring the method static in this case will let the compiler check that you -// don't use members that you do not intend to use. -// -// Declaring a method as static if possible is also good practice because clients can -// tell from the method signature that calling the method can't alter the object's state. -// -// Doing so, is also a micro performance optimization, since a static method is a -// bit cheaper to invoke than an instance method, because the *this* reference* -// doesn't need anymore to be passed. -// -// Notice that if a matched method is a handler, bound to an event through code -// generated by a designer, declaring it as static might break the designer -// generated code, if the generated code use the *this* invocation syntax, -// (like *this.Method()*). -// - -// -// Declare matched methods as static. -// -// Since such method doesn't use any instance fields and methods of its type and -// base-types, you should consider if it makes sense, to move such a method -// to a static utility class. -//]]> - Constructor should not call a virtual method -// ND1209:ConstructorShouldNotCallAVirtualMethod -warnif count > 0 - -from t in JustMyCode.Types where - t.IsClass && - !t.IsGeneratedByCompiler && - !t.IsSealed - -from ctor in t.Constructors -let virtualMethodsCalled = - from mCalled in ctor.MethodsCalled - where mCalled.IsVirtual && !mCalled.IsFinal && - // Only take care of just-my-code virtual methods called - JustMyCode.Contains(mCalled) && - ( mCalled.ParentType == t || - (t.DeriveFrom(mCalled.ParentType) && - // Don't accept Object methods since they can be called - // from another reference than the 'this' reference. - mCalled.ParentType.FullName != "System.Object") - ) - select mCalled -where virtualMethodsCalled.Count() > 0 - -select new { - ctor , - virtualMethodsCalled, - // If there is no derived type, it might be - // an opportunity to mark t as sealed. - t.DerivedTypes, - Debt = ((virtualMethodsCalled.Count())*6).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule matches constructors of a non-sealed class that call one or -// several virtual methods. -// -// When an object written in C# is constructed, what happens is that constructors -// run in order from the base class to the most derived class. -// -// Also objects do not change type as they are constructed, but start out as -// the most derived type, with the method table being for the most derived type. -// This means that virtual method calls always run on the most derived type, -// even when calls are made from the constructor. -// -// When you combine these two facts you are left with the problem that if you -// make a virtual method call in a constructor, and it is not the most derived -// type in its inheritance hierarchy, then it will be called on a class whose -// constructor has not been run, and therefore may not be in a suitable state -// to have that method called. -// -// Hence this situation makes the class *fragile to derive from*. -// - -// -// Violations reported can be solved by re-designing object initialisation -// or by declaring the parent class as *sealed*, if possible. -// -]]> - Avoid the Singleton pattern -// ND1210:AvoidTheSingletonPattern -warnif count > 0 -from t in Application.Types -where !t.IsStatic && !t.IsAbstract && (t.IsClass || t.IsStructure) - -// All ctors of a singleton are private -where t.Constructors.Where(ctor => !ctor.IsPrivate).Count() == 0 - -// Require mutable instance fields to be shared across the several clients of the single object. -let mutableInstanceFields = t.InstanceFields.Where(f => !f.IsImmutable) -where mutableInstanceFields.Any() - -// A singleton contains one or several static fields of its parent type, -// or of an interface implented by its parent type, -// to reference the unique instance -let staticFieldInstances = t.StaticFields.WithFieldTypeIn(t.InterfacesImplemented.Concat(t)) -where staticFieldInstances.Count() == 1 - -let staticFieldInstance = staticFieldInstances.Single() -let methodsUsingField = staticFieldInstance.MethodsUsingMe -let methodsUsingField2 = methodsUsingField.Concat(methodsUsingField.SelectMany(m => m.MethodsCallingMe)) - -select new { - t, - staticFieldInstance, - methodsUsingField2, - mutableInstanceFields , - Debt = (3*methodsUsingField2.Count()).ToMinutes().ToDebt(), - AnnualInterest = (10+methodsUsingField2.Count()).ToMinutes().ToAnnualInterest() -} - -// -// The *singleton pattern* consists in enforcing that a class has just -// a single instance: http://en.wikipedia.org/wiki/Singleton_pattern -// At first glance, this pattern looks appealing, it is simple to implement, -// it adresses a common situation, and as a consequence it is widely used. -// -// However, we discourage you from using singleton classes because experience -// shows that **singleton often results in less testable and less maintainable code**. -// Singleton is *by-design*, not testable. Each unit test should use their own objects -// while singleton forces multiple unit-tests to use the same instance object. -// -// Also the singleton static *GetInstance()* method allows *magic* access to that -// single object and its state from wherever developers want! This potentially -// attractive facility unfortunatly ends up into *unorganized*/*messy* code that -// will require effort to be refactored. -// -// Notice that this rule matches only singleton types with **mutable** instance fields -// because singleton pitfalls result from anarchical access and modification -// of instance data. -// -// More details available in these discussions: -// http://codebetter.com/patricksmacchia/2011/05/04/back-to-basics-usage-of-static-members/ -// http://adamschepis.com/blog/2011/05/02/im-adam-and-im-a-recovering-singleton-addict/ -// - -// -// This rule matches *the classic syntax of singletons*, where one -// static field hold the single instance of the parent class. We underline that -// *the problem is this particular syntax*, that plays against testability. -// The problem is not the fact that a single instance of the class lives -// at runtime. -// -// Hence to fix matches fo this rule, creates the single instance -// at the startup of the program, and pass it to all classes and methods -// that need to access it. -// -// If multiple singletons are identified, they actually form together a -// *program execution context*. Such context can be unified in a unique -// singleton context. Doing so will make it easier to propagate the -// context across the various program units. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 3 minutes per method relying on the singleton. -// It is not rare that hundreds of methods rely on the singleton -// and that it takes hours to get rid of a singleton, refactoring -// the way just explained above. -// -// The severity of each singleton issue is **Critical** because as -// explained, using a the singleton pattern can really prevent the -// whole program to be testable. -//]]> - Don't assign static fields from instance methods -// ND1211:DontAssignStaticFieldsFromInstanceMethods -warnif count > 0 -from f in Application.Fields where - f.IsStatic && - !f.IsLiteral && - !f.IsInitOnly && - !f.IsGeneratedByCompiler && - // Contract API define such a insideContractEvaluation static field - f.Name != "insideContractEvaluation" -let assignedBy = f.MethodsAssigningMe.Where(m => !m.IsStatic) -where assignedBy .Count() > 0 -select new { - f, - assignedBy, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// Assigning static fields from instance methods leads to -// poorly maintainable and non-thread-safe code. -// -// More discussion on the topic can be found here: -// http://codebetter.com/patricksmacchia/2011/05/04/back-to-basics-usage-of-static-members/ -// - -// -// If the *static* field is just assigned once in the program -// lifetime, make sure to declare it as *readonly* and assign -// it inline, or from the static constructor. -// -// In *Object-Oriented-Programming* the natural artifact -// to hold states that can be modified is **instance fields**. -// -// Hence to fix violations of this rule, make sure to -// hold assignable states through *instance* fields, not -// through *static* fields. -//]]> - Avoid empty interfaces -// ND1212:AvoidEmptyInterfaces -warnif count > 0 from t in JustMyCode.Types where - t.IsInterface && - t.NbMethods == 0 && - !t.InterfacesImplemented.Any() -select new { - t, - t.TypesThatImplementMe, - Debt = (10 + 3*t.TypesThatImplementMe.Count()).ToMinutes().ToDebt(), - Severity = t.TypesThatImplementMe.Any() ? Severity.Medium : Severity.Low -} - -// -// Interfaces define members that provide a behavior or usage contract. -// The functionality that is described by the interface -// can be adopted by any type, regardless of where the type -// appears in the inheritance hierarchy. -// A type implements an interface by providing implementations -// for the members of the interface. -// An empty interface does not define any members. -// Therefore, it does not define a contract that can be implemented. -// -// If your design includes empty interfaces that types -// are expected to implement, you are probably using an interface -// as a marker or a way to identify a group of types. -// If this identification will occur at run time, -// the correct way to accomplish this is to use a custom attribute. -// Use the presence or absence of the attribute, -// or the properties of the attribute, to identify the target types. -// If the identification must occur at compile time, -// then it is acceptable to use an empty interface. -// -// Note that if an interface is empty but implements at least one -// other interface, it won't be matched by this rule. -// Such interface can be considered as not empty, -// since implementing it means that sub-interfaces members -// must be implemented. -// - -// -// Remove the interface or add members to it. -// If the empty interface is being used to label a set of types, -// replace the interface with a custom attribute. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 10 minutes to discard an empty interface plus -// 3 minutes per type implementing an empty interface. -//]]> - Avoid types initialization cycles -// ND1213:AvoidTypesInitializationCycles -warnif count > 0 - -// Types initialization cycle can only happen between types of an assembly. -from assembly in Application.Assemblies - -let cctorSuspects = assembly.ChildMethods.Where( - m => m.IsClassConstructor && - // Optimization: types involved in a type cycle necessarily don't have type level. - m.ParentType.Level == null) - -where cctorSuspects.Count() > 1 -let typesSuspects = cctorSuspects.ParentTypes().ToHashSetEx() - -// -// dicoTmp associates to each type suspect T, a set of types from typesSuspects -// that contains at least a method or a field used directly or indirectly by the cctor of T. -// -let dicoTmp = cctorSuspects.ToDictionary( - cctor => cctor.ParentType, - cctor => ((IMember)cctor).ToEnumerable().FillIterative( - members => from m in members - from mUsed in (m is IMethod) ? (m as IMethod).MembersUsed : new IMember[0] - where mUsed.ParentAssembly == assembly - select mUsed) - .DefinitionDomain - .Select(m => m.ParentType) // Don't need .Distinct() here, because of ToHashSetEx() below. - .Except(cctor.ParentType) - .Intersect(typesSuspects) - .ToHashSetEx() -) - -// -// dico associates to each type suspect T, the set of types initialized (directly or indirectly) -// by the initialization of T. This second step is needed, because if a cctor of a type T1 -// calls a member of a type T2, not only the cctor of T1 triggers the initialization of T2, -// but also it triggers the initialization of all types that are initialized by T2 initialization. -// -let dico = typesSuspects.Where(t => dicoTmp[t].Count() > 0).ToDictionary( - typeSuspect => typeSuspect, - typeSuspect => typeSuspect.ToEnumerable().FillIterative( - types => from t in types - from tUsed in dicoTmp[t] - select tUsed) - .DefinitionDomain - .Except(typeSuspect) - .ToHashSetEx() -) - - -// -// Now that dico is prepared, detect the cctor cycles -// -from t in dico.Keys - - // Thanks to the work done to build dico, it is now pretty easy - // to spot types involved in an initialization cyle with t! - let usersAndUseds = from tTmp in dico[t] - where dico.ContainsKey(tTmp) && dico[tTmp].Contains(t) - select tTmp - where usersAndUseds.Count() > 0 - - // Here we've found type(s) both using and used by the suspect type. - // A cycle involving the type t is found! - // v2017.3.2: don't call Append() as an extension method else ambiguous syntax error - // with the new extension method in .NET Fx v4.7.1 / .NET Standard 2.0: System.Linq.Enumerable.Append() - let typeInitCycle = ExtensionMethodsEnumerable.Append(usersAndUseds,t) - - - // Compute methodsCalled and fieldsUsed, useful to explore - // how a cctor involved in a type initialization cycle, triggers other type initialization. - let methodsCalledDepth = assembly.ChildMethods.DepthOfIsUsedBy(t.ClassConstructor) - let fieldsUsedDepth = assembly.ChildFields.DepthOfIsUsedBy(t.ClassConstructor) - - let methodsCalled = methodsCalledDepth.DefinitionDomain.OrderBy(m => methodsCalledDepth[m]).ToArray() - let fieldsUsed = fieldsUsedDepth.DefinitionDomain.OrderBy(f => fieldsUsedDepth[f]).ToArray() - -// Use the tick box to: Group cctors methods By parent types -select new { - t.ClassConstructor, - cctorsCycle= typeInitCycle.Select(tTmp => tTmp.ClassConstructor), - - // methodsCalled and fieldsUsed are members used directly and indirectly by the cctor. - // Export these members to the dependency graph (right click the cell Export/Append … to the Graph) - // and see how the cctor trigger the initialization of other types - methodsCalled, - fieldsUsed, - Debt = (20+10*typeInitCycle.Count()).ToMinutes().ToDebt(), - Severity = Severity.Critical -} - -// -// The *class constructor* (also called *static constructor*, and named *cctor* in IL code) -// of a type, if any, is executed by the CLR at runtime, the first time the type is used. -// A *cctor* doesn't need to be explicitly declared in C# or VB.NET, to exist in compiled IL code. -// Having a static field inline initialization is enough to have -// the *cctor* implicitly declared in the parent class or structure. -// -// If the *cctor* of a type *t1* is using the type *t2* and if the *cctor* of *t2* is using *t1*, -// some type initialization unexpected and hard-to-diagnose buggy behavior can occur. -// Such a cyclic chain of initialization is not necessarily limited to two types -// and can embrace *N* types in the general case. -// More information on types initialization cycles can be found here: -// http://codeblog.jonskeet.uk/2012/04/07/type-initializer-circular-dependencies/ -// -// The present code rule enumerates types initialization cycles. -// Some *false positives* can appear if some lambda expressions are defined -// in *cctors* or in methods called by *cctors*. In such situation, this rule -// considers these lambda expressions as executed at type initialization time, -// while it is not necessarily the case. -// - -// -// Types initialization cycles create confusion and unexpected behaviors. -// If several states hold by several classes must be initialized during the first -// access of any of those classes, a better design option is to create a dedicated -// class whose responsibility is to initialize and hold all these states. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 20 minutes per cycle plus 10 minutes per type class constructor -// involved in the cycle. -//]]> - - - Avoid custom delegates -// ND1300:AvoidCustomDelegates - -warnif count > 0 -from t in JustMyCode.Types where t.IsDelegate - -let invokeMethod = (from m in t.Methods where m.SimpleName == "Invoke" select m).Single() -let signature1 = invokeMethod.Name.Substring( - invokeMethod.SimpleName.Length, - invokeMethod.Name.Length - invokeMethod.SimpleName.Length) - -// 'ref' and 'out' parameters cannot be supported -where !signature1.Contains("&") - -let signature2 = signature1.Replace("(","<").Replace(")",">") -let signature3 = signature2 == "<>" ? "" : signature2 -let resultTypeName = invokeMethod.ReturnType == null ? "????" : - invokeMethod.ReturnType.FullName == "System.Void" ? "" : - invokeMethod.ReturnType.Name -let replaceWith = - resultTypeName == "Boolean" && invokeMethod.NbParameters == 1 ? - "Predicate" + signature3 : resultTypeName == "" ? - "Action" + signature3 : invokeMethod.NbParameters ==0 ? - "Func<" + resultTypeName + ">" : - "Func" + signature3.Replace(">", "," + resultTypeName + ">") - -let methodsUser = t.TypesUsingMe.ChildMethods().Where(m => m.IsUsing(t)) - -select new { - t, - replaceWith, - methodsUser, - Debt = (5 + 3*methodsUser.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// Generic delegates sould be preferred over custom delegates. -// Generic delegates are: -// -// • *Action<…>* to represent any method with *void* return type. -// -// • *Func<…>* to represent any method with a return type. The last -// generic argument is the return type of the prototyped methods. -// -// • *Predicate* to represent any method that takes an instance -// of *T* and that returns a *boolean*. -// -// • Expression<…> that represents function definitions that can be -// compiled and subsequently invoked at runtime but can also be -// serialized and passed to remote processes. -// -// Thanks to generic delegates, not only the code using these custom -// delegates will become clearer, but you'll be relieved from the -// maintenance of these delegate types. -// -// Notice that delegates that are consumed by *DllImport* extern methods -// must not be converted, else this could provoke marshalling issues. -// - -// -// Remove custom delegates and replace them with generic -// delegates shown in the **replaceWith** column. -// -// The estimated Debt, which means the effort to fix such issue, -// is 5 minutes per custom delegates plus 3 minutes per method -// using such custom delegate. -//]]> - Types with disposable instance fields must be disposable -// ND1301:TypesWithDisposableInstanceFieldsMustBeDisposable -warnif count > 0 - -// Several IDisposable types can be found if several .NET profiles are referenced. -let iDisposables = ThirdParty.Types.WithFullName("System.IDisposable") -where iDisposables.Any() // in case the code base doesn't use at all System.IDisposable - -from t in Application.Types.Except( - Application.Types.ThatImplementAny(iDisposables) - // Don't match ASP.NET types like Page, MasterPage or Control - .Union(Application.Types.Where(t => t.BaseClasses.Any(bc => bc.ParentNamespace.Name.StartsWith("System.Web.UI"))))) -where !t.IsGeneratedByCompiler - -let instanceFieldsDisposable = - t.InstanceFields.Where(f => f.FieldType != null && - f.FieldType.InterfacesImplemented.Intersect(iDisposables).Any()) - -where instanceFieldsDisposable.Any() - -// Don't warn for types that implement the dispose pattern with a Dispose(bool) method. -where t.Methods.FirstOrDefault(m => m.Name == "Dispose(Boolean)") == null - -select new { - t, - instanceFieldsDisposable, - Debt = (5 + 2*instanceFieldsDisposable.Count()).ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns when a class declares and implements an instance field that -// is a *System.IDisposable* type and the class does not implement *IDisposable*. -// -// A class implements the *IDisposable* interface to dispose of unmanaged resources -// that it owns. An instance field that is an *IDisposable* type indicates that -// the field owns an unmanaged resource. A class that declares an *IDisposable* -// field indirectly owns an unmanaged resource and should implement the -// *IDisposable* interface. If the class does not directly own any unmanaged -// resources, it should not implement a finalizer. -// -// This rules might report false positive in case the lifetime of the disposable -// objects referenced, is longer than the lifetime of the object that hold the -// disposable references. -// - -// -// To fix a violation of this rule, implement *IDisposable* and from the -// *IDisposable.Dispose()* method call the *Dispose()* method of the field(s). -// -// The estimated Debt, which means the effort to fix such issue, -// is 5 minutes per type matched plus 3 minutes per disposable instance field. -//]]> - Disposable types with unmanaged resources should declare finalizer -// ND1302:DisposableTypesWithUnmanagedResourcesShouldDeclareFinalizer - -// This default rule is disabled by default, -// see in the rule description (below) why. -// warnif count > 0 - -// Several IDisposable type can be found if several .NET Fx are referenced. -let iDisposables = ThirdParty.Types.WithFullName("System.IDisposable") -where iDisposables.Any() // in case the code base doesn't use at all System.IDisposable - -let disposableTypes = Application.Types.ThatImplementAny(iDisposables) -let unmanagedResourcesFields = disposableTypes.ChildFields().Where(f => - !f.IsStatic && - f.FieldType != null && - f.FieldType.FullName.EqualsAny( - "System.IntPtr", - "System.UIntPtr", - "System.Runtime.InteropServices.HandleRef")).ToHashSetEx() -let disposableTypesWithUnmanagedResource = unmanagedResourcesFields.ParentTypes() - -from t in disposableTypesWithUnmanagedResource -where !t.HasFinalizer -let unmanagedResourcesTypeFields = unmanagedResourcesFields.Intersect(t.InstanceFields) -select new { - t, - unmanagedResourcesTypeFields, - //Debt = 10.ToMinutes().ToDebt(), - //Severity = Severity.Critical -} - -// -//A type that implements *System.IDisposable*, -//and has fields that suggest the use of unmanaged resources, -//does not implement a finalizer as described by *Object.Finalize()*. -//A violation of this rule is reported -//if the disposable type contains fields of the following types: -// -// • *System.IntPtr* -// -// • *System.UIntPtr* -// -// • *System.Runtime.InteropServices.HandleRef* -// -// Notice that this default rule is disabled by default, -// because it typically reports *false positive* for classes -// that just hold some references to managed resources, -// without the responsibility to dispose them. -// -// To enable this rule just uncomment *warnif count > 0*. -// - -// -//To fix a violation of this rule, -//implement a finalizer that calls your *Dispose()* method. -//]]> - Methods that create disposable object(s) and that don't call Dispose() -// ND1303:MethodsThatCreateDisposableObjectAndThatDontCallDispose - -// Uncomment this to transform this code query into a code rule. -// warnif count > 0 - -// Several IDisposable types can be found if several .NET Fx are referenced. -let iDisposables = ThirdParty.Types.WithFullName("System.IDisposable") -where iDisposables.Any() // in case the code base doesn't use at all System.IDisposable - -// Build sequences of disposableTypes and disposeMethods -let disposableTypes = Types.ThatImplementAny(iDisposables).Concat(iDisposables) -let disposeMethods = disposableTypes.ChildMethods().WithName("Dispose()").ToHashSetEx() - - -// -> You can refine this code query by assigning to disposableTypesToLookAfter something like: -// disposableTypes.WithFullNameIn("Namespace.TypeName1", "Namespace.TypeName2", ...) -let disposableTypesToLookAfter = disposableTypes - - -// -> You can refine this code query by assigning to methodsToLookAfter something like: -// Application.Assemblies.WithNameLike("Asm").ChildMethods() -let methodsToLookAfter = Application.Methods - - -// Enumerate methods that create any disposable type, without calling Dispose() -from m in methodsToLookAfter.ThatCreateAny(disposableTypesToLookAfter ) - -where !m.MethodsCalled.Intersect(disposeMethods).Any() -select new { - m, - disposableObjectsCreated = disposableTypes.Where(t => m.CreateA(t)), - m.MethodsCalled, - //Debt = 10.ToMinutes().ToDebt(), - //Severity = Severity.Low -} - -// -// This code query enumerates methods that create one or several disposable object(s), -// without calling any Dispose() method. -// -// This code query is not a code rule because it is acceptable to do so, -// as long as disposable objects are disposed somewhere else. -// -// This code query is designed to be be easily refactored -// to look after only specific disposable types, or specific caller methods. -// -// You can then refactor this code query to adapt it to your needs and transform it into a code rule. -//]]> - Classes that are candidate to be turned into structures -// ND1304:ClassesThatAreCandidateToBeTurnedIntoStructures - -warnif count > 0 -from t in JustMyCode.Types where - t.IsClass && - !t.IsGeneratedByCompiler && - !t.IsStatic && - !t.IsGeneric && - - t.NbChildren == 0 && // Must not have children - - t.Constructors.All(c => c.NbParameters > 0) && // Must not have parameterless ctor, struct cannot have custom parameterless ctor - - t.IsImmutable && // Structures should be immutable type. - - // Must have fields and all fields must be of value-type - t.InstanceFields.Count() > 0 && - t.InstanceFields.All(f => f.FieldType != null) && - t.InstanceFields.All(f => f.FieldType.IsStructure || f.FieldType.IsEnumeration) && - - // Must not implement interfaces to avoid boxing mismatch - // when structures implements interfaces. - t.InterfacesImplemented.Count() == 0 && - - // Must derive directly from System.Object - t.DepthOfDeriveFrom("System.Object".AllowNoMatch()) == 1 && - - // Must not be a serializable class because a structure should be immutable - // and serialized types are mutable. - !t.TypesUsed.Any(t1 => t1.IsAttributeClass && t1.ParentNamespace.Name == "Newtonsoft.Json") && - !t.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) && - !t.IsUsing("System.Xml.Serialization.XmlElementAttribute".AllowNoMatch()) && - !t.IsUsing("System.Xml.Serialization.XmlAttributeAttribute".AllowNoMatch()) && - !t.HasAttribute("System.Runtime.Serialization.DataContractAttribute".AllowNoMatch()) && - !t.IsUsing("System.Runtime.Serialization.DataMemberAttribute".AllowNoMatch()) && - - // ASP.NET Core ViewModel and Repository - !t.SimpleName.EndsWithAny("Model","Repository") && - !t.IsUsing("System.ComponentModel.DataAnnotations".AllowNoMatch().MatchNamespace()) && - - // Must not be an entry point class like 'Program' - !t.Methods.Any(m => m.IsEntryPoint) - - // && t.IsSealed <-- You might want to add this condition - // to restraint the set. - // && !t.IsPubliclyVisible <-- You might want to add this condition if - // you are developping a framework with classes - // that are intended to be sub-classed by - // your clients. - let methodsUser = t.TypesUsingMe.ChildMethods().Where(m => m.IsUsing(t)) - -select new { - t, - t.SizeOfInst, - t.InstanceFields, - methodsUser, - Debt = (5 + 1*methodsUser.Count()).ToMinutes().ToDebt(), - Severity = Severity.Info -} - -// -// *Int32*, *Double*, *Char* or *Boolean* are structures and not classes. -// Structures are particularly suited to implement **lightweight values**. -// Hence a class is candidate to be turned into a structure -// when its instances are *lightweight values*. -// -// This is a matter of *performance*. It is expected that a program -// works with plenty of *short lived lightweight values*. -// In such situation, the advantage of using *struct* instead of -// *class*, (in other words, the advantage of using *values* instead -// of *objects*), is that *values* are not managed by the garbage collector. -// This means that values are cheaper to deal with. -// -// This rule matches classes that looks like being *lightweight values*. -// The characterization of such class is: -// -// • It has instance fields. -// -// • All instance fields are typed with value-types (primitive, structure or enumeration) -// -// • It is immutable (the value of its instance fields cannot be modified once the constructor ended). -// -// • It implements no interfaces. -// -// • It has no parameterless construtor. -// -// • It is not generic. -// -// • It has no derived classes. -// -// • It derives directly from *System.Object*. -// -// • ASP.NETCore ViewModel (its name ends with *Model*) and Repository -// -// This rule doesn't take account if instances of matched -// classes are numerous *short-lived* objects. -// These criterions are just indications. Only you can decide if it is -// *performance wise* to transform a class into a structure. -// -// A related case-study of using *class* or *struct* for *Tuple<…>* generic -// types can be found here: -// http://stackoverflow.com/questions/2410710/why-is-the-new-tuple-type-in-net-4-0-a-reference-type-class-and-not-a-value-t -// - -// -// Just use the keyword *struct* instead of the keyword *class*. -// -// **CAUTION:** Before applying this rule, make sure to understand -// the **deep implications** of transforming a class into a structure. -// http://msdn.microsoft.com/en-us/library/aa664471(v=vs.71).aspx -// -// The estimated Debt, which means the effort to fix such issue, -// is 5 minutes per class matched plus one minute per method -// using such class transformed into a structure. -//]]> - Avoid namespaces with few types -// ND1305:AvoidNamespacesWithFewTypes - -warnif count > 0 - -// Common infrastructure namespace names not matched by the rule. -// Complete this list to your need. -let infraNamespaceNames = new HashSet() { - "Services","Exceptions","Logging", "Domain", - "Identity", "Migrations", "Controllers", - "Specifications", "Interfaces", "Components", - "Bus", "Models", "EventHandlers", "Mappings", - "ViewModels", "ViewComponents", "Notifications", - "Configurations", "Extensions", "Events", - "Context", "Data", "EventSourcing", "Repository", - "IoC", "Manage", "Commands", "CommandHandlers", - "Validations", "EventStoreSQL", "Authorization", - "Formatters", "Xml", "Json", "Enums", - "Abstractions", "BaseClasses", "Settings", - "Helpers", "Validators", "Entities" -} - -from n in JustMyCode.Namespaces -where n.Name.Length > 0 // Don't match anonymous namespaces - && !infraNamespaceNames.Contains(n.SimpleName) // Don't match common infrastructure namespaces - && n.Name != n.ParentAssembly.Name // Don't warn on namespace named as assembly to avoid warning on new VS projects -let types = n.ChildTypes.Where(t => !t.IsGeneratedByCompiler).ToArray() -where - types.Length > 0 && // Don't match namespaces that contain only types GeneratedByCompiler - types.Length < 5 && - // Only match namespaces that have all types in JustMyCode - types.All(t => JustMyCode.Contains(t)) - orderby types.Length ascending -select new { - n, - types, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// This rule warns about namespaces other than the global namespace -// that contain less than five types. -// -// Make sure that each of your namespaces has a logical organization -// and that a valid reason exists to put types in a sparsely -// populated namespace. -// -// Namespaces should contain types that are used together in most -// scenarios. When their applications are mutually exclusive, -// types should be located in separate namespaces. For example, -// the *System.Web.UI* namespace contains types that are used -// in Web applications, and the *System.Windows.Forms* namespace -// contains types that are used in Windows-based applications. -// Even though both namespaces have types that control aspects -// of the user interface, these types are not designed for -// use in the same application. Therefore, they are located in -// separate namespaces. -// -// Careful namespace organization can also be helpful because -// it increases the discoverability of a feature. By examining the -// namespace hierarchy, library consumers should be able to locate -// the types that implement a feature. -// -// Notice that this rule source code contains a list of common -// infrastructure namespace names that you can complete. -// Namespaces with ending name component in this list are not matched. -// - -// -// To fix a violation of this rule, try to combine namespaces -// that contain just a few types into a single namespace. -// -]]> - Nested types should not be visible -// ND1306:NestedTypesShouldNotBeVisible - -warnif count > 0 from t in JustMyCode.Types where - t.IsNested && - !t.IsGeneratedByCompiler && - !t.IsPrivate -let typesUser = t.TypesUsingMe.Where(t1 => t1 != t.ParentType && t1.ParentType != t.ParentType) -select new { - t, - t.Visibility, - typesUser, - Debt = (2 + 4*typesUser.Count()).ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns about nested types not declared as private. -// -// A nested type is a type declared within the scope of another -// type. Nested types are useful for encapsulating private -// implementation details of the containing type. Used -// for this purpose, nested types should not be externally visible. -// -// Do not use externally visible nested types for logical -// grouping or to avoid name collisions; instead use namespaces. -// -// Nested types include the notion of member accessibility, -// which some programmers do not understand clearly. -// -// Protected types can be used in subclasses and nested types -// in advanced customization scenarios. -// - -// -// If you do not intend the nested type to be externally visible, -// change the type's accessibility. -// -// Otherwise, remove the nested type from its parent and make it -// *non-nested*. -// -// If the purpose of the nesting is to group some nested types, -// use a namespace to create the hierarchy instead. -// -// The estimated Debt, which means the effort to fix such issue, -// is 2 minutes per nested type plus 4 minutes per outter type -// using such nesting type. -//]]> - Declare types in namespaces -// ND1307:DeclareTypesInNamespaces - -warnif count > 0 from n in Application.Namespaces where - // If an anonymous namespace can be found, - // it means that it contains types outside of namespaces. - n.Name == "" - - // Eliminate anonymous namespaces that contains - // only notmycode types (like generated types). - let childTypes = n.ChildTypes.Where(t => JustMyCode.Contains(t)) - where childTypes.Count() > 0 -select new { - n, - childTypes, - n.NbLinesOfCode, - Debt = 2*childTypes.Count().ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// Types are declared within namespaces to prevent name collisions, -// and as a way of organizing related types in an object hierarchy. -// -// Types outside any named namespace are in a *global -// namespace* that cannot be referenced in code. -// -// The *global namespace* has no name, hence it is qualified as -// being the *anonymous namespace*. -// -// This rule warns about *anonymous namespaces*. -// - -// -// To fix a violation of this rule, -// declare all types of all anonymous -// namespaces in some named namespaces. -//]]> - Empty static constructor can be discarded -// ND1308:EmptyStaticConstructorCanBeDiscarded - -warnif count > 0 from m in JustMyCode.Methods where - m.IsClassConstructor && - m.NbLinesOfCode == 0 -select new { - m, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// The *class constructor* (also called *static constructor*, and named *cctor* in IL code) -// of a type, if any, is executed by the CLR at runtime, just before the first time the type is used. -// -// This rule warns about the declarations of *static constructors* -// that don't contain any lines of code. Such *cctors* are useless -// and can be safely removed. -// - -// -// Remove matched empty *static constructors*. -//]]> - Instances size shouldn't be too big -// ND1309:InstancesSizeShouldntBeTooBig - -warnif count > 0 from t in JustMyCode.Types where - t.SizeOfInst > 128 && - - // You might want to restrict this rule only on structure, since the cost of copying instance data at each method call might be prohibitive - // t.IsStructure && - - // Discard types that represent WPF, WindowsForm and ASP.NET forms and controls and EntityFramwork classes. - t.BaseClasses.All(bc => !bc.ParentNamespace.Name.StartsWithAny( - "System.Windows", "System.Web.UI", "System.Web", "Microsoft.EntityFramework", - // Discard types related to these namespaces that typically require large instances size - "System.ComponentModel", "System.Xml", - // Just add more component vendors here if needed - "DevExpress")) - - orderby t.SizeOfInst descending -select new { - t, - t.SizeOfInst, - t.InstanceFields, - t.BaseClasses, - Debt = t.SizeOfInst.Linear(128,10, 2048, 120).ToMinutes().ToDebt(), - - // The annual interest varies linearly from interests for severity Medium for 64 bytes per instance - // to twice interests for severity High for 2048 bytes per instance - AnnualInterest = (t.SizeOfInst.Linear(128, Severity.Medium.AnnualInterestThreshold().Value.TotalMinutes, - 2048, 2*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)) - )*(t.IsStructure ? 10 : 1) // Multiply interest by 10 for structures - .ToMinutes().ToAnnualInterest() - -} - -// -// Types where *SizeOfInst > 128* might degrade performance -// if many instances are created at runtime. -// They can also be hard to maintain. -// -// Notice that a class with a large *SizeOfInst* value -// doesn't necessarily have a lot of instance fields. -// It might derive from a class with a large *SizeOfInst* value. -// -// This query doesn't match types that represent WPF -// and WindowsForm forms and controls nor Entity Framework -// special classes. -// -// Some other namespaces like *System.ComponentModel* or *System.Xml* -// have base classes that typically imply large instances size -// so this rule doesn't match these situations. -// -// This rule doesn't match custom *DevExpress* component -// and it is easy to modify this rule ro append other component vendors -// to avoid false positives. -// -// See the definition of the *SizeOfInst* metric here -// https://www.ndepend.com/docs/code-metrics#SizeOfInst -// - -// -// A type with a large *SizeOfInst* value hold *directly* -// a lot of data. Typically, you can group this data into -// smaller types that can then be composed. -// -// The estimated Debt, which means the effort to fix such issue, -// varies linearly from severity **Medium** for 128 bytes per instance -// to twice interests for severity **High** for 2048 bytes per instance. -// -// The estimated annual interest of issues of this rule is 10 times higher -// for structures, because large structures have a significant performance cost. -// Indeed, each time such structure *value* is passed as a method parameter -// it gets copied to a new local variable -// (note that the word *value* is more appropriate than the word *instance* for structures). -// For this reason, such structure should be declared as class. -//]]> - Attribute classes should be sealed -// ND1310:AttributeClassesShouldBeSealed - -warnif count > 0 from t in Application.Types where - t.IsAttributeClass && - !t.IsSealed && - !t.IsAbstract && - t.IsPublic -select new { - t, - t.NbLinesOfCode, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// The .NET Framework class library provides methods -// for retrieving custom attributes. By default, -// these methods search the attribute inheritance -// hierarchy; for example -// *System.Attribute.GetCustomAttribute()* -// searches for the specified attribute type, or any -// attribute type that extends the specified attribute -// type. -// -// Sealing the attribute eliminates the search -// through the inheritance hierarchy, and can improve -// performance. -// - -// -// To fix a violation of this rule, seal the attribute -// type or make it abstract. -//]]> - Don't use obsolete types, methods or fields -// ND1311:DontUseObsoleteTypesMethodsOrFields - -warnif count > 0 -let obsoleteTypes = Types.Where(t => t.IsObsolete) -let obsoleteMethods = Methods.Where(m => m.IsObsolete).ToHashSetEx() -let obsoleteFields = Fields.Where(f => f.IsObsolete) - -from m in JustMyCode.Methods.UsingAny(obsoleteTypes).Union( - JustMyCode.Methods.UsingAny(obsoleteMethods)).Union( - JustMyCode.Methods.UsingAny(obsoleteFields)) -let obsoleteTypesUsed = obsoleteTypes.UsedBy(m) - -// Optimization: MethodsCalled + Intersect() is faster than using obsoleteMethods.UsedBy() -let obsoleteMethodsUsed = m.MethodsCalled.Intersect(obsoleteMethods) -let obsoleteFieldsUsed = obsoleteFields.UsedBy(m) - -let obsoleteUsage = obsoleteTypesUsed.Cast().Concat(obsoleteMethodsUsed).Concat(obsoleteFieldsUsed) - -select new { - m, - obsoleteUsage, - Debt = (5*obsoleteUsage.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// The attribute *System.ObsoleteAttribute* is used to tag -// types, methods or fields of an API that clients shouldn't -// use because these code elements will be removed sooner -// or later. -// -// This rule warns about methods that use a type, a method -// or a field, tagged with *System.ObsoleteAttribute*. -// - -// -// Typically when a code element is tagged with -// *System.ObsoleteAttribute*, a *workaround message* -// is provided to clients. -// -// This *workaround message* will tell you what to do -// to avoid using the obsolete code element. -// -// The estimated Debt, which means the effort to fix such issue, -// is 5 minutes per type, method or field used. -// -// Issues of this rule have a severity **High** -// because it is important to not rely anymore on obsolete code. -//]]> - Do implement methods that throw NotImplementedException -// ND1312:DoImplementMethodsThatThrowNotImplementedException - -warnif count > 0 -from m in Application.Methods -where m.CreateA("System.NotImplementedException".AllowNoMatch()) -select new { - m, - m.NbLinesOfCode, - Debt = (m.NbLinesOfCode == 1 ? 10 : 3).ToMinutes().ToDebt(), - Severity = m.NbLinesOfCode == 1 ? Severity.High : Severity.Medium -} - -// -// The exception *NotImplementedException* is used to declare -// a method *stub* that can be invoked, and defer the -// development of the method implementation. -// -// This exception is especially useful when doing **TDD** -// (*Test Driven Development*) when tests are written first. -// This way tests fail until the implementation is written. -// -// Hence using *NotImplementedException* is a *temporary* -// facility, and before releasing, will come a time when -// this exception shouldn't be used anywhere in code. -// -// *NotImplementedException* should not be used permanently -// to mean something like *this method should be overriden* -// or *this implementation doesn't support this facility*. -// Artefact like *abstract method* or *abstract class* should -// be used instead, to favor a *compile time* error over a -// *run-time* error. -// -// This rule warns about method still using -// *NotImplementedException*. -// - -// -// Investigate why *NotImplementedException* is still -// thrown. -// -// Such issue has a **High** severity if the method code -// consists only in throwing *NotImplementedException*. -// Such situation means either that the method should be -// implemented, either that what should be a *compile time* -// error is a *run-time* error *by-design*, -// and this is not good design. Sometime this situation -// also pinpoints a method stub that can be safely removed. -// -// If *NotImplementedException* is thrown from a method -// with significant logic, the severity is considered as -// **Medium**, because often the fix consists in throwing -// another exception type, like **InvalidOperationException**. -//]]> - Override equals and operator equals on value types -// ND1313:OverrideEqualsAndOperatorEqualsOnValueTypes - -warnif count > 0 -from t in JustMyCode.Types where - t.IsStructure && - t.InstanceFields.Count() > 0 -let equalsMethod = t.InstanceMethods.Where(m0 => m0.Name == "Equals(Object)").SingleOrDefault() -where equalsMethod == null -select new { - t, - t.InstanceFields, - Debt = (15 + 2*t.InstanceFields.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// For value types, the inherited implementation of *Equals()* uses -// the Reflection library, and compares the contents of all instances -// fields. Reflection is computationally expensive, and comparing -// every field for equality might be unnecessary. -// -// If you expect users to compare or sort instances, or use them -// as hash table keys, your value type should implement *Equals()*. -// In C# and VB.NET, you should also provide an implementation of -// *GetHashCode()* and of the equality and inequality operators. -// - -// -// To fix a violation of this rule, provide an implementation -// of *Equals()* and *GetHashCode()* and implement the equality -// and inequality operators. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 15 minutes plus 2 minutes per instance field. -//]]> - Boxing/unboxing should be avoided -// ND1314:BoxingUnboxingShouldBeAvoided -warnif count > 0 from m in JustMyCode.Methods where - m.IsUsingBoxing || - m.IsUsingUnboxing -select new { - m, - m.NbLinesOfCode, - m.IsUsingBoxing, - m.IsUsingUnboxing, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// *Boxing* is the process of converting a value type to the type -// *object* or to any interface type implemented by this value -// type. When the CLR boxes a value type, it wraps the value -// inside a *System.Object* and stores it on the managed heap. -// -// *Unboxing* extracts the value type from the object. Boxing -// is implicit; unboxing is explicit. -// -// The concept of boxing and unboxing underlies the C# unified -// view of the type system in which a value of any type can -// be treated as an object. More about *boxing* and *unboxing* -// here: https://msdn.microsoft.com/en-us/library/yz2be5wk.aspx -// -// **This rule has been disabled by default** to avoid noise in -// issues found by the NDepend default rule set. If boxing/unboxing -// is important to your team, just re-activate this rule. -// - -// -// Thanks to .NET generic, and especially thanks to -// generic collections, *boxing* and *unboxing* should -// be rarely used. Hence in most situations the code can -// be refactored to avoid relying on *boxing* and *unboxing*. -// See for example: -// http://stackoverflow.com/questions/4403055/boxing-unboxing-and-generics -// -// With a performance profiler, indentify methods that consume -// a lot of CPU time. If such method uses *boxing* or -// *unboxing*, especially in a **loop**, make sure to refactor it. -// -// By default issues of this rule have a **Low** severity -// because they reflect more an advice than a problem. -//]]> - - - Avoid namespaces mutually dependent -// ND1400:AvoidNamespacesMutuallyDependent - -warnif count > 0 - -// Optimization: restraint application assemblies set -// If some namespaces are mutually dependent -// - They must be declared in the same assembly -// - The parent assembly must ContainsNamespaceDependencyCycle -from assembly in Application.Assemblies.Where(a => a.ContainsNamespaceDependencyCycle != null && a.ContainsNamespaceDependencyCycle.Value) - -// hashset is used to avoid reporting both A <-> B and B <-> A -let hashset = new HashSet() - -// Optimization: restraint namespaces set -let namespacesSuspect = assembly.ChildNamespaces.Where( - // If a namespace doesn't have a Level value, it must be in a dependency cycle - // or it must be using directly or indirectly a dependency cycle. - n => n.Level == null && - // Also require the namespace to be JustMyCode to avoid generated namespaces like My / My.Resources in VB.NET - JustMyCode.Contains(n)) - -from nA in namespacesSuspect - -// Select namespaces mutually dependent with nA -let unused = hashset.Add(nA) // Populate hashset -let namespacesMutuallyDependentWith_nA = nA.NamespacesUsed.Using(nA) - .Except(hashset) // <-- avoid reporting both A <-> B and B <-> A -where namespacesMutuallyDependentWith_nA.Count() > 0 - -from nB in namespacesMutuallyDependentWith_nA - -// nA and nB are mutually dependent -// Infer which one is low level and which one is high level, -// for that we need to compute the coupling from A to B -// and from B to A in terms of number of types and methods -// usages involved in the coupling. -let typesOfBUsedByA = nB.ChildTypes.UsedBy(nA).ToArray() // Enumerate once -let couplingA2B = - typesOfBUsedByA.Sum(t => t.TypesUsingMe.Count(t1 => t1.ParentNamespace == nA)) + - typesOfBUsedByA.ChildMethods().Sum(m => m.MethodsCallingMe.Count(m1 => m1.ParentNamespace == nA)) - -let typesOfAUsedByB = nA.ChildTypes.UsedBy(nB).ToArray() // Enumerate once -let couplingB2A = - typesOfAUsedByB.Sum(t => t.TypesUsingMe.Count(t1 => t1.ParentNamespace == nB)) + - typesOfAUsedByB.ChildMethods().Sum(m => m.MethodsCallingMe.Count(m1 => m1.ParentNamespace == nB)) - -// The lowLevelNamespace is inferred from the fact that -// [coupling lowLevel -> highLevel] is lower than [coupling highLevel -> lowLevel] -let lowLevelNamespace = (couplingA2B < couplingB2A) ? nA : nB -let highLevelNamespace = (lowLevelNamespace == nA) ? nB : nA - -let highLevelTypesUsed = (lowLevelNamespace == nA) ? typesOfBUsedByA : typesOfAUsedByB -let lowLevelTypesUser = lowLevelNamespace.ChildTypes.UsingAny(highLevelTypesUsed) - -let lowLevelTypesMethodsUser = lowLevelTypesUser.Cast() - .Concat(lowLevelTypesUser.ChildMethods().Using(highLevelNamespace)) - .ToArray() // Enumerate once - -// Make the rule works also when lines of code is not available -let lowLevelNamespaceLoc = lowLevelNamespace.NbLinesOfCode ?? (lowLevelNamespace.NbILInstructions / 7) -let highLevelNamespaceLoc = highLevelNamespace.NbLinesOfCode ?? (highLevelNamespace.NbILInstructions / 7) - -let annualInterestPerIssue = - // Such issue has at least a Severity.Medium, never a Severity.Low - Math.Max(Severity.Medium.AnnualInterestThreshold().Value.TotalSeconds, - ((3600 + lowLevelNamespaceLoc + highLevelNamespaceLoc) / Math.Max(1,lowLevelTypesMethodsUser.Length)).Value) - .ToSeconds().ToAnnualInterest() - -// Select in details types and methods involved in the coupling lowLevelNamespace using highLevelNamespace -from tmCulprit in lowLevelTypesMethodsUser -let used = (tmCulprit.IsType ? tmCulprit.AsType.TypesUsed.Where(t => t.ParentNamespace == highLevelNamespace) : - tmCulprit.AsMethod.MembersUsed.Where(m => m.ParentNamespace == highLevelNamespace)) - .ToArray() // Enumerate once - -select new { - tmCulprit, - shouldntUse = used, - becauseNamespace = lowLevelNamespace, - shouldntUseNamespace = highLevelNamespace, - Debt = used.Length.Linear(1, 15, 10, 60).ToMinutes().ToDebt(), - AnnualInterest = annualInterestPerIssue - -} -// -// This rule lists types and methods from a low-level namespace -// that use types and methods from higher-level namespace. -// -// The pair of low and high level namespaces is made of two -// namespaces that use each other. -// -// For each pair of namespaces, to infer which one is low-level -// and which one is high-level, the rule computes the two coupling -// [from A to B] and [from B to A] in terms of number of types, -// methods and fields involved in the coupling. Typically -// the coupling from low-level to high-level namespace is significantly -// lower than the other legitimate coupling. -// -// Following this rule is useful to avoid **namespaces dependency -// cycles**. This will get the code architecture close to a -// *layered architecture*, where *low-level* code is not allowed -// to use *high-level* code. -// -// In other words, abiding by this rule will help significantly -// getting rid of what is often called **spaghetti code: -// Entangled code that is not properly layered and structured**. -// -// More on this in our white books relative to partitioning code. -// https://www.ndepend.com/docs/white-books -// - -// -// Refactor the code to make sure that **the low-level namespace -// doesn't use the high-level namespace**. -// -// The rule lists in detail which low-level types and methods -// shouldn't use which high-level types and methods. The refactoring -// patterns that help getting rid of each listed dependency include: -// -// • Moving one or several types from the *low-level* namespaces -// to the *high-level* one, or do the opposite. -// -// • Use *Inversion of Control (IoC)*: -// http://en.wikipedia.org/wiki/Inversion_of_control -// This consists in creating new interfaces in the -// *low-level* namespace, implemented by classes -// in the *high-level* namespace. This way *low-level* -// code can consume *high-level* code through interfaces, -// without using directly *high-level* implementations. -// Interfaces can be passed to *low-level* code through -// the *high-level* namespace code, or through even -// higher-level code. In related documentations -// you can see these interfaces named as *callbacks*, -// and the overall pattern is also known as -// *Dependency Injection (DI)*: -// http://en.wikipedia.org/wiki/Dependency_injection -// -// That rule might not be applicable for frameworks -// that present public namespaces mutually dependent. -// In such situation the cost to break the API can be -// higher than the cost to let the code entangled. -// -// - -// -// The estimated **Debt**, which means the effort to fix such issue -// to make sure that the first namespace doesn't rely anymore -// on the second one, depends on the number of types and methods used. -// -// Because both namespace are now forming a *super-component* -// that cannot be partitioned in smaller components, the cost to -// unfix each issue is proportional to the size of this super-component. -// As a consequence, the estimated **Annual Interest**, which means -// the annual cost to let both namespaces mutually dependend, is equal -// to an hour plus a number of minutes proportional to the size -// (in lines of code) of both namespaces. The obtained *Annual Interest* -// value is then divided by the number of detailled issues listed. -// -// Often the estimated *Annual Interest* for each listed issue -// is higher than the *Debt*, which means that leaving such issue -// unfixed for a year costs more than taking the time to fix issue once. -// -// -- -// -// To explore the coupling between the two namespaces mutually -// dependent: -// -// 1) from the *becauseNamespace right-click menu* choose -// *Copy to Matrix Columns* to export this low-level namespace -// to the horizontal header of the dependency matrix. -// -// 2) from the *shouldntUseNamespace right-click menu* choose -// *Copy to Matrix Rows* to export this high-level namespace to -// the vertical header of the dependency matrix. -// -// 3) double-click the black matrix cell (it is black because of -// the mutual dependency). -// -// 4) in the matrix command bar, click the button: -// *Remove empty Row(s) and Column(s)*. -// -// At this point, the dependency matrix shows types involved -// into the coupling. -// -// • Blue cells represent types from low-level namespace using types -// from high-level namespace -// -// • Green cells represent types from high-level namespace using -// types from low-level namespace -// -// • Black cells represent types from low-level and high-level -// namespaces that use each other. -// -// There are more green cells than blue and black cells because -// green cell represents correct coupling from high-level to low-level. -// **The goal is to eliminate incorrect dependencies represented by -// blue and black cells.** -// - ]]> - Avoid namespaces dependency cycles -// ND1401:AvoidNamespacesDependencyCycles - -warnif count > 0 - -// Optimization: restraint application assemblies set -// If some namespaces are mutually dependent -// - They must be declared in the same assembly -// - The parent assembly must ContainsNamespaceDependencyCycle -from assembly in Application.Assemblies - .Where(a => a.ContainsNamespaceDependencyCycle != null && - a.ContainsNamespaceDependencyCycle.Value) - -// Optimization: restraint namespaces set -let namespacesSuspect = assembly.ChildNamespaces.Where(n => - // A namespace involved in a cycle necessarily have a null Level. - n.Level == null && - // Also require the namespace to be JustMyCode to avoid generated namespaces like My / My.Resources in VB.NET - JustMyCode.Contains(n)) - -// hashset is used to avoid iterating again on namespaces already caught in a cycle. -let hashset = new HashSet() - - -from suspect in namespacesSuspect - // By commenting in this line, the query matches all namespaces involved in a cycle. - where !hashset.Contains(suspect) - - // Define 2 code metrics - // • Namespaces depth of is using indirectly the suspect namespace. - // • Namespaces depth of is used by the suspect namespace indirectly. - // Note: for direct usage the depth is equal to 1. - let namespacesUserDepth = namespacesSuspect.DepthOfIsUsing(suspect) - let namespacesUsedDepth = namespacesSuspect.DepthOfIsUsedBy(suspect) - - // Select namespaces that are both using and used by namespaceSuspect - let usersAndUsed = from n in namespacesSuspect where - namespacesUserDepth[n] > 0 && - namespacesUsedDepth[n] > 0 - select n - - where usersAndUsed.Count() > 0 - - // Here we've found namespace(s) both using and used by the suspect namespace. - // A cycle involving the suspect namespace is found! - // v2017.3.2: don't call Append() as an extension method else ambiguous syntax error - // with the new extension method in .NET Fx v4.7.1 / .NET Standard 2.0: System.Linq.Enumerable.Append() - let cycle = ExtensionMethodsEnumerable.Append(usersAndUsed,suspect) - - // Fill hashset with namespaces in the cycle. - // .ToArray() is needed to force the iterating process. - let unused1 = (from n in cycle let unused2 = hashset.Add(n) select n).ToArray() - -select new { - suspect, - cycle, - Debt = 120.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule lists all *application namespace dependency cycles*. -// Each row shows a different cycle, indexed with one of the namespace entangled -// in the cycle. -// -// To browse a cycle on the dependency graph or the dependency matrix, right click -// a cycle cell and export the matched namespaces to the dependency graph or matrix. -// -// In the matrix, dependency cycles are represented with red squares and black cells. -// To easily browse dependency cycles, the dependency matrix comes with an option: -// *Display Direct and Indirect Dependencies* -// -// Read our white books relative to partitioning code, -// to know more about namespaces dependency cycles, and why avoiding them -// is a *simple yet efficient* solution to clean the architecture of a code base. -// https://www.ndepend.com/docs/white-books -// - -// -// Removing first pairs of *mutually dependent namespaces* will eliminate -// most *namespaces dependency cycles*. This is why it is recommended -// focusing on matches of the default rule -// **Avoid namespaces mutually dependent** before dealing -// with the present rule. -// -// Once solving all *mutually dependent namespaces*, remaining cycles -// matched by the present rule necessarily involve 3 or more namespaces -// like in: *A is using B is using C is using A*. -// Such cycle can be broken by identifying which namespace should -// be at the *lower-level*. For example if B should be at the -// *lower-level*, then it means C should be at the *higher-level* -// and to break the cycle, you just have to remove the dependency -// from B to C, with a pattern described in the *HowToFix* section -// of the rule *Avoid namespaces mutually dependent*. -// -// The estimated Debt, which means the effort to fix such issue, -// doesn't depend on the cycle length. First because fixing the rule -// **Avoid namespaces mutually dependent** will fix most cycle reported -// here, second because even a long cycle can be broken by removing -// a few dependency. -//]]> - Avoid partitioning the code base through many small library Assemblies -// ND1402:AvoidPartitioningTheCodeBaseThroughManySmallLibraryAssemblies - -warnif count > 10 -from a in Application.Assemblies where - ( a.NbLinesOfCode < 1000 || - a.NbILInstructions < 7000 ) && - a.FilePath.FileExtension.ToLower() == ".dll" -select new { - a, - a.NbLinesOfCode, - a.NbILInstructions, - Debt = 40.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// Each .NET Assembly compiled represents one or several physical file(s). -// Having too many library .NET Assemblies is a symptom of -// considering **physical** .NET Assemblies as **logical** components. -// -// We advise having less, and bigger, .NET Assemblies -// and using the concept of namespaces to define logical components. -// Benefits are: -// -// • Faster compilation time. -// -// • Faster startup time for your program. -// -// • Easier deployment thanks to less files to manage. -// -// • If you are developing a Framework, -// less .NET assemblies to reference and manage for your clients. -// - -// -// Consider using the *physical* concept of assemblies for physical needs -// only. -// -// Our white book about **Partitioning code base through .NET assemblies -// and Visual Studio projects** explains in details valid and invalid -// reasons to use assemblies. -// Download it here: -// https://www.ndepend.com/Res/NDependWhiteBook_Assembly.pdf -//]]> - UI layer shouldn't use directly DB types -// ND1403:UILayerShouldntUseDirectlyDBTypes - -warnif count > 0 - -// UI layer is made of types using a UI framework -let uiTypes = Application.Types.UsingAny(Assemblies.WithNameIn("PresentationFramework", "System.Windows", "System.Windows.Forms", "System.Web")) - -// You can easily customize this part to define what are DB types. -let dbTypes = ThirdParty.Assemblies.WithNameIn("System.Data", "EntityFramework", "NHibernate").ChildTypes() - // Ideally even DataSet and associated, usage should be forbidden from UI layer: - // http://stackoverflow.com/questions/1708690/is-list-better-than-dataset-for-ui-layer-in-asp-net - .Except(ThirdParty.Types.WithNameIn("DataSet", "DataTable", "DataRow")) - -from uiType in uiTypes.UsingAny(dbTypes) -let dbTypesUsed = dbTypes.Intersect(uiType.TypesUsed) - -let dbTypesAndMembersUsed = dbTypesUsed.Union(dbTypesUsed.ChildMembers().UsedBy(uiType)) - -// Per defaut this rule estimates a technical debt -// proportional to the coupling between the UI and DB types. -let couplingPerUIType = 2 + - uiType.Methods.UsingAny(dbTypesUsed).Count() + - dbTypesAndMembersUsed.Count() - -select new { - uiType, - dbTypesAndMembersUsed, - Debt = (4 * couplingPerUIType).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is more a *sample rule to adapt to your need*, -// than a rigid rule you should abide by. It shows how to -// define code layers in a rule and how to be warned about -// layers dependencies violations. -// -// This rule first defines the UI layer and the DB framework -// layer. Second it checks if any UI layer type is using -// directly any DB framework layer type. -// -// • The **DB framework layer** is defined as the set of *third-party* -// types in the framework *ADO.NET*, *EntityFramework*, -// *NHibernate* types, that the application is consuming. -// It is easy to append and suppress any DB framework. -// -// • The UI layer (**User Interface Layer**) is defined as the -// set of types that use *WPF*, *Windows Form*, *ASP.NET*. -// -// *UI using directly DB frameworks* is generally considered -// as *poor design* because DB frameworks accesses should be -// a concept hidden to UI, encapsulated into a **dedicated -// Data Access Layer (DAL)**. -// -// Notice that per defaut this rule estimates a technical debt -// proportional to the coupling between the UI and DB types. -// - -// -// This rule lists precisely which UI type uses which -// DB framework type. Instead of fixing matches one by one, -// first imagine how DB framework accesses could be -// encapsulated into a dedicated layer. -// -]]> - UI layer shouldn't use directly DAL layer -// ND1404:UILayerShouldntUseDirectlyDALLayer - -warnif count > 0 - -// UI layer is made of types using a UI framework -let uiTypes = Application.Types.UsingAny(Assemblies.WithNameIn("PresentationFramework", "System.Windows", "System.Windows.Forms", "System.Web")) - -// Exclude commonly used DataSet and associated, from ADO.Net types -// You can easily customize this part to define what are DB types. -let dbTypes = ThirdParty.Assemblies.WithNameIn("System.Data", "EntityFramework", "NHibernate").ChildTypes() - .Except(ThirdParty.Types.WithNameIn("DataSet", "DataTable", "DataRow")) - -// DAL layer is made of types using a DB framework -// .ToHashSetEx() results to faster execution of dalTypes.Intersect(uiType.TypesIUse). -let dalTypes = Application.Types.UsingAny(dbTypes).ToHashSetEx() - -from uiType in uiTypes.UsingAny(dalTypes) -let dalTypesUsed = dalTypes.Intersect(uiType.TypesUsed) - -let dalTypesAndMembersUsed = dalTypesUsed.Union(dalTypesUsed.ChildMembers().UsedBy(uiType)) - -// Per defaut this rule estimates a technical debt -// proportional to the coupling between the UI with the DAL layer. -let couplingPerUIType = 2 + - uiType.Methods.UsingAny(dalTypesUsed).Count() + - dalTypesAndMembersUsed.Count() - -select new { - uiType, - // if dalTypesUsed is empty, it means that the uiType is part of the DAL - dalTypesAndMembersUsed, - Debt = (4 * couplingPerUIType).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is more a *sample rule to adapt to your need*, -// than a rigid rule you should abide by. It shows how to -// define code layers in a rule and how to be warned about -// layers dependencies violations. -// -// This rule first defines the UI layer and the DAL layer. -// Second it checks if any UI layer type is using directly -// any DAL layer type. -// -// • The DB layer (the DAL, **Data Access Layer**) is defined as -// the set of types of the application that use *ADO.NET*, -// *EntityFramework*, *NHibernate* types. It is easy to append -// and suppress any DB framework. -// -// • The UI layer (**User Interface Layer**) is defined as the -// set of types that use *WPF*, *Windows Form*, *ASP.NET*. -// -// *UI using directly DAL* is generally considered as *poor -// design* because DAL accesses should be a concept -// hidden to UI, encapsulated into an **intermediary domain -// logic**. -// -// Notice that per defaut this rule estimates a technical debt -// proportional to the coupling between the UI with the DAL layer. -// - -// -// This rule lists precisely which UI type uses which DAL type. -// -// More about this particular design topic here: -// http://www.kenneth-truyers.net/2013/05/12/the-n-layer-myth-and-basic-dependency-injection/ -// -]]> - Assemblies with poor cohesion (RelationalCohesion) -// ND1405:AssembliesWithPoorRelationalCohesion - -warnif count > 0 from a in Application.Assemblies - -// Build the types list on which we want to check cohesion -// This is the assembly 'a' type, minus enumeration -// and types generated by the compiler. -let types = a.ChildTypes.Where( - t => !t.IsGeneratedByCompiler && - !t.IsEnumeration && - JustMyCode.Contains(t)) - // Absolutly need ToHashet() to have fast Intersect() calls below. - .ToHashSetEx() - -// Relational Cohesion metrics is relevant only if there are enough types -where types.LongCount()> 20 - -// R is the total number of relationship between types of the assemblies. -let R = types.Sum(t => t.TypesUsed.Intersect(types).Count()) - -// Relational Cohesion formula -let relationalCohesion = (double)R / types.Count -where - - (relationalCohesion < 1.5 || - relationalCohesion > 4.0) -select new { - a, - a.ChildTypes, - relationalCohesion, - a.RelationalCohesion, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// This rule computes the *Relational Cohesion* metric for -// the application assemblies, and warns about wrong values. -// -// The *Relational Cohesion* for an assembly, is the total number -// of relationship between types of the assemblies, divided -// by the number of types. In other words it is the average -// number of types in the assembly used by a type in the assembly. -// -// As classes inside an assembly should be strongly related, -// the cohesion should be high. On the other hand, a value -// which is too high may indicate over-coupling. A good range -// for *Relational Cohesion* is **1.5 to 4.0**. -// -// Notice that assemblies with less than 20 types are ignored. -// - -// -// Matches of this present rule might reveal either assemblies -// with specific coding constraints (like code generated that -// have particular structure) either issues in design. -// -// In the second case, large refactoring can be planned -// not to respect this rule in particular, but to increase -// the overall design and code maintainability. -// -// The severity of issues of this rule is **Low** because -// the code metric *Relational Cohesion* is an information -// about the code structure state but **is not actionable**, -// it doesn't tell precisely what to do obtain a better score. -// -// Fixing actionable issues of others **Architecture** and -// **Code Smells** default rules will necessarily increase -// the *Relational Cohesion* scores. -//]]> - Namespaces with poor cohesion (RelationalCohesion) -// ND1406:NamespacesWithPoorRelationalCohesion - -warnif count > 0 from n in Application.Namespaces - -// Build the types list on which we want to check cohesion -// This is the namespace children types, minus enumerations -// and types generated by the compiler. -let types = n.ChildTypes.Where( - t => JustMyCode.Contains(t) && !t.IsEnumeration) - // Absolutly need ToHashet() to have fast Intersect() calls below. - .ToHashSetEx() - -// Relational Cohesion metrics is relevant only if there are enough types -where types.LongCount() > 20 - -// R is the total number of relationship between types of the namespaces. -let R = types.Sum(t => t.TypesUsed.Intersect(types).Count()) - -// Relational Cohesion formula -let relationalCohesion = (double)R / types.Count -where - (relationalCohesion < 1.5 || - relationalCohesion > 4.0) -select new { - n, - n.ChildTypes, - relationalCohesion, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// This rule computes the *Relational Cohesion* metric for -// the application namespaces, and warns about wrong values. -// -// The *Relational Cohesion* for a namespace, is the total number -// of relationship between types of the namespaces, divided -// by the number of types. In other words it is the average -// number of types in the namespace used by a type in the namespace. -// -// As classes inside a namespace should be strongly related, -// the cohesion should be high. On the other hand, a value -// which is too high may indicate over-coupling. A good range -// for *Relational Cohesion* is **1.5 to 4.0**. -// -// Notice that namespaces with less than 20 types are ignored. -// - -// -// Matches of this present rule might reveal either namespaces -// with specific coding constraints (like code generated that -// have particular structure) either issues in design. -// -// In the second case, refactoring sessions can be planned -// to increase the overall design and code maintainability. -// -// You can get an overview of class coupling for a -// matched namespace by exporting the *ChildTypes* to the graph. -// (Right click the *ChildTypes* cells) -// -// The severity of issues of this rule is **Low** because -// the code metric *Relational Cohesion* is an information -// about the code structure state but **is not actionable**, -// it doesn't tell precisely what to do obtain a better score. -// -// Fixing actionable issues of others **Architecture** and -// **Code Smells** default rules will necessarily increase -// the *Relational Cohesion* scores. -//]]> - Assemblies that don't satisfy the Abstractness/Instability principle -// ND1407:AssembliesThatDontSatisfyTheAbstractnessInstabilityPrinciple - -warnif count > 0 from a in Application.Assemblies - where a.NormDistFromMainSeq > 0.7 - orderby a.NormDistFromMainSeq descending -select new { - a, - a.NormDistFromMainSeq, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// The **Abstractness versus Instability Diagram** that is shown in the NDepend -// report helps to assess which assemblies are **potentially painful to maintain** -// (i.e concrete and stable) and which assemblies are **potentially useless** -// (i.e abstract and instable). -// -// • **Abstractness**: If an assembly contains many abstract types -// (i.e interfaces and abstract classes) and few concrete types, -// it is considered as abstract. -// -// • **Stability**: An assembly is considered stable if its types -// are used by a lot of types from other assemblies. In this context -// stable means *painful to modify*. -// -// From these metrics, we define the *perpendicular normalized distance of -// an assembly from the idealized line* **A + I = 1** (called *main sequence*). -// This metric is an indicator of the assembly's balance between abstractness -// and stability. We precise that the word *normalized* means that the range -// of values is [0.0 … 1.0]. -// -// This rule warns about assemblies with a *normalized distance* greater than -// than 0.7. -// -// This rules use the default code metric on assembly -// *Normalized Distance from the Main Sequence* explained here: -// https://www.ndepend.com/docs/code-metrics#DitFromMainSeq -// -// These concepts have been originally introduced by *Robert C. Martin* -// in 1994 in this paper: http://www.objectmentor.com/resources/articles/oodmetrc.pdf -// - -// -// Violations of this rule indicate assemblies with an improper -// *abstractness / stability* balance. -// -// • Either the assembly is *potentially painful to maintain* (i.e is massively -// used and contains mostly concrete types). This can be fixed by creating -// abstractions to avoid too high coupling with concrete implementations. -// -// • Either the assembly is *potentially useless* (i.e contains mostly -// abstractions and is not used enough). In such situation, the design -// must be reviewed to see if it can be enhanced. -// -// The severity of issues of this rule is **Low** because -// the *Abstractness/Instability principle* is an information -// about the code structure state but **is not actionable**, -// it doesn't tell precisely what to do obtain a better score. -// -// Fixing actionable issues of others **Architecture** and -// **Code Smells** default rules will necessarily push -// the *Abstractness/Instability principle* scores in the -// right direction. -//]]> - Higher cohesion - lower coupling -// ND1408:HigherCohesionLowerCoupling - -// warnif count > 0 -let abstractNamespaces = JustMyCode.Namespaces.Where( - n => n.ChildTypes.Where(t => !t.IsInterface && !t.IsEnumeration && !t.IsDelegate).Count() == 0 -).ToHashSetEx() - -let concreteNamespaces = JustMyCode.Namespaces.Except(abstractNamespaces).ToHashSetEx() - -from n in concreteNamespaces -let namespacesUsed = n.NamespacesUsed.ExceptThirdParty() -let concreteNamespacesUsed = namespacesUsed.Except(abstractNamespaces) -let abstractNamespacesUsed = namespacesUsed.Except(concreteNamespaces) -orderby concreteNamespacesUsed.Count() descending -select new { - n, - concreteNamespacesUsed , - abstractNamespacesUsed, - // Debt = 50.ToMinutes().ToDebt(), - // Severity = Severity.High -} - -// -// It is deemed as a good software architecture practice to clearly separate -// *abstract* namespaces that contain only abstractions (interfaces, enumerations, delegates) -// from *concrete* namespaces, that contain classes and structures. -// -// Typically, the more concrete namespaces rely on abstract namespaces *only*, -// the more **Decoupled** is the architecture, and the more **Cohesive** are -// classes inside concrete namespaces. -// -// The present code query defines sets of abstract and concrete namespaces -// and show for each concrete namespaces, which concrete and abstract namespaces -// are used. -// - -// -// This query can be transformed into a code rule, depending if you wish to -// constraint your code structure *coupling / cohesion* ratio. -//]]> - Avoid mutually-dependent types -// ND1409:AvoidMutuallyDependentTypes - -warnif count > 0 -from t1 in Application.Types -where t1.NbLinesOfCode > 10 && - (t1.IsClass || t1.IsStructure) - -from t2 in t1.TypesUsingMe -where t2.NbLinesOfCode > 10 && - (t2.IsClass || t2.IsStructure) && - t1.IsUsing(t2) - -// To avoid reporting twice the same pair -// use lexicographically sort of full names of types -// to only take the first one. -where new string[] {t1.FullName, t2.FullName}.OrderBy(x => x).First() == t1.FullName - -// So far we only keep dependencies that involve method call. -// It is possible to comment the two 'where' clauses below to get also -// others kind of dependencies, like usage of typeof(Class). -let t2MethodsUsingT1 = t2.Methods.Where(m => m.IsUsing(t1)).ToArray() -where t2MethodsUsingT1.Length > 0 - -let t1MethodsUsingT2 = t1.Methods.Where(m => m.IsUsing(t2)).ToArray() -where t1MethodsUsingT2.Length > 0 - -select new { - t1, t2, - t2MethodsUsingT1, - t1MethodsUsingT2, - Debt = 20.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule matches pairs of classes or structures that use each others. -// -// In this situation both types are not isolated: each type cannot be -// reviewed, refactored or tested without the other one. -// -// This rule is disabled by default because there are some common -// situations, like when implementing recursive data structures, -// complex serialization or using the visitor pattern, where having -// mutually dependent types is legitimate. -// Just enable this rule if you wish to fix most of its issues. -// - -// -// Fixing mutually-dependent types means identifying the unwanted dependency -// (*t1* using *t2*, or *t2* using *t1*) and then removing this dependency. -// -// Often you'll have to use the **dependency inversion principle** by creating -// one or several interfaces dedicated to abstract one implementation -// from the other one: https://en.wikipedia.org/wiki/Dependency_inversion_principle -//]]> - Example of custom rule to check for dependency -// ND9900:ExplicitId9900 - -warnif count > 0 from a in Assemblies -where -a.IsUsing("Foo1.Foo2".AllowNoMatch().MatchNamespace()) && -(a.Name == @"Foo3") -select new { - a, - a.NbLinesOfCode, - // Debt and Severity / Annual Interest can be modified to estimate well the - // effort to fix the issue and the annual cost to leave the issue unfixed. - Debt = 30.ToMinutes().ToDebt(), - Severity = Severity.High -} -// the assembly Foo3 -// shouldn't use directly -// the namespace Foo3.Foo4 -// because (TODO insert your reason) - -// -// This rule is a **sample rule** that shows how to -// check if a particular dependency exists or not, -// from a code element **A** to a code element **B**, -// **A** and **B** being an *Assembly*, a *Namespace*, a *Type*, -// a *Method* or a *Field*, **A** and **B** being not -// necessarily of same kind (i.e two Assemblies or -// two Namespaces…). -// -// Such rule can be generated: -// -// • by right clicking the cell in the *Dependency Matrix* -// with **B** in row and **A** in column, -// -// • or by right-clicking the concerned arrow in the *Dependency -// Graph* from **A** to **B**, -// -// and in both cases, click the menu -// **Generate a code rule that warns if this dependency exists** -// -// The generated rule will look like this one. -// It is now up to you to adapt this rule to check exactly -// your needs. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - - - API Breaking Changes: Types -// ND1500:APIBreakingChangesTypes - -warnif count > 0 from t in codeBase.OlderVersion().Application.Types -where t.IsPubliclyVisible && - - // The type has been removed, it was not tagged as obsolete - // and its parent assembly hasn't been removed … - ( ( t.WasRemoved() && - !t.ParentAssembly.WasRemoved() && - !t.IsObsolete) || - - // … or the type is not publicly visible anymore - !t.WasRemoved() && !t.NewerVersion().IsPubliclyVisible) - -select new { - t, - NewVisibility = - (t.WasRemoved() ? " " : - t.NewerVersion().Visibility.ToString()), - Debt = 20.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This rule warns if a type publicly visible in the *baseline*, -// is not publicly visible anymore or if it has been removed. -// Clients code using such type will be broken. -// - -// -// Make sure that public types that used to be presented to -// clients, still remain public now, and in the future. -// -// If a public type must really be removed, you can tag it -// with *System.ObsoleteAttribute* with a *workaround message* -// during a few public releases, until it gets removed definitely. -// Notice that this rule doesn't match types removed that were -// tagged as obsolete. -// -// Issues of this rule have a severity equal to **High** -// because an API Breaking change can provoque significant -// friction with consumers of the API. -//]]> - API Breaking Changes: Methods -// ND1501:APIBreakingChangesMethods - -warnif count > 0 from m in codeBase.OlderVersion().Application.Methods -where m.IsPubliclyVisible && - - // The method has been removed, it was not tagged as obsolete - // and its parent type hasn't been removed … - ( ( m.WasRemoved() && - !m.ParentType.WasRemoved() && - !m.IsObsolete ) - - // … or the method is not publicly visible anymore - || (!m.WasRemoved() && !m.NewerVersion().IsPubliclyVisible) - - // … or the method return type has changed - || (!m.WasRemoved() && m.ReturnType != null && m.NewerVersion().ReturnType != null - && m.ReturnType.FullName != m.NewerVersion().ReturnType.FullName) - ) - -//-------------------------------------- -// Handle special case: if between two versions a regular property becomes -// an auto-property (or vice-versa) the property getter/setter method have -// a different value for IMethod.IsGeneratedByCompiler -// since auto-property getter/setter are marked as generated by the compiler. -// -// If a method IsGeneratedByCompiler value changes between two versions, -// NDepend doesn't pair the newer/older occurences of the method. -// -// Hence in such situation, a public method is seen as added -// and a public method is seen as removed, but the API is not broken! -// The equivalentMethod-check below avoids reporting such -// API Breaking Change false-positive. -let equivalentMethod = m.WasRemoved() && m.ParentType.IsPresentInBothBuilds() ? - m.ParentType.NewerVersion().Methods - .FirstOrDefault(m1 => - m1.IsPubliclyVisible && - m1.Name == m.Name && - m1.IsGeneratedByCompiler != m.IsGeneratedByCompiler && - (m1.ReturnType == null || m.ReturnType == null || m1.ReturnType.FullName == m.ReturnType.FullName) - ) - : null -where equivalentMethod == null -//-------------------------------------- - - -select new { - m, - NewVisibility = - (m.WasRemoved() ? " " : - m.NewerVersion().Visibility.ToString()), - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This rule warns if a method publicly visible in the *baseline*, -// is not publicly visible anymore or if it has been removed. -// Clients code using such method will be broken. -// - -// -// Make sure that public methods that used to be presented to -// clients, still remain public now, and in the future. -// -// If a public method must really be removed, you can tag it -// with *System.ObsoleteAttribute* with a *workaround message* -// during a few public releases, until it gets removed definitely. -// Notice that this rule doesn't match methods removed that were -// tagged as obsolete. -// -// Issues of this rule have a severity equal to **High** -// because an API Breaking change can provoque significant -// friction with consumers of the API. -// -]]> - API Breaking Changes: Fields -// ND1502:APIBreakingChangesFields - -warnif count > 0 from f in codeBase.OlderVersion().Application.Fields -where f.IsPubliclyVisible && - - // The field has been removed, it was not tagged as obsolete - // and its parent type hasn't been removed … - ( ( f.WasRemoved() && - !f.ParentType.WasRemoved() && - !f.IsObsolete) - - // … or the field is not publicly visible anymore - || !f.WasRemoved() && !f.NewerVersion().IsPubliclyVisible) - - // … or the field type has changed - || (!f.WasRemoved() && f.FieldType != null && f.NewerVersion().FieldType != null - && f.FieldType.FullName != f.NewerVersion().FieldType.FullName) - -select new { - f, - NewVisibility = - (f.WasRemoved() ? " " : - f.NewerVersion().Visibility.ToString()), - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This rule warns if a field publicly visible in the *baseline*, -// is not publicly visible anymore or if it has been removed. -// Clients code using such field will be broken. -// - -// -// Make sure that public fields that used to be presented to -// clients, still remain public now, and in the future. -// -// If a public field must really be removed, you can tag it -// with *System.ObsoleteAttribute* with a *workaround message* -// during a few public releases, until it gets removed definitely. -// Notice that this rule doesn't match fields removed that were -// tagged as obsolete. -// -// Issues of this rule have a severity equal to **High** -// because an API Breaking change can provoque significant -// friction with consumers of the API. -//]]> - API Breaking Changes: Interfaces and Abstract Classes -// ND1503:APIBreakingChangesInterfacesAndAbstractClasses - -warnif count > 0 from tNewer in Application.Types where - (tNewer.IsInterface || tNewer.IsClass && tNewer.IsAbstract) && - tNewer.IsPubliclyVisible && - tNewer.IsPresentInBothBuilds() - -let tOlder = tNewer.OlderVersion() where tOlder.IsPubliclyVisible - -let methodsRemoved = tOlder.Methods.Where(m => m.IsAbstract && m.WasRemoved()) -let methodsAdded = tNewer.Methods.Where(m => m.IsAbstract && m.WasAdded()) - -where methodsAdded.Count() > 0 || methodsRemoved.Count() > 0 -select new { - tNewer, - methodsAdded, - methodsRemoved, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This rule warns if a publicly visible interface or abstract class -// has been changed and contains new abstract methods or -// if some abstract methods have been removed. -// -// Clients code that implement such interface or derive from -// such abstract class will be broken. -// - -// -// Make sure that the public contracts of interfaces and abstract classes -// that used to be presented to clients, remain stable now, and in the future. -// -// If a public contract must really be changed, you can tag -// abstract methods that will be removed with *System.ObsoleteAttribute* -// with a *workaround message* during a few public releases, until it gets -// removed definitely. -// -// Issues of this rule have a severity equal to **High** -// because an API Breaking change can provoque significant -// friction with consummers of the API. -// The severity is not set to **Critical** because an interface -// is not necessarily meant to be implemented by the consummer -// of the API. -//]]> - Broken serializable types -// ND1504:BrokenSerializableTypes - -warnif count > 0 - -from t in Application.Types where - - // Collect types tagged with SerializableAttribute - t.HasAttribute("System.SerializableAttribute".AllowNoMatch()) && - !t.IsDelegate && - t.IsPresentInBothBuilds() && - t.HasAttribute(t) - - // Find newer and older versions of NonSerializedAttribute - let newNonSerializedAttribute = ThirdParty.Types.WithFullName("System.NonSerializedAttribute").SingleOrDefault() - let oldNonSerializedAttribute = newNonSerializedAttribute == null ? null : newNonSerializedAttribute.OlderVersion() - - // Find added or removed fields not marked with NonSerializedAttribute - let addedInstanceField = from f in t.InstanceFields where - f.WasAdded() && - (newNonSerializedAttribute == null || !f.HasAttribute(newNonSerializedAttribute)) - select f - let removedInstanceField = from f in t.OlderVersion().InstanceFields where - f.WasRemoved() && - (oldNonSerializedAttribute == null || !f.HasAttribute(oldNonSerializedAttribute)) - select f - where addedInstanceField.Count() > 0 || removedInstanceField.Count() > 0 - -select new { - t, - addedInstanceField, - removedInstanceField, - Debt = 20.ToMinutes().ToDebt(), - Severity = Severity.Critical -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This rule warns about breaking changes in types tagged with -// *SerializableAttribute*. -// -// To do so, this rule searches for serializable type with serializable -// instance fields added or removed. Notice that it doesn't take account -// of fields tagged with *NonSerializedAttribute*. -// -// From http://msdn.microsoft.com/library/system.serializableattribute.aspx : -// "All the public and private fields in a type that are marked by the -// *SerializableAttribute* are serialized by default, unless the type -// implements the *ISerializable* interface to override the serialization process. -// The default serialization process excludes fields that are marked -// with the *NonSerializedAttribute* attribute." -// - -// -// Make sure that the serialization process of serializable types remains -// stable now, and in the future. -// -// Else you'll have to deal with **Version Tolerant Serialization** -// that is explained here: -// https://msdn.microsoft.com/en-us/library/ms229752(v=vs.110).aspx -// -// Issues of this rule have a severity equal to **High** -// because an API Breaking change can provoque significant -// friction with consummers of the API. -//]]> - Avoid changing enumerations Flags status -// ND1505:AvoidChangingEnumerationsFlagsStatus - -warnif count > 0 - -let oldFlags = codeBase.OlderVersion().ThirdParty.Types.WithFullName("System.FlagsAttribute").FirstOrDefault() -let newFlags = ThirdParty.Types.WithFullName("System.FlagsAttribute").FirstOrDefault() -where oldFlags != null && newFlags != null - -from t in Application.Types where - t.IsEnumeration && - t.IsPresentInBothBuilds() -let hasFlagsAttributeNow = t.HasAttribute(newFlags) -let usedToHaveFlagsAttribute = t.OlderVersion().HasAttribute(oldFlags) -where hasFlagsAttributeNow != usedToHaveFlagsAttribute -select new { - t, - hasFlagsAttributeNow, - usedToHaveFlagsAttribute, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This rule matches enumeration types that used to be tagged -// with *FlagsAttribute* in the *baseline*, and not anymore. -// It also matches the opposite, enumeration types that are now -// tagged with *FlagsAttribute*, and were not tagged in the *baseline*. -// -// Being tagged with *FlagsAttribute* is a strong property for an enumeration. -// Not so much in terms of *behavior* (only the *enum.ToString()* method -// behavior changes when an enumeration is tagged with *FlagsAttribute*) -// but in terms of *meaning*: is the enumeration a **range of values** -// or a **range of flags**? -// -// As a consequence, changing the *FlagsAttribute*s status of an enumeration can -// have significant impact for its clients. -// - -// -// Make sure the *FlagsAttribute* status of each enumeration remains stable -// now, and in the future. -//]]> - API: New publicly visible types -from t in Application.Types -where t.IsPubliclyVisible && - - // The type has been removed and its parent assembly hasn't been removed … - ( (t.WasAdded() && !t.ParentAssembly.WasAdded()) || - - // … or the type existed but was not publicly visible - !t.WasAdded() && !t.OlderVersion().IsPubliclyVisible) - -select new { - t, - OldVisibility = - (t.WasAdded() ? " " : - t.OlderVersion().Visibility.ToString()), -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists types that are new in the public -// surface of the analyzed assemblies. -//]]> - API: New publicly visible methods -from m in Application.Methods -where m.IsPubliclyVisible && - - // The method has been removed and its parent assembly hasn't been removed … - ( (m.WasAdded() && !m.ParentType.WasAdded()) || - - // … or the method existed but was not publicly visible - !m.WasAdded() && !m.OlderVersion().IsPubliclyVisible) - -//-------------------------------------- -// Handle special case: if between two versions a regular property becomes -// an auto-property (or vice-versa) the property getter/setter method have -// a different value for IMethod.IsGeneratedByCompiler -// since auto-property getter/setter are marked as generated by the compiler. -// -// If a method IsGeneratedByCompiler value changes between two versions, -// NDepend doesn't pair the newer/older occurences of the method. -// -// Hence in such situation, a public method is seen as added -// and a public method is seen as removed, but the API is not broken! -// The equivalentMethod-check below avoids reporting such -// API Breaking Change false-positive. -let equivalentMethod = m.WasAdded() && m.ParentType.IsPresentInBothBuilds() ? - m.ParentType.OlderVersion().Methods - .FirstOrDefault(m1 => - m1.IsPubliclyVisible && - m1.Name == m.Name && - m1.IsGeneratedByCompiler != m.IsGeneratedByCompiler) - : null -where equivalentMethod == null -//-------------------------------------- - -select new { - m, - OldVisibility = - (m.WasAdded() ? " " : - m.OlderVersion().Visibility.ToString()) -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists methods that are new in the public -// surface of the analyzed assemblies. -// -]]> - API: New publicly visible fields -from f in Application.Fields -where f.IsPubliclyVisible && - - // The method has been removed and its parent assembly hasn'f been removed … - ( (f.WasAdded() && !f.ParentType.WasAdded()) || - - // … or the t existed but was not publicly visible - !f.WasAdded() && !f.OlderVersion().IsPubliclyVisible) - -select new { - f, - OldVisibility = - (f.WasAdded() ? " " : - f.OlderVersion().Visibility.ToString()) -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists fields that are new in the public -// surface of the analyzed assemblies. -//]]> - - - Code should be tested -// ND1600:CodeShouldBeTested - -warnif count > 0 - -// This lambda infers a factor in the range [0,1] from a sequence of distinct types used, -// based on how many of these types are actually concrete (i.e are not interfaces or enumeration). -// Primitive types (int, bool...) are eliminated from the sequence of types used -// by filtering types declared in the namespace System. -let abstractionUsageFactorFormula = new Func,double>(typesUsed => - typesUsed.Count(t => t.ParentNamespace.Name != "System" && !t.IsInterface && !t.IsEnumeration) - / (1 + typesUsed.Count(t => t.ParentNamespace.Name != "System"))) - - -from method in Application.Methods - where !method.IsExcludedFromCoverage && method.NbLinesOfCode >= 0 && method.PercentageCoverage < 100 - - // Factor in case method is partially covered - let uncoverageFactor = ((100 - method.PercentageCoverage) / 100).Value - - // Complexity factor - let complexityFactor = ((method.CyclomaticComplexity ?? method.ILCyclomaticComplexity) + method.ILNestingDepth).Linear(0, 0.1, 10, 1).Value - - // Not my code is often generated code and is in general easier to get tested since test can be generated as well. - let justMyCodeFactor = (JustMyCode.Contains(method) ? 1 : 0.4) - - // abstractionUsageFactor reflects the fact that code that relies on interfaces - // is easier to test that code that relies on concrete classes. - let abstractionUsageFactor = 0.7 + 0.3 *abstractionUsageFactorFormula(method.MembersUsed.Select(m => m.ParentType).Distinct()) - - // The usageFactor depends on the method 'rank' that is a value - // indicating if the method is often used or not - let usageFactor = (method.Rank / (method.Rank + 4)).Value - - // It is more complicated to write tests for non publicly visible methods - let visibilityFactor = method.Visibility.EqualsAny(Visibility.Public, Visibility.Internal) ? 1 : - method.Visibility != Visibility.Private ? 1.1 : 1.2 - - // Is is more complicated to write tests for methods that read mutable static fields - // whose changing state is shared across tests executions. - let staticFieldUsageFactor = method.ReadsMutableTypeState ? 1.3 : 1.0 - - - // Both "effort to write tests" and "annual cost to not test" for a method - // is determined by several factors in the range [0,1] that multiplies the effortToDevelop - let effortToDevelopInMinutes = method.EffortToDevelop().Value.TotalMinutes - - let effortToWriteTests = Math.Max(2, // Minimum 2 minutes per method not tested - effortToDevelopInMinutes * - uncoverageFactor * - complexityFactor * - justMyCodeFactor * - abstractionUsageFactor * - visibilityFactor * - staticFieldUsageFactor).ToMinutes().ToDebt() - - let annualCostToNotFix = Math.Max(2, // Minimum 2 minutes per method not tested - effortToDevelopInMinutes * - usageFactor * - uncoverageFactor * - justMyCodeFactor).ToMinutes().ToAnnualInterest() - - orderby annualCostToNotFix.Value descending - -select new { - method, - method.PercentageCoverage, - method.NbLinesOfCode, - method.NbLinesOfCodeNotCovered, - method.CyclomaticComplexity, - Debt = effortToWriteTests, - AnnualInterest = annualCostToNotFix, - - // BreakingPoint = effortToWriteTests.BreakingPoint(annualCostToNotFix), - - // Uncomment the line below to tinker with various factors - // uncoverageFactor, complexityFactor , justMyCodeFactor , abstractionUsageFactor, visibilityFactor, staticFieldUsageFactor -} - - -// -// This rule lists methods not covered at all by test -// or partially covered by tests. -// -// For each match, the rules estimates the **technical debt**, i.e -// the effort to write unit and integration tests for the method. -// The estimation is based on the effort to develop the code element -// multiplied by factors in the range ]0,1.3] based on -// -// • the method code size and complexity -// -// • the actual percentage coverage -// -// • the abstractness of types used, because relying on classes instead of -// interfaces makes the code more difficult to test -// -// • the method visibility because testing private or protected -// methods is more difficult than testing public and internal ones -// -// • the fields used by the method, because is is more complicated to -// write tests for methods that read mutable static fields whose changing -// state is shared across tests executions. -// -// • whether the method is considered *JustMyCode* or not because *NotMyCode* -// is often generated easier to get tested since tests can be generated as well. -// -// This rule is necessarily a large source of technical debt, since -// the code left untested is by definition part of the technical debt. -// -// This rule also estimates the **annual interest**, i.e the annual cost -// to let the code uncovered, based on the effort to develop the -// code element, multiplied by factors based on usage of the code element. -// - -// -// Write unit tests to test and cover the methods and their parent classes -// matched by this rule. -//]]> - New Methods should be tested -// ND1601:NewMethodsShouldBeTested - -warnif count > 0 -from m in Application.Methods where - m.NbLinesOfCode > 0 && - m.PercentageCoverage < 30 && - m.WasAdded() - orderby m.NbLinesOfCode descending, - m.NbLinesOfCodeNotCovered , - m.PercentageCoverage -select new { - m, - m.PercentageCoverage, - m.NbLinesOfCode, - m.NbLinesOfCodeNotCovered, - - // Simplistic Debt estimation, because the effort to write tests for a method not 100% tested - // is already estimated properly with the rule "Code should be tested". - Debt = m.NbLinesOfCodeNotCovered.Linear(1,2, 10,10).ToMinutes().ToDebt(), - - Severity = Severity.High -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// This rule operates only on methods added or refactored since the baseline. -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// It is important to write code mostly covered by tests -// to achieve *maintainable* and *non-error-prone* code. -// -// In real-world, many code bases are poorly covered by tests. -// However it is not practicable to stop the development for months -// to refactor and write tests to achieve high code coverage ratio. -// -// Hence it is recommended that each time a method (or a type) gets added, -// the developer takes the time to write associated unit-tests to cover it. -// -// Doing so will help to increase significantly the maintainability of the code base. -// You'll notice that quickly, refactoring will also be driven by testability, -// and as a consequence, the overall code structure and design will increase as well. -// -// Issues of this rule have a **High** severity because they reflect -// an actual trend to not care about writing tests on refactored code. -// - -// -// Write unit-tests to cover the code of most methods and classes added. -//]]> - Methods refactored should be tested -// ND1602:MethodsRefactoredShouldBeTested - -warnif count > 0 -from m in Application.Methods where - m.PercentageCoverage < 30 && - m.CodeWasChanged() - orderby m.NbLinesOfCode descending, - m.NbLinesOfCodeNotCovered , - m.PercentageCoverage -select new { - m, - m.PercentageCoverage, - m.NbLinesOfCode, - m.NbLinesOfCodeNotCovered, - - // Simplistic Debt estimation, because the effort to write tests for a method not 100% tested - // is already estimated properly with the rule "Code should be tested". - Debt = m.NbLinesOfCodeNotCovered.Linear(1,2, 10,10).ToMinutes().ToDebt(), - - Severity = Severity.High -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// This rule operates only on methods added or refactored since the baseline. -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// It is important to write code mostly covered by tests -// to achieve *maintainable* and *non-error-prone* code. -// -// In real-world, many code bases are poorly covered by tests. -// However it is not practicable to stop the development for months -// to refactor and write tests to achieve high code coverage ratio. -// -// Hence it is recommended that each time a method (or a type) gets refactored, -// the developer takes the time to write associated unit-tests to cover it. -// -// Doing so will help to increase significantly the maintainability of the code base. -// You'll notice that quickly, refactoring will also be driven by testability, -// and as a consequence, the overall code structure and design will increase as well. -// -// Issues of this rule have a **High** severity because they reflect -// an actual trend to not care about writing tests on refactored code. -// - -// -// Write unit-tests to cover the code of most methods and classes refactored. -//]]> - Assemblies Namespaces and Types should be tested -// ND1603:AssembliesNamespacesAndTypesShouldBeTested - -warnif count > 0 -from elem in CodeElementParents -where elem.NbLinesOfCode > 0 && - elem.PercentageCoverage == 0 && - elem.Parent.PercentageCoverage != 0 -orderby elem.NbLinesOfCode descending -select new { - elem, - elem.NbLinesOfCodeNotCovered, - Debt = 4.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// This rule lists assemblies, namespaces and types that are not -// covered at all by unit tests. -// -// If a parent is matched its children are not matched. For example -// if a namespace is matched, its child types are not matched. -// -// This rule goal is not to collide with the **Code should be tested** -// rule that lists uncovered code for each method and infer the effort -// to write unit tests (the *Debt*) and the annual cost to let the code -// untested (the Annual Interest). -// -// This rule goal is to inform of large code elements left untested. -// As a consequence the *Debt* per issue is only 4 minutes and the -// severity of the issues is *Low*. -// -// -// -// Write unit and integration tests to cover, even partially, -// code elements matched by this rule. -// -// Then use issues of the rules **Code should be tested**, -// **New Methods should be tested** and -// **Methods refactored should be tested** -// to write more tests where it matters most, and eventually -// refactor some code to make it more testable. -//]]> - Types almost 100% tested should be 100% tested -// ND1604:TypesAlmost100PercentTestedShouldBe100PercentTested - -warnif count > 0 -from t in Application.Types where - t.PercentageCoverage >= 95 && - t.PercentageCoverage <= 99 && - !t.IsGeneratedByCompiler - - let methodsCulprit = t.Methods.Where(m => m.PercentageCoverage < 100) - - orderby t.NbLinesOfCode descending , - t.NbLinesOfCodeNotCovered , - t.PercentageCoverage -select new { - t, - t.PercentageCoverage, - t.NbLinesOfCode, - t.NbLinesOfCodeNotCovered, - methodsCulprit, - - // Simplistic Debt estimation, because the effort to write tests for a type not 100% tested - // is already estimated properly with the rule "Code should be tested". - Debt = t.NbLinesOfCodeNotCovered.Linear(1,2, 20,20).ToMinutes().ToDebt(), - - Severity = Severity.High -} - -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// Often covering the few percents of remaining uncovered code of a class, -// requires as much work as covering the first 90%. -// For this reason, often teams estimate that 90% coverage is enough. -// However *untestable code* usually means *poorly written code* -// which usually leads to *error prone code*. -// So it might be worth refactoring and making sure to cover the few uncovered lines of code -// **because most tricky bugs might come from this small portion of hard-to-test code**. -// -// Not all classes should be 100% covered by tests (like UI code can be hard to test) -// but you should make sure that most of the logic of your application -// is defined in some *easy-to-test classes*, 100% covered by tests. -// -// Issues of this rule have a **High** severity because as explained, -// such situation is *bug-prone*. -// - -// -// Write more unit-tests dedicated to cover code not covered yet. -// If you find some *hard-to-test code*, it is certainly a sign that this code -// is not *well designed* and hence, needs refactoring. -//]]> - Namespaces almost 100% tested should be 100% tested -// ND1605:NamespacesAlmost100PercentTestedShouldBe100PercentTested - -warnif count > 0 -from n in Application.Namespaces where - n.PercentageCoverage >= 95 && - n.PercentageCoverage <= 99 - - let methodsCulprit = n.ChildMethods.Where(m => m.PercentageCoverage < 100) - - orderby n.NbLinesOfCode descending , - n.NbLinesOfCodeNotCovered , - n.PercentageCoverage -select new { - n, - n.PercentageCoverage, - n.NbLinesOfCode, - n.NbLinesOfCodeNotCovered, - methodsCulprit, - - // Simplistic Debt estimation, because the effort to write tests for a type not 100% tested - // is already estimated properly with the rule "Code should be tested". - Debt = n.NbLinesOfCodeNotCovered.Linear(1,2, 50,60).ToMinutes().ToDebt(), - - Severity = Severity.High -} - -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// Often covering the few percents of remaining uncovered code of -// one or several classes in a namespace -// requires as much work as covering the first 90%. -// For this reason, often teams estimate that 90% coverage is enough. -// However *untestable code* usually means *poorly written code* -// which usually leads to *error prone code*. -// So it might be worth refactoring and making sure to cover the few uncovered lines of code -// **because most tricky bugs might come from this small portion of hard-to-test code**. -// -// Not all classes should be 100% covered by tests (like UI code can be hard to test) -// but you should make sure that most of the logic of your application -// is defined in some *easy-to-test classes*, 100% covered by tests. -// -// Issues of this rule have a **High** severity because as explained, -// such situation is *bug-prone*. -// - -// -// Write more unit-tests dedicated to cover code not covered yet in the namespace. -// If you find some *hard-to-test code*, it is certainly a sign that this code -// is not *well designed* and hence, needs refactoring. -//]]> - Types that used to be 100% covered by tests should still be 100% covered -// ND1606:TypesThatUsedToBe100PercentCoveredByTestsShouldStillBe100PercentCovered - -warnif count > 0 -from t in JustMyCode.Types where - t.IsPresentInBothBuilds() && - t.OlderVersion().PercentageCoverage == 100 && - t.PercentageCoverage < 100 - -from m in t .MethodsAndConstructors where - m.NbLinesOfCode> 0 && - m.PercentageCoverage < 100 && - !m.IsExcludedFromCoverage - -select new { - m, - m.PercentageCoverage, - - // Simplistic Debt estimation, because the effort to write tests for a method not 100% tested - // is already estimated properly with the rule "Code should be tested". - Debt = t.NbLinesOfCodeNotCovered.Linear(1,2, 10,10).ToMinutes().ToDebt(), - - Severity = Severity.High -} - -// -// This rule is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// Often covering 10% of remaining uncovered code of a class, -// requires as much work as covering the first 90%. -// For this reason, typically teams estimate that 90% coverage is enough. -// However *untestable code* usually means *poorly written code* -// which usually leads to *error prone code*. -// So it might be worth refactoring and making sure to cover the 10% remaining code -// **because most tricky bugs might come from this small portion of hard-to-test code**. -// -// Not all classes should be 100% covered by tests (like UI code can be hard to test) -// but you should make sure that most of the logic of your application -// is defined in some *easy-to-test classes*, 100% covered by tests. -// -// In this context, this rule warns when a type fully covered by tests is now only partially covered. -// -// Issues of this rule have a **High** severity because often, -// a type that used to be 100% and is not covered anymore -// is a bug-prone situation that should be carefully handled. -// - -// -// Write more unit-tests dedicated to cover code not covered anymore. -// If you find some *hard-to-test code*, it is certainly a sign that this code -// is not *well designed* and hence, needs refactoring. -// -// You'll find code impossible to cover by unit-tests, like calls to *MessageBox.Show()*. -// An infrastructure must be defined to be able to *mock* such code at test-time. -//]]> - Types tagged with FullCoveredAttribute should be 100% covered -// ND1607:TypesTaggedWithFullCoveredAttributeShouldBe100PercentCovered - -warnif count > 0 -from t in Application.Types where - t.HasAttribute ("NDepend.Attributes.FullCoveredAttribute".AllowNoMatch()) && - t.PercentageCoverage < 100 - -from m in t .MethodsAndConstructors where - m.NbLinesOfCode> 0 && - m.PercentageCoverage < 100 && - !m.IsExcludedFromCoverage - -select new { - m, - m.PercentageCoverage, - m.NbLinesOfCodeNotCovered, - m.NbLinesOfCode, - - // Simplistic Debt estimation, because the effort to write tests for a method not 100% tested - // is already estimated properly with the rule "Code should be tested". - Debt = m.NbLinesOfCodeNotCovered.Linear(1,2, 10,10).ToMinutes().ToDebt(), - - Severity = Severity.High -} - -// -// This rule lists methods partially covered by tests, of types tagged with -// **FullCoveredAttribute**. -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// By using a **FullCoveredAttribute**, you can express in source code the intention -// that a class is 100% covered by tests, and should remain 100% covered in the future. -// If you don't want to link *NDepend.API.dll*, -// you can use your own attribute and adapt the source code of this rule. -// -// Benefits of using a **FullCoveredAttribute** are twofold: -// Not only the intention is expressed in source code, -// but it is also continuously checked by the present rule. -// -// Often covering 10% of remaining uncovered code of a class, -// requires as much work as covering the first 90%. -// For this reason, often teams estimate that 90% coverage is enough. -// However *untestable code* usually means *poorly written code* which usually means *error prone code*. -// So it might be worth refactoring and making sure to cover the 10% remaining code -// **because most tricky bugs might come from this small portion of hard-to-test code**. -// -// Not all classes should be 100% covered by tests (like UI code can be hard to test) -// but you should make sure that most of the logic of your application -// is defined in some *easy-to-test classes*, 100% covered by tests. -// -// Issues of this rule have a **High** severity because often, -// a type that used to be 100% and is not covered anymore -// is a bug-prone situation that should be carefully handled. -// - -// -// Write more unit-tests dedicated to cover code of matched classes not covered yet. -// If you find some *hard-to-test code*, it is certainly a sign that this code -// is not *well designed* and hence, needs refactoring. -//]]> - Types 100% covered should be tagged with FullCoveredAttribute -// ND1608:Types100PercentCoveredShouldBeTaggedWithFullCoveredAttribute - -warnif count > 0 from t in JustMyCode.Types where - !t.HasAttribute ("NDepend.Attributes.FullCoveredAttribute".AllowNoMatch()) && - t.PercentageCoverage == 100 && - !t.IsGeneratedByCompiler -select new { - t, - t.NbLinesOfCode, - Debt = 3.ToMinutes().ToDebt(), // It is fast to add such attribute to a type. - Severity = Severity.Low -} - -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// By using a **FullCoveredAttribute**, you can express in source code the intention -// that a class is 100% covered by tests, and should remain 100% covered in the future. -// -// Benefits of using a **FullCoveredAttribute** are twofold: -// Not only the intention is expressed in source code, -// but it is also continuously checked by the present rule. -// -// Issues of this rule have an **Low** severity because they don't reflect -// a problem, but provide an advice for potential improvement. -// - -// -// Just tag types 100% covered by tests with the **FullCoveredAttribute** -// that can be found in *NDepend.API.dll*, -// or by an attribute of yours defined in your own code -// (in which case this rule must be adapted). -//]]> - Methods should have a low C.R.A.P score -// ND1609:MethodsShouldHaveALowCRAPScore - -warnif count > 0 -from m in JustMyCode.Methods - -// Don't match too short methods -where m.NbLinesOfCode > 10 && m.CoverageDataAvailable - -let CC = m.CyclomaticComplexity -let uncov = (100 - m.PercentageCoverage) / 100f -let CRAP = (double)(CC * CC * uncov * uncov * uncov) + CC -where CRAP != null && CRAP > 30 -orderby CRAP descending, m.NbLinesOfCode descending -select new { - m, - CRAP, - CC, - m.PercentageCoverage, m.NbLinesOfCode, - - // CRAP score equals 30 => 10 minutes debt - // CRAP score equals 3000 => 3 hours to write tests - Debt = CRAP.Linear(30,10, 3000, 3*60).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is executed only if some code coverage data is imported -// from some code coverage files. -// -// So far this rule is disabled because other code coverage rules -// assess properly code coverage issues. -// -// **Change Risk Analyzer and Predictor** (i.e. CRAP) is a code metric -// that helps in pinpointing overly both complex and untested code. -// Is has been first defined here: -// http://www.artima.com/weblogs/viewpost.jsp?thread=215899 -// -// The Formula is: **CRAP(m) = CC(m)^2 * (1 – cov(m)/100)^3 + CC(m)** -// -// • where *CC(m)* is the *cyclomatic complexity* of the method *m* -// -// • and *cov(m)* is the *percentage coverage* by tests of the method *m* -// -// Matched methods cumulates two highly *error prone* code smells: -// -// • A complex method, difficult to develop and maintain. -// -// • Non 100% covered code, difficult to refactor without introducing any regression bug. -// -// The higher the CRAP score, the more painful to maintain and error prone is the method. -// -// An arbitrary threshold of 30 is fixed for this code rule as suggested by inventors. -// -// Notice that no amount of testing will keep methods with a Cyclomatic Complexity -// higher than 30, out of CRAP territory. -// -// Notice that this rule doesn't match too short method -// with less than 10 lines of code. -// - -// -// In such situation, it is recommended to both refactor the complex method logic -// into several smaller and less complex methods -// (that might belong to some new types especially created), -// and also write unit-tests to full cover the refactored logic. -// -// You'll find code impossible to cover by unit-tests, like calls to *MessageBox.Show()*. -// An infrastructure must be defined to be able to *mock* such code at test-time. -//]]> - Test Methods - -let testAttr = ThirdParty.Types.WithNameIn("FactAttribute", "TestAttribute", "TestCaseAttribute") -let testMethods = Methods.TaggedWithAnyAttributes(testAttr) -from m in testMethods -select m - -// -// We advise to not include test assemblies in code analyzed by NDepend. -// We estimate that it is acceptable and practical to lower the quality gate of test code, -// because the important measures for tests are: -// -// • The coverage ratio, -// -// • And the amount of logic results asserted: This includes both -// assertions in test code, and assertions in code covered by tests, -// like *Code Contract* assertions and *Debug.Assert(…)* assertions. -// -// But if you wish to enforce the quality of test code, you'll need to -// consider test assemblies in your list of application assemblies -// analyzed by NDepend. -// -// In such situation, this code query lists tests methods and you can -// reuse this code in custom rules. -//]]> - Methods directly called by test Methods - -let testAttr = ThirdParty.Types.WithNameIn("FactAttribute", "TestAttribute", "TestCaseAttribute") -let testMethods = Methods.TaggedWithAnyAttributes(testAttr).ToHashSetEx() - -// --- Uncomment this line if your test methods are in dedicated test assemblies --- -//let testAssemblies = testMethods.ParentAssemblies().ToHashSetEx() - -from m in Application.Methods.UsedByAny(testMethods) - -// --- Uncomment this line if your test methods are in dedicated test assemblies --- -//where !testAssemblies.Contains(m.ParentAssembly) - -select new { m , - calledByTests = m.MethodsCallingMe.Intersect(testMethods ), - // --- Uncomment this line if your project import some coverage data --- - // m.PercentageCoverage -} - - -// -// This query lists all methods directly called by tests methods. -// Overrides of virtual and abstract methods, called through polymorphism, are not listed. -// Methods solely invoked through a delegate are not listed. -// Methods solely invoked through reflection are not listed. -// -// We advise to not include test assemblies in code analyzed by NDepend. -// We estimate that it is acceptable and practical to lower the quality gate of test code, -// because the important measures for tests are: -// -// • The coverage ratio, -// -// • And the amount of logic results asserted: This includes both -// assertions in test code, and assertions in code covered by tests, -// like *Code Contract* assertions and *Debug.Assert(…)* assertions. -// -// But if you wish to run this code query, -// you'll need to consider test assemblies in your list of -// application assemblies analyzed by NDepend. -//]]> - Methods directly and indirectly called by test Methods - -let testAttr = from t in ThirdParty.Types.WithNameIn("FactAttribute", "TestAttribute", "TestCaseAttribute") select t -let testMethods = Methods.TaggedWithAnyAttributes(testAttr) - -// --- Uncomment this line if your test methods are in dedicated test assemblies --- -// let testAssemblies = testMethods.ParentAssemblies().ToHashSetEx() - -let depthOfCalledByTest = Application.Methods.DepthOfIsUsedByAny(testMethods) -from pair in depthOfCalledByTest -where pair.Value > 0 -orderby pair.Value ascending -// --- Uncomment this line if your test methods are in dedicated test assemblies --- -//&& !testAssemblies.Contains(pair.CodeElement.ParentAssembly) - -select new { - method = pair.CodeElement, - // (depthOfCalledByTests == 1) means that the method is directly called by tests - // (depthOfCalledByTests == 2) means that the method is directly called by a method directly called by tests - // … - depthOfCalledByTests = pair.Value, - nbLinesOfCode = pair.CodeElement.NbLinesOfCode, - // --- Uncomment this line if your project import some coverage data --- - // m.PercentageCoverage -} - -// -// This query lists all methods *directly or indirectly* called by tests methods. -// *Indirectly* called by a test means that a test method calls a method, that calls a method… -// From this recursion, a code metric named *depthOfCalledByTests* is inferred, -// The value *1* means directly called by test, -// the value *2* means called by a method that is called by a test… -// -// Overrides of virtual and abstract methods, called through polymorphism, are not listed. -// Methods solely invoked through a delegate are not listed. -// Methods solely invoked through reflection are not listed. -// -// We advise to not include test assemblies in code analyzed by NDepend. -// We estimate that it is acceptable and practical to lower the quality gate of test code, -// because the important measures for tests are: -// -// • The coverage ratio, -// -// • And the amount of logic results asserted: This includes both -// assertions in test code, and assertions in code covered by tests, -// like *Code Contract* assertions and *Debug.Assert(…)* assertions. -// -// But if you wish to run this code query, -// you'll need to consider test assemblies in your list of -// application assemblies analyzed by NDepend. -//]]> - - - Potentially Dead Types -// ND1700:PotentiallyDeadTypes - -warnif count > 0 -// Filter procedure for types that should'nt be considered as dead -let canTypeBeConsideredAsDeadProc = new Func( - t => !t.IsPublic && // Public types might be used by client applications of your assemblies. - t.Name != "Program" && - !t.IsGeneratedByCompiler && - - // If you don't want to link NDepend.API.dll, you can use your own - // IsNotDeadCodeAttribute or UsedImplicitlyAttribute - // and adapt the source code of this rule. - !t.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) && - !t.HasAttribute("JetBrains.Annotations.UsedImplicitlyAttribute".AllowNoMatch()) && - - // Exclude static types that define only const fields - // because they cannot be seen as used in IL code. - !(t.IsStatic && t.NbMethods == 0 && !t.Fields.Where(f => !f.IsLiteral).Any()) && - - // Entity Framework ModelSnapshot classes are used ony by the EF infrastructure. - !t.DeriveFrom("Microsoft.EntityFrameworkCore.Infrastructure.ModelSnapshot".AllowNoMatch())) - -// Select types unused -let typesUnused = - from t in JustMyCode.Types where - t.NbTypesUsingMe == 0 && canTypeBeConsideredAsDeadProc(t) - select t - -// Dead types = types used only by unused types (recursive) -let deadTypesMetric = typesUnused.FillIterative( -types => from t in codeBase.Application.Types.UsedByAny(types).Except(types) - where canTypeBeConsideredAsDeadProc(t) && - t.TypesUsingMe.Intersect(types).Count() == t.NbTypesUsingMe - select t) - -from t in deadTypesMetric.DefinitionDomain -select new { - t, - depth = deadTypesMetric[t], - t.TypesUsingMe, - Debt = 15.ToMinutes().ToDebt(), - AnnualInterest = (10 + (t.NbLinesOfCode ?? 1)).ToMinutes().ToAnnualInterest() -} - -// -// This rule lists *potentially* **dead types**. -// A dead type is a type that can be removed -// because it is never used by the program. -// -// This rule lists not only types not used anywhere in code, -// but also types used only by types not used anywhere in code. -// This is why this rule comes with a column *TypesusingMe* and -// this is why there is a code metric named *depth*: -// -// • A *depth* value of *0* means the type is not used. -// -// • A *depth* value of *1* means the type is used only by types not used. -// -// • etc… -// -// By reading the source code of this rule, you'll see that by default, -// *public* types are not matched, because such type might not be used -// by the analyzed code, but still be used by client code, not analyzed by NDepend. -// This default behavior can be easily changed. -// -// Note that this rule doesn't match Entity Framework ModelSnapshot classes -// that are used ony by the EF infrastructure. -// - -// -// *Static analysis* cannot provide an *exact* list of dead types, -// because there are several ways to use a type *dynamically* (like through reflection). -// -// For each type matched by this query, first investigate if the type is used somehow -// (like through reflection). -// If the type is really never used, it is important to remove it -// to avoid maintaining useless code. -// If you estimate the code of the type might be used in the future, -// at least comment it, and provide an explanatory comment about the future intentions. -// -// If a type is used somehow, -// but still is matched by this rule, you can tag it with the attribute -// **IsNotDeadCodeAttribute** found in *NDepend.API.dll* to avoid matching the type again. -// You can also provide your own attribute for this need, -// but then you'll need to adapt this code rule. -// -// Issues of this rule have a **Debt** equal to 15 minutes because it only -// takes a short while to investigate if a type can be safely discarded. -// The **Annual Interest** of issues of this rule, the annual cost to not -// fix such issue, is proportional to the type #lines of code, because -// the bigger the type is, the more it slows down maintenance. -//]]> - Potentially Dead Methods -// ND1701:PotentiallyDeadMethods - -warnif count > 0 -// Filter procedure for methods that should'nt be considered as dead -let canMethodBeConsideredAsDeadProc = new Func( - m => !m.IsPubliclyVisible && // Public methods might be used by client applications of your assemblies. - !m.IsEntryPoint && // Main() method is not used by-design. - !m.IsExplicitInterfaceImpl && // The IL code never explicitly calls explicit interface methods implementation. - !m.IsClassConstructor && // The IL code never explicitly calls class constructors. - !m.IsFinalizer && // The IL code never explicitly calls finalizers. - !m.IsVirtual && // Only check for non virtual method that are not seen as used in IL. - !(m.IsConstructor && // Don't take account of protected ctor that might be call by a derived ctors. - m.IsProtected) && - !m.IsEventAdder && // The IL code never explicitly calls events adder/remover. - !m.IsEventRemover && - !m.IsGeneratedByCompiler && - !m.ParentType.IsDelegate && - - // Don't consider Global ASP.NET methods as unused - !m.ParentType.DeriveFrom("System.Web.HttpApplication".AllowNoMatch()) && - - // Methods tagged with these attributes are called by the serialization infrastructure. - !m.HasAttribute("System.Runtime.Serialization.OnSerializingAttribute".AllowNoMatch()) && - !m.HasAttribute("System.Runtime.Serialization.OnDeserializingAttribute".AllowNoMatch()) && - !m.HasAttribute("System.Runtime.Serialization.OnSerializedAttribute".AllowNoMatch()) && - !m.HasAttribute("System.Runtime.Serialization.OnDeserializedAttribute".AllowNoMatch()) && - - // If you don't want to link NDepend.API.dll, you can use your own - // IsNotDeadCodeAttribute or UsedImplicitlyAttribute - // and adapt the source code of this rule. - !m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) && - !m.HasAttribute("JetBrains.Annotations.UsedImplicitlyAttribute".AllowNoMatch()) && - - // Don't consider public getters/setters - // of classes that implement INotifyPropertyChanged - // as dead code. - !(m.IsPublic && (m.IsPropertyGetter || m.IsPropertySetter) && - m.ParentType.Implement("System.ComponentModel.INotifyPropertyChanged".AllowNoMatch())) - ) - -// Get methods unused -let methodsUnused = - from m in JustMyCode.Methods where - m.NbMethodsCallingMe == 0 && - canMethodBeConsideredAsDeadProc(m) - select m - -// Dead methods = methods used only by unused methods (recursive) -let deadMethodsMetric = methodsUnused.FillIterative( - methods => // Unique loop, just to let a chance to build the hashset. - from o in (new object()).ToEnumerable() - // Use a hashet to make Intersect calls much faster! - let hashset = methods.ToHashSetEx() - from m in codeBase.Application.Methods.UsedByAny(methods).Except(methods) - where canMethodBeConsideredAsDeadProc(m) && - // Select methods called only by methods already considered as dead - hashset.Intersect(m.MethodsCallingMe).Count() == m.NbMethodsCallingMe - select m) - -from m in JustMyCode.Methods.Intersect(deadMethodsMetric.DefinitionDomain) -let depth = deadMethodsMetric[m] -select new { - m, - depth, - m.MethodsCallingMe, - Debt = (10 + 3*depth).ToMinutes().ToDebt(), - AnnualInterest = (8 + (m.NbLinesOfCode ?? 1)).ToMinutes().ToAnnualInterest() -} - -// -// This rule lists *potentially* **dead methods**. -// A dead method is a method that can be removed -// because it is never called by the program. -// -// This rule lists not only methods not called anywhere in code, -// but also methods called only by methods not called anywhere in code. -// This is why this rule comes with a column *MethodsCallingMe* and -// this is why there is a code metric named *depth*: -// -// • A *depth* value of *0* means the method is not called. -// -// • A *depth* value of *1* means the method is called only by methods not called. -// -// • etc… -// -// By reading the source code of this rule, you'll see that by default, -// *public* methods are not matched, because such method might not be called -// by the analyzed code, but still be called by client code, not analyzed by NDepend. -// This default behavior can be easily changed. -// - -// -// *Static analysis* cannot provide an *exact* list of dead methods, -// because there are several ways to invoke a method *dynamically* (like through reflection). -// -// For each method matched by this query, first investigate if the method is invoked somehow -// (like through reflection). -// If the method is really never invoked, it is important to remove it -// to avoid maintaining useless code. -// If you estimate the code of the method might be used in the future, -// at least comment it, and provide an explanatory comment about the future intentions. -// -// If a method is invoked somehow, -// but still is matched by this rule, you can tag it with the attribute -// **IsNotDeadCodeAttribute** found in *NDepend.API.dll* to avoid matching the method again. -// You can also provide your own attribute for this need, -// but then you'll need to adapt this code rule. -// -// Issues of this rule have a **Debt** equal to 10 minutes because it only -// takes a short while to investigate if a method can be safely discarded. -// On top of these 10 minutes, the depth of usage of such method adds up -// 3 minutes per unity because dead method only called by dead code -// takes a bit more time to be investigated. -// -// The **Annual Interest** of issues of this rule, the annual cost to not -// fix such issue, is proportional to the type #lines of code, because -// the bigger the method is, the more it slows down maintenance. -//]]> - Potentially Dead Fields -// ND1702:PotentiallyDeadFields - -warnif count > 0 -from f in JustMyCode.Fields where - f.NbMethodsUsingMe == 0 && - !f.IsPublic && // Although not recommended, public fields might be used by client applications of your assemblies. - !f.IsLiteral && // The IL code never explicitly uses literal fields. - !f.IsEnumValue && // The IL code never explicitly uses enumeration value. - f.Name != "value__" && // Field named 'value__' are relative to enumerations and the IL code never explicitly uses them. - !f.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) && - !f.HasAttribute("JetBrains.Annotations.UsedImplicitlyAttribute".AllowNoMatch()) && - !f.IsGeneratedByCompiler - // If you don't want to link NDepend.API.dll, you can use your own IsNotDeadCodeAttribute - // and adapt the source code of this rule. -select new { - f, - Debt = 10.ToMinutes().ToDebt(), - AnnualInterest = 8.ToMinutes().ToAnnualInterest() -} - -// -// This rule lists *potentially* **dead fields**. -// A dead field is a field that can be removed -// because it is never used by the program. -// -// By reading the source code of this rule, you'll see that by default, -// *public* fields are not matched, because such field might not be used -// by the analyzed code, but still be used by client code, not analyzed by NDepend. -// This default behavior can be easily changed. -// Some others default rules in the *Visibility* group, warn about public fields. -// -// More restrictions are applied by this rule because of some *by-design* limitations. -// NDepend mostly analyzes compiled IL code, and the information that -// an enumeration value or a literal constant (which are fields) is used -// is lost in IL code. Hence by default this rule won't match such field. -// - -// -// *Static analysis* cannot provide an *exact* list of dead fields, -// because there are several ways to assign or read a field *dynamically* -// (like through reflection). -// -// For each field matched by this query, first investigate -// if the field is used somehow (like through reflection). -// If the field is really never used, it is important to remove it -// to avoid maintaining a useless code element. -// -// If a field is used somehow, -// but still is matched by this rule, you can tag it with the attribute -// **IsNotDeadCodeAttribute** found in *NDepend.API.dll* -// to avoid matching the field again. -// You can also provide your own attribute for this need, -// but then you'll need to adapt this code rule. -// -// Issues of this rule have a **Debt** equal to 10 minutes because it only -// takes a short while to investigate if a method can be safely discarded. -// The **Annual Interest** of issues of this rule, the annual cost to not -// fix such issue, is set by default to 8 minutes per unused field matched. -//]]> - Wrong usage of IsNotDeadCodeAttribute -// ND1703:WrongUsageOfIsNotDeadCodeAttribute - -warnif count > 0 - -let tAttr = Types.WithFullName("NDepend.Attributes.IsNotDeadCodeAttribute").FirstOrDefault() -where tAttr != null - -// Get types that do a wrong usage of IsNotDeadCodeAttribute -let types = from t in Application.Types where - t.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) && - - ( // types used don't need to be tagged with IsNotDeadCodeAttribute! - t.TypesUsingMe.Count(t1 => - !t.NestedTypes.Contains(t1) && - !t1.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) ) > 0 || - - // Static types that define only const fields cannot be seen as used in IL code. - // They don't need to be tagged with IsNotDeadCodeAttribute. - (t.IsStatic && t.NbMethods == 0 && !t.Fields.Where(f => !f.IsLiteral).Any()) - ) - select t - -// Get methods that do a wrong usage of IsNotDeadCodeAttribute -let methods = from m in Application.Methods where - m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) && - m.MethodsCallingMe.Count(m1 => !m1.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch())) > 0 - select m - -// Get fields that do a wrong usage of IsNotDeadCodeAttribute -let fields = from f in Application.Fields where - f.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) && - f.MethodsUsingMe.Count(m1 => !m1.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch())) > 0 - select f - -from member in types.Cast().Concat(methods).Concat(fields) -select new { - member, - Debt = 4.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// The attribute **NDepend.Attributes.IsNotDeadCodeAttribute** -// is defined in *NDepend.API.dll*. This attribute is used -// to mean that a code element is not used directly, but is used -// somehow, like through reflection. -// -// This attribute is used in the dead code rules, -// *Potentially dead Types*, *Potentially dead Methods* -// and *Potentially dead Fields*. -// If you don't want to link *NDepend.API.dll*, you can use -// your own *IsNotDeadCodeAttribute* and adapt the source code of -// this rule, and the source code of the *dead code* rules. -// -// In this context, this code rule matches code elements -// (types, methods, fields) that are tagged with this attribute, -// but still used directly somewhere in the code. -// - -// -// Just remove *IsNotDeadCodeAttribute* tagging of -// types, methods and fields matched by this rule -// because this tag is not useful anymore. -//]]> - - - Don't use CoSetProxyBlanket and CoInitializeSecurity -// ND3100:DontUseCoSetProxyBlanketAndCoInitializeSecurity -warnif count > 0 - -from m in Application.Methods -where (m.HasAttribute ("System.Runtime.InteropServices.DllImportAttribute".AllowNoMatch())) - && m.SimpleName.EqualsAny("CoSetProxyBlanket","CoInitializeSecurity") - -select new { - m, - Debt = 1.ToHours().ToDebt(), - AnnualInterest = 2.ToHours().ToAnnualInterest() -} - -// -// As soon as some managed code starts being JIT’ed and executed by the CLR, -// it is too late to call *CoInitializeSecurity()* or *CoSetProxyBlanket()*. -// By this point in time, the CLR has already initialized the -// COM security environment for the entire Windows process. -// - -// -// Don't call CoSetProxyBlanket() or CoInitializeSecurity() from managed code. -// -// Instead write an unmanaged "shim" in C++ that will call -// one or both methods before loading the CLR within the process. -// -// More information about writing such unmanaged "shim" -// can be found in this StackOverflow answer: -// https://stackoverflow.com/a/48545055/27194 -//]]> - Don't use System.Random for security purposes -// ND3101:DontUseSystemRandomForSecurityPurposes -warnif count > 0 - -from m in Application.Methods -where m.CreateA("System.Random".AllowNoMatch()) -select new { - m, - Debt = 15.ToMinutes().ToDebt(), - AnnualInterest = 1.ToHours().ToAnnualInterest() -} - -// -// The algorithm used by the implementation of **System.Random** is weak -// because random numbers generated can be predicted. -// -// Using predictable random values in a security critical context -// can lead to vulnerabilities. -// - -// -// If the matched method is meant to be executed in a security -// critical context use **System.Security.Cryptography.RandomNumberGenerator** -// or **System.Security.Cryptography.RNGCryptoServiceProvider** instead. -// These random implementations are slower to execute but the random numbers -// generated cannot be predicted. -// -// Find more on using *RNGCryptoServiceProvider* to generate random values here: -// https://stackoverflow.com/questions/32932679/using-rngcryptoserviceprovider-to-generate-random-string -// -// Otherwise you can use the faster **System.Random** implementation and -// suppress corresponding issues. -// -// More information about the weakness of *System.Random* implementation -// can be found here: https://stackoverflow.com/a/6842191/27194 -//]]> - Don't use DES/3DES weak cipher algorithms -// ND3102:DontUseDES3DESWeakCipherAlgorithms -warnif count > 0 - -from m in Application.Methods -where - m.CreateA("System.Security.Cryptography.TripleDESCryptoServiceProvider".AllowNoMatch()) || - m.CreateA("System.Security.Cryptography.DESCryptoServiceProvider".AllowNoMatch()) || - m.IsUsing("System.Security.Cryptography.DES.Create()".AllowNoMatch()) || - m.IsUsing("System.Security.Cryptography.DES.Create(String)".AllowNoMatch()) -select new { - m, - Debt = 1.ToHours().ToDebt(), - Severity = Severity.High -} - -// -// Since 2005 the NIST, the US National Institute of Standards and Technology, -// doesn't consider DES and 3DES cypher algorithms as secure. Source: -// https://www.nist.gov/news-events/news/2005/06/nist-withdraws-outdated-data-encryption-standard -// - -// -// Use the AES (Advanced Encryption Standard) algorithms instead -// with the .NET Framework implementation: -// *System.Security.Cryptography.AesCryptoServiceProvider*. -// -// You can still suppress issues of this rule when using -// DES/3DES algorithms for compatibility reasons with legacy applications and data. -//]]> - Don't disable certificate validation -// ND3103:DontDisableCertificateValidation -warnif count > 0 - -from m in Application.Methods -where - m.IsUsing("System.Net.ServicePointManager.set_ServerCertificateValidationCallback(RemoteCertificateValidationCallback)".AllowNoMatch()) -select new { - m, - Debt = 30.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// Matched methods are subscribing a custom certificate validation -// procedure through the delegate: *ServicePointManager.ServerCertificateValidationCallback*. -// -// Doing so is often used to disable certificate validation -// to connect easily to a host that is not signed by a **root certificate authority**. -// https://en.wikipedia.org/wiki/Root_certificate -// -// This creates a **vulnerability to man-in-the-middle attacks** since the client will trust any certificate. -// https://en.wikipedia.org/wiki/Man-in-the-middle_attack -// - -// -// Don't rely on a weak custom certificate validation. -// -// If a legitimate custom certificate validation procedure must be subscribed, -// you can chose to suppress related issue(s). -//]]> - Review publicly visible event handlers -// ND3104:ReviewPubliclyVisibleEventHandlers -warnif count > 0 -from m in Application.Methods -where - m.IsPubliclyVisible - && m.Name.EndsWith("(Object,EventArgs)") -select new { - m, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Low -} - - -// -// Review publicly visible event handlers to check those that are -// running security critical actions. -// -// An event handler is any method with the standard signature *(Object,EventArgs)*. -// An event handler can be registered to any event matching this standard signature. -// -// As a consequence, such event handler can be subscribed -// by malicious code to an event to provoke execution of the -// security critical action on event firing. -// -// Even if such event handler does a security check, -// it can be executed from a chain of trusted callers on the call stack, -// and cannot detect about malicious registration. -// - -// -// Change matched event handlers to make them non-public. -// Preferably don't run a security critical action from an event handler. -// -// If after a careful check no security critical action is involved -// from a matched event-handler, you can suppress the issue. -//]]> - Pointers should not be publicly visible -// ND3105:PointersShouldNotBePubliclyVisible - -warnif count > 0 -from f in Application.Fields -where - f.FieldType != null && - f.FieldType.FullName.EqualsAny("System.IntPtr","System.UIntPtr") && - (f.IsPubliclyVisible || f.IsProtected) -let methodsUserOutsideMyAssembly = f.MethodsUsingMe.Where(m => m.ParentAssembly != m.ParentAssembly) -select new { - f, - f.FieldType, - methodsUserOutsideMyAssembly, - Debt = (15 + 10*methodsUserOutsideMyAssembly.Count()).ToMinutes().ToDebt(), - Severity = f.IsInitOnly ? Severity.Medium : Severity.High -} - -// -// Pointers should not be exposed publicly. -// -// This rule detects fields with type *System.IntPtr* or *System.UIntPtr* -// that are public or protected. -// -// Pointers are used to access unmanaged memory from managed code. -// Exposing a pointer publicly makes it easy for malicious code -// to read and write unmanaged data used by the application. -// -// The situation is even worse if the field is not read-only -// since malicious code can change it and force the application -// to rely on arbitrary data. -// - -// -// Pointers should have the visibility - private or internal. -// -// The estimated Debt, which means the effort to fix such issue, -// is 15 minutes and 10 additional minutes per method using the field outside its assembly. -// -// The estimated Severity of such issue is *Medium*, and *High* -// if the field is non read-only. -//]]> - Seal methods that satisfy non-public interfaces -// ND3106:SealMethodsThatSatisfyNonPublicInterfaces - -warnif count > 0 -from m in Application.Methods -where - !m.IsFinal && // If the method is not marked as virtual the flag final is set - m.IsPubliclyVisible && - !m.ParentType.IsSealed && - m.ParentType.IsClass -let overridenInterface = - m.OverriddensBase.FirstOrDefault(mb => mb.ParentType.IsInterface && !mb.ParentType.IsPubliclyVisible) -where overridenInterface != null -select new { - m, - overridenInterface = overridenInterface.ParentType, - Debt = 30.ToMinutes().ToDebt(), - Severity = Severity.High - } - -// -// A match of this rule represents a virtual method, publicly visible, -// defined in a non-sealed public class, that overrides a method of an -// internal interface. -// -// The interface not being public indicates a process that -// should remain private. -// -// Hence this situation represents a security vulnerability because -// it is now possible to create a malicious class, that derives from -// the parent class and that overrides the method behavior. -// This malicious behavior will be then invoked by private implementation. -// - -// -// You can: -// -// - seal the parent class, -// -// - or change the accessibility of the parent class to non-public, -// -// - or implement the method without using the *virtual* modifier, -// -// - or change the accessibility of the method to non-public. -// -// If after a careful check such situation doesn't represent -// a security threat, you can suppress the issue. -//]]> - Review commands vulnerable to SQL injection -// ND3107:ReviewCommandsVulnerableToSQLInjection - -warnif count > 0 - -let commands = ThirdParty.Types.WithFullNameIn( - "System.Data.Common.DbCommand", - "System.Data.SqlClient.SqlCommand", - "System.Data.OleDb.OleDbCommand", - "System.Data.Odbc.OdbcCommand", - "System.Data.OracleClient.OracleCommand") -where commands.Any() -let commandCtors = commands.ChildMethods().Where(m => m.IsConstructor).ToHashSetEx() -let commandExecutes = commands.ChildMethods().Where(m => m.SimpleName.Contains("Execute")).ToHashSetEx() -let commandParameters = commands.ChildMethods().Where(m => m.IsPropertyGetter && m.SimpleName == "get_Parameters").ToHashSetEx() - - -from m in Application.Methods.UsingAny(commandCtors) - .UsingAny(commandExecutes) -where !m.MethodsCalled.Intersect(commandParameters).Any() && - // Check also that non of the method call rely on command parameters to get less false positives: - !m.MethodsCalled.SelectMany(mc => mc.MethodsCalled).Intersect(commandParameters).Any() - -select new { m, - createA = m.MethodsCalled.Intersect(commandCtors).First().ParentType, - calls = m.MethodsCalled.Intersect(commandExecutes).First(), - Debt = 15.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule matches methods that create a DB command -// (like an *SqlCommand* or an *OleDbCommand*) -// that call an *Execute* command method (like *ExecuteScalar()*) -// and that don't use command parameters. -// -// This situation is prone to **SQL Injection** https://en.wikipedia.org/wiki/SQL_injection -// since malicious SQL code might be injected in string parameters values. -// -// However there might be false positives. -// So review carefully matched methods -// and use suppress issues when needed. -// -// To limit the false positives, this rule also checks whether -// command parameters are accessed from any sub method call. -// This is a solid indication of non-vulnerability. -// - -// -// If after a careful check it appears that the method is indeed -// using some strings to inject parameters values in the SQL query string, -// **command.Parameters.Add(...)** must be used instead. -// -// You can get more information on adding parameters explicitely here: -// https://stackoverflow.com/questions/4892166/how-does-sqlparameter-prevent-sql-injection -// ]]> - Review data adapters vulnerable to SQL injection -// ND3108:ReviewDataAdaptersVulnerableToSQLInjection - -warnif count > 0 - -let adapters = ThirdParty.Types.WithFullNameIn( - "System.Data.Common.DataAdapter", - "System.Data.Common.DbDataAdapter", - "System.Data.SqlClient.SqlDataAdapter", - "System.Data.OleDb.OleDbDataAdapter", - "System.Data.Odbc.OdbcDataAdapter", - "System.Data.OracleClient.OracleDataAdapter") - -where adapters.Any() -let adapterCtors = adapters.ChildMethods().Where(m => m.IsConstructor).ToHashSetEx() -let adapterFill = adapters.ChildMethods().Where(m => m.SimpleName.Contains("Fill")).ToHashSetEx() - - -let commands = ThirdParty.Types.WithFullNameIn( - "System.Data.Common.DbCommand", - "System.Data.SqlClient.SqlCommand", - "System.Data.OleDb.OleDbCommand", - "System.Data.Odbc.OdbcCommand", - "System.Data.OracleClient.OracleCommand") -where commands.Any() -let commandParameters = commands.ChildMethods().Where(m => m.IsPropertyGetter && m.SimpleName == "get_Parameters").ToHashSetEx() - - -from m in Application.Methods.UsingAny(adapterCtors) - .UsingAny(adapterFill) -where !m.MethodsCalled.Intersect(commandParameters).Any() && - // Check also that non of the method call rely on command parameters to get less false positives: - !m.MethodsCalled.SelectMany(mc => mc.MethodsCalled).Intersect(commandParameters).Any() - -select new { m, - createA = m.MethodsCalled.Intersect(adapterCtors).First().ParentType, - calls = m.MethodsCalled.Intersect(adapterFill).First(), - Debt = 15.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule matches methods that create a DB adapter -// (like an *SqlDataAdapter* or an *OleDbDataAdapter*) -// that call the method *Fill()* -// and that don't use DB command parameters. -// -// This situation is prone to **SQL Injection** https://en.wikipedia.org/wiki/SQL_injection -// since malicious SQL code might be injected in string parameters values. -// -// However there might be false positives. -// So review carefully matched methods -// and use suppress issues when needed. -// -// To limit the false positives, this rule also checks whether -// command parameters are accessed from any sub method call. -// This is a solid indication of non-vulnerability. -// - -// -// If after a careful check it appears that the method is indeed -// using some strings to inject parameters values in the SQL query string, -// **adapter.SelectCommand.Parameters.Add(...)** must be used instead -// (or *adapter.UpdateCommand* or *adapter.InsertCommand*, depending on the context). -// -// You can get more information on adding parameters explicitely here: -// https://stackoverflow.com/questions/4892166/how-does-sqlparameter-prevent-sql-injection -// ]]> - - - Methods that could have a lower visibility -// ND1800:MethodsThatCouldHaveALowerVisibility - -warnif count > 0 from m in JustMyCode.Methods where - m.Visibility != m.OptimalVisibility && - - !m.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) && - !m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) && - !m.HasAttribute("JetBrains.Annotations.UsedImplicitlyAttribute".AllowNoMatch()) && - // If you don't want to link NDepend.API.dll, you can use your own attributes - // and adapt the source code of this rule. - - // methods of serialized type must remain public. - !m.ParentType.TypesUsed.Any(t1 => t1.IsAttributeClass && t1.ParentNamespace.Name == "Newtonsoft.Json") && - !m.ParentType.HasAttribute("System.Runtime.Serialization.DataContractAttribute".AllowNoMatch()) && - !m.ParentType.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) && - - // Eliminate public methods visible outside of their assembly - // because the rule cannot know if the developer left the method public - // intentionally or not. - !m.IsPubliclyVisible && - - // Avoid matching public methods declared in a non-public type, - // that could have the visibility internal, because - // such situation is caught by the rule 'Avoid public methods not publicly visible'. - !(m.Visibility == Visibility.Public && - m.ParentType.Visibility != Visibility.Public && - m.OptimalVisibility == Visibility.Internal) && - - // Eliminate default constructor from the result. - // Whatever the visibility of the declaring class, - // default constructors are public and introduce noise - // in the current rule. - !( m.IsConstructor && m.IsPublic && m.NbParameters == 0) && - - // Don't advise to reduce visibility of property getters/setters - // of classes that implement INotifyPropertyChanged - !((m.IsPropertyGetter || m.IsPropertySetter) && - m.ParentType.Implement("System.ComponentModel.INotifyPropertyChanged".AllowNoMatch())) && - - // Don't decrease the visibility of Main() methods. - !m.IsEntryPoint - -select new { - m, - m.Visibility , - CouldBeDeclared = m.OptimalVisibility, - m.MethodsCallingMe, - - Debt = 30.ToSeconds().ToDebt(), // It is fast to change the method visibility - Severity = Severity.Medium -} - -// -// This rule warns about methods that can be declared with a lower visibility -// without provoking any compilation error. -// For example *private* is a visibility lower than *internal* -// which is lower than *public*. -// -// **Narrowing visibility** is a good practice because doing so **promotes encapsulation**. -// The scope from which methods can be called is then reduced to a minimum. -// -// By default, this rule doesn't match publicly visible methods that could have a -// lower visibility because it cannot know if the developer left the method public -// intentionally or not. Public methods matched are declared in non-public types. -// -// By default this rule doesn't match methods with the visibility *public* -// that could be *internal*, declared in a type that is not *public* -// (internal, or nested private for example) because -// this situation is caught by the rule *Avoid public methods not publicly visible*. -// -// Notice that methods tagged with one of the attribute -// *NDepend.Attributes.CannotDecreaseVisibilityAttribute* or -// *NDepend.Attributes.IsNotDeadCodeAttribute*, found in *NDepend.API.dll* -// are not matched. If you don't want to link *NDepend.API.dll* but still -// wish to rely on this facility, you can declare these attributes in your code. -// - -// -// Declare each matched method with the specified *optimal visibility* -// in the *CouldBeDeclared* rule result column. -// -// By default, this rule matches *public methods*. If you are publishing an API -// many public methods matched should remain public. In such situation, -// you can opt for the *coarse solution* to this problem by adding in the -// rule source code *&& !m.IsPubliclyVisible* or you can prefer the -// *finer solution* by tagging each concerned method with -// *CannotDecreaseVisibilityAttribute*. -//]]> - Types that could have a lower visibility -// ND1801:TypesThatCouldHaveALowerVisibility - -warnif count > 0 from t in JustMyCode.Types where - - t.Visibility != t.OptimalVisibility && - - // If you don't want to link NDepend.API.dll, you can use your own attributes - // and adapt the source code of this rule. - !t.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) && - !t.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) && - !t.HasAttribute("JetBrains.Annotations.UsedImplicitlyAttribute".AllowNoMatch()) && - - // JSON and XML serialized type must remain public. - !t.IsUsing("Newtonsoft.Json".MatchNamespace().AllowNoMatch()) && - !t.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) && - - // Eliminate public types visible outside of their assembly - // because the rule cannot know if the developer left the type public - // intentionally or not. - !t.IsPubliclyVisible && - - // Static types that define only const fields cannot be seen as used in IL code. - // They don't have to be tagged with CannotDecreaseVisibilityAttribute. - !( t.IsStatic && - !t.Methods.Any(m => !m.IsClassConstructor) && - !t.Fields.Any(f => !f.IsLiteral && !(f.IsStatic && f.IsInitOnly))) && - - // A type used by an interface that has the same visibility - // cannot have its visibility decreased, else a compilation error occurs! - !t.TypesUsingMe.Any(tUser => - tUser.IsInterface && - tUser.Visibility == t.Visibility) && - - // Don't change the visibility of a type that contain an entry point method. - !t.Methods.Any(m =>m.IsEntryPoint) - -select new { - t, - t.Visibility , - CouldBeDeclared = t.OptimalVisibility, - t.TypesUsingMe, - - Debt = 30.ToSeconds().ToDebt(), // It is fast to change the method visibility - Severity = Severity.Medium -} - -// -// This rule warns about types that can be declared with a lower visibility -// without provoking any compilation error. -// For example *private* is a visibility lower than *internal* -// which is lower than *public*. -// -// **Narrowing visibility** is a good practice because doing so **promotes encapsulation**. -// The scope from which types can be consumed is then reduced to a minimum. -// -// By default, this rule doesn't match publicly visible types that could have -// a lower visibility because it cannot know if the developer left the type public -// intentionally or not. Public types matched are nested in non-public types. -// -// Notice that types tagged with one of the attribute -// *NDepend.Attributes.CannotDecreaseVisibilityAttribute* or -// *NDepend.Attributes.IsNotDeadCodeAttribute*, found in *NDepend.API.dll* -// are not matched. If you don't want to link *NDepend.API.dll* but still -// wish to rely on this facility, you can declare these attributes in your code. -// - -// -// Declare each matched type with the specified *optimal visibility* -// in the *CouldBeDeclared* rule result column. -// -// By default, this rule matches *public types*. If you are publishing an API -// many public types matched should remain public. In such situation, -// you can opt for the *coarse solution* to this problem by adding in the -// rule source code *&& !m.IsPubliclyVisible* or you can prefer the -// *finer solution* by tagging each concerned type with -// *CannotDecreaseVisibilityAttribute*. -//]]> - Fields that could have a lower visibility -// ND1802:FieldsThatCouldHaveALowerVisibility - -warnif count > 0 from f in JustMyCode.Fields where - f.Visibility != f.OptimalVisibility && - !f.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) && - !f.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) && - !f.HasAttribute("JetBrains.Annotations.UsedImplicitlyAttribute".AllowNoMatch()) && - // If you don't want to link NDepend.API.dll, you can use your own attributes - // and adapt the source code of this rule. - - // JSON and XML serialized fields must remain public. - !f.ParentType.TypesUsed.Any(t1 => t1.IsAttributeClass && t1.ParentNamespace.Name == "Newtonsoft.Json") && - !f.HasAttribute("System.Xml.Serialization.XmlElementAttribute".AllowNoMatch()) && - !f.HasAttribute("System.Xml.Serialization.XmlAttributeAttribute".AllowNoMatch()) && - !f.HasAttribute("System.Xml.Serialization.XmlArrayAttribute".AllowNoMatch()) && - !f.HasAttribute("System.Xml.Serialization.XmlArrayItemAttribute".AllowNoMatch()) && - - // Don't check for serialized fields visibility - !f.HasAttribute("System.Runtime.Serialization.DataMemberAttribute".AllowNoMatch()) && - - // Eliminate public fields visible outside of their assembly - // because the rule cannot know if the developer left the field public - // intentionally or not. - !f.IsPubliclyVisible - -select new { - f, - f.Visibility , - CouldBeDeclared = f.OptimalVisibility, - f.MethodsUsingMe, - - Debt = 30.ToSeconds().ToDebt(), // It is fast to change the field visibility - Severity = Severity.Medium -} - -// -// This rule warns about fields that can be declared with a lower visibility -// without provoking any compilation error. -// For example *private* is a visibility lower than *internal* -// which is lower than *public*. -// -// **Narrowing visibility** is a good practice because doing so **promotes encapsulation**. -// The scope from which fields can be consumed is then reduced to a minimum. -// -// By default, this rule doesn't match publicly visible fields that could have a -// lower visibility because it cannot know if the developer left the field public -// intentionally or not. Public fields matched are declared in non-public types. -// -// Notice that fields tagged with one of the attribute -// *NDepend.Attributes.CannotDecreaseVisibilityAttribute* or -// *NDepend.Attributes.IsNotDeadCodeAttribute*, found in *NDepend.API.dll* -// are not matched. If you don't want to link *NDepend.API.dll* but still -// wish to rely on this facility, you can declare these attributes in your code. -// - -// -// Declare each matched field with the specified *optimal visibility* -// in the *CouldBeDeclared* rule result column. -// -// By default, this rule matches *public fields*. If you are publishing an API -// some public fields matched should remain public. In such situation, -// you can opt for the *coarse solution* to this problem by adding in the -// rule source code *&& !m.IsPubliclyVisible* or you can prefer the -// *finer solution* by tagging eah concerned field with -// *CannotDecreaseVisibilityAttribute*. -//]]> - Types that could be declared as private, nested in a parent type -// ND1803:TypesThatCouldBeDeclaredAsPrivateNestedInAParentType - -warnif count > 0 -from t in JustMyCode.Types -where !t.IsGeneratedByCompiler && - !t.IsNested && - !t.IsPubliclyVisible && - !t.IsEnumeration && - // Only one type user… - t.TypesUsingMe.Count() == 1 - -let couldBeNestedIn = t.TypesUsingMe.Single() -where !couldBeNestedIn.IsGeneratedByCompiler && - !couldBeNestedIn.IsInterface && // Cannot nest a type in an interface - // …declared in the same namespace - couldBeNestedIn.ParentNamespace == t.ParentNamespace && - - // Don't advise to move a base class - // or an interface into one of its child type. - !couldBeNestedIn.DeriveFrom(t) && - !couldBeNestedIn.Implement(t) - - // Require that t doesn't contain any extension method. - // Types with extension methods cannot be nested. -where t.Methods.All(m => !m.IsExtensionMethod) - -select new { - t, - couldBeNestedIn, - Debt = 3.ToMinutes().ToDebt(), // It is fast to nest a type into another one - Severity = Severity.Low // This rule proposes advices, not potential problems -} - -// -// This rule matches types that can be potentially -// *nested* and declared *private* into another type. -// -// The conditions for a type to be potentially nested -// into a *parent type* are: -// -// • the *parent type* is the only type consuming it, -// -// • the type and the *parent type* are declared in the same namespace. -// -// Declaring a type as private into a parent type **promotes encapsulation**. -// The scope from which the type can be consumed is then reduced to a minimum. -// -// This rule doesn't match classes with extension methods -// because such class cannot be nested in another type. -// - -// -// Nest each matched *type* into the specified *parent type* and -// declare it as private. -// -// However *nested private types* are hardly testable. Hence this rule -// might not be applied to types consumed directly by tests. -//]]> - Avoid publicly visible constant fields -// ND1804:AvoidPubliclyVisibleConstantFields - -warnif count > 0 -from f in JustMyCode.Fields -where f.IsLiteral && - f.IsPubliclyVisible && - !f.IsEnumValue -select new { - f, - - Debt = 30.ToSeconds().ToDebt(), // It is fast to update field declaration - Severity = Severity.Medium -} - -// -// This rule warns about constant fields that are visible outside their -// parent assembly. Such field, when used from outside its parent assembly, -// has its constant value *hard-coded* into the client assembly. -// Hence, when changing the field's value, it is *mandatory* to recompile -// all assemblies that consume the field, else the program will run -// with different constant values in-memory. Certainly in such situation -// bugs are lurking. -// - -// -// Declare matched fields as **static readonly** instead of **constant**. -// This way, the field value is *safely changeable* without the need to -// recompile client assemblies. -// -// Notice that enumeration value fields suffer from the same *potential -// pitfall*. But enumeration values cannot be declared as -// *static readonly* hence the rule comes with the condition -// **&& !f.IsEnumValue** to avoid matching these. Unless you decide -// to banish public enumerations, just let the rule *as is*. -//]]> - Fields should be declared as private -// ND1805:FieldsShouldBeDeclaredAsPrivate - -warnif count > 0 from f in JustMyCode.Fields where - !f.IsPrivate && - - // These conditions filter cases where fields - // doesn't represent state that should be encapsulated. - !f.IsGeneratedByCompiler && - !f.IsSpecialName && - !f.IsInitOnly && - !f.IsLiteral && - !f.IsEnumValue && - !f.HasAttribute("System.Xml.Serialization.XmlAttributeAttribute".AllowNoMatch()) && - !f.HasAttribute("System.Runtime.Serialization.DataMemberAttribute".AllowNoMatch()) - -// A non-private field assigned from outside its class, -// usually leads to complicated field state management. -let outsideMethodsAssigningMe = - f.MethodsAssigningMe.Where(m => m.ParentType != f.ParentType) - -select new { - f, - f.Visibility, - outsideMethodsAssigningMe, - - Debt = (60+20*outsideMethodsAssigningMe.Count()).ToSeconds().ToDebt(), - // The cost to leave such issue unfixed is higher if the field is publicly visible! - AnnualInterest = Severity.Medium.AnnualInterestThreshold() * (f.IsPubliclyVisible ? 3 : 1) -} - -// -// This rule matches **non-private and mutable fields**. -// *Mutable* means that the field value can be modified. -// Typically mutable fields are *non-constant*, -// *non-readonly* fields. -// -// Fields should be considered as **implementation details** -// and as a consequence they should be declared as private. -// -// If something goes wrong with a *non-private field*, -// the culprit can be anywhere, and so in order to track down -// the bug, you may have to look at quite a lot of code. -// -// A private field, by contrast, can only be assigned from -// inside the same class, so if something goes wrong with that, -// there is usually only one source file to look at. -// -// Issues of this rule are fast to get fixed, and they have -// a debt proportional to the number of methods assigning -// the field. -// - -// -// Declare a matched mutable field as *private*, or declare it -// as *readonly*. -// -// If code outside the type needs to access the field -// you can encapsulate the field accesses in a read-write property. -// At least with a read-write property you can set a debug breakpoint -// on the property setter, which makes easier to track write-accesses -// in case of problem. -// -]]> - Constructors of abstract classes should be declared as protected or private -// ND1806:ConstructorsOfAbstractClassesShouldBeDeclaredAsProtectedOrPrivate - -warnif count > 0 -from t in Application.Types where - t.IsClass && - t.IsAbstract -let ctors = t.Constructors.Where(c => !c.IsProtected && !c.IsPrivate) -where ctors.Count() > 0 -select new { - t, - ctors, - - Debt = 30.ToSeconds().ToDebt(), - Severity = Severity.Medium -} - -// -// Constructors of abstract classes can only be called from derived -// classes. -// -// Because a public constructor is creating instances of its class, -// and because it is forbidden to create instances of an *abstract* class, -// an abstract class with a public constructor is wrong design. -// -// Notice that when the constructor of an abstract class is private, -// it means that derived classes must be nested in the abstract class. -// - -// -// To fix a violation of this rule, -// either declare the constructor as *protected*, -// or do not declare the type as *abstract*. -//]]> - Avoid public methods not publicly visible -// ND1807:AvoidPublicMethodsNotPubliclyVisible - -warnif count > 0 -from m in JustMyCode.Methods where - !m.IsPubliclyVisible && m.IsPublic && - - // Eliminate virtual methods - !m.IsVirtual && - // Eliminate interface and delegate types - !m.ParentType.IsInterface && - !m.ParentType.IsDelegate && - // Eliminate default constructors - !(m.IsConstructor && m.NbParameters == 0) && - // Eliminate operators that must be declared public - !m.IsOperator && - // Eliminate methods generated by compiler, except auto-property getter/setter - (!m.IsGeneratedByCompiler || m.IsPropertyGetter || m.IsPropertySetter) && - - // Don't advise to reduce visibility of property getters/setters - // of classes that implement INotifyPropertyChanged - !((m.IsPropertyGetter || m.IsPropertySetter) && - m.ParentType.Implement("System.ComponentModel.INotifyPropertyChanged".AllowNoMatch())) - -let calledOutsideParentType = - m.MethodsCallingMe.FirstOrDefault(mCaller => mCaller.ParentType != m.ParentType) != null - -select new { - m, - parentTypeVisibility = m.ParentType.Visibility, - declareMethodAs = (Visibility) (calledOutsideParentType ? Visibility.Internal : Visibility.Private), - methodsCaller = m.MethodsCallingMe, - - Debt = 30.ToSeconds().ToDebt(), - Severity = Severity.Low -} - -// -// This rule warns about methods declared as *public* -// whose parent type is not declared as *public*. -// -// In such situation *public* means, *can be accessed -// from anywhere my parent type is visible*. Some -// developers think this is an elegant language construct, -// some others find it misleading. -// -// This rule can be deactivated if you don't agree with it. -// Read the whole debate here: -// http://ericlippert.com/2014/09/15/internal-or-public/ -// -// By default issues of this rule have a **Low** severity -// because they reflect more an advice than a problem. -// - -// -// Declare the method as *internal* if it is used outside of -// its type, else declare it as *private*. -//]]> - Event handler methods should be declared as private or protected -// ND1808:EventHandlerMethodsShouldBeDeclaredAsPrivateOrProtected - -warnif count > 0 -from m in Application.Methods where - !(m.IsPrivate || m.IsProtected) && - !m.IsGeneratedByCompiler && - - // A method is considered as an event handler if… - m.NbParameters == 2 && // … it has two parameters … - m.Name.Contains("Object") && // … of types Object … - m.Name.Contains("EventArgs") && // … and EventArgs - - // Discard special cases - !m.ParentType.IsDelegate && - !m.IsGeneratedByCompiler - -select new { - m, - m.Visibility, - Debt = 2.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// Think of a event handler like for example *OnClickButton()*. -// Typically such method must be declared as *private* -// and shouldn't be called in other context than event firing. -// -// Such method can also be declared as *protected* because -// some designers such as the ASP.NET designer, generates such method -// as *protected* to let a chance to sub-classes to call it. -// - -// -// If you have the need that event handler method should be called -// from another class, then find a code structure that more -// closely matches the concept of what you're trying to do. -// Certainly you don't want the other class to click a button; you -// want the other class to do something that clicking a button -// also do. -//]]> - Wrong usage of CannotDecreaseVisibilityAttribute -// ND1809:WrongUsageOfCannotDecreaseVisibilityAttribute - -warnif count > 0 - -let tAttr = Types.WithFullName("NDepend.Attributes.CannotDecreaseVisibilityAttribute").FirstOrDefault() -where tAttr != null - -// Get types that do a wrong usage of CannotDecreaseVisibilityAttribute -let types = from t in Application.Types where - t.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) && - ( t.Visibility == t.OptimalVisibility || - - // Static types that define only const fields cannot be seen as used in IL code. - // They don't need to be tagged with CannotDecreaseVisibilityAttribute. - (t.IsStatic && t.NbMethods == 0 && !t.Fields.Any(f => !f.IsLiteral)) - ) - select t - -// Get methods that do a wrong usage of CannotDecreaseVisibilityAttribute -let methods = from m in Application.Methods where - m.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) && - m.Visibility == m.OptimalVisibility - select m - -// Get fields that do a wrong usage of CannotDecreaseVisibilityAttribute -let fields = from f in Application.Fields where - f.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) && - f.Visibility == f.OptimalVisibility - select f - -from member in types.Cast().Concat(methods).Concat(fields) -select new { - member, - Debt = 30.ToSeconds().ToDebt(), - Severity = Severity.Low -} - -// -// The attribute **NDepend.Attributes.CannotDecreaseVisibilityAttribute** -// is defined in *NDepend.API.dll*. If you don't want to reference -// *NDepend.API.dll* you can declare it in your code. -// -// Usage of this attribute means that a code element visibility is not -// optimal (it can be lowered like for example from *public* to *internal*) -// but shouldn’t be modified anyway. Typical reasons to do so include: -// -// • Public code elements consumed through reflection, through a mocking -// framework, through XML or binary serialization, through designer, -// COM interfaces… -// -// • Non-private code element invoked by test code, that would be difficult -// to reach from test if it was declared as *private*. -// -// In such situation *CannotDecreaseVisibilityAttribute* is used to avoid -// that default rules about not-optimal visibility warn. Using this -// attribute can be seen as an extra burden, but it can also be seen as -// an opportunity to express in code: **Don't change the visibility else -// something will be broken** -// -// In this context, this code rule matches code elements -// (types, methods, fields) that are tagged with this attribute, -// but still already have an optimal visibility. -// - -// -// Just remove *CannotDecreaseVisibilityAttribute* tagging of -// types, methods and fields matched by this rule -// because this tag is not useful anymore. -// -// By default issues of this rule have a **Low** severity -// because they reflect more an advice than a problem. -//]]> - Methods that should be declared as 'public' in C#, 'Public' in VB.NET - -from m in Application.Methods where - m.ShouldBePublic -let usedInAssemblies = m.MethodsCallingMe.ParentAssemblies().Except(m.ParentAssembly) -select new { - m, - m.ParentAssembly, - usedInAssemblies, - m.MethodsCallingMe -} - -// -// This code query lists methods that *should* be declared -// as *public*. Such method is actually declared as *internal* -// and is consumed from outside its parent assembly -// thanks to the attribute -// *System.Runtime.CompilerServices.InternalsVisibleToAttribute*. -// -// This query relies on the property -// *NDepend.CodeModel.IMember.ShouldBePublic* -// https://www.ndepend.com/api/webframe.html?NDepend.API~NDepend.CodeModel.IMember~ShouldBePublic.html -// -// This is just a code query, it is not intended to advise -// you to declare the method as *public*, but to inform you -// that the code actually relies on the peculiar behavior -// of the attribute *InternalsVisibleToAttribute*. -//]]> - - - Fields should be marked as ReadOnly when possible -// ND1900:FieldsShouldBeMarkedAsReadOnlyWhenPossible - -warnif count > 0 -from f in JustMyCode.Fields where - f.IsImmutable && - !f.IsInitOnly && // The condition IsInitOnly matches fields that - // are marked with the C# readonly keyword - // (ReadOnly in VB.NET). - !f.IsGeneratedByCompiler && - !f.IsEventDelegateObject && - !f.ParentType.IsEnumeration && - !f.IsLiteral && - - // Don't warn if a method using the field is also calling a method that has 'ref' and 'out' parameters. - // This could lead to false positive. A field used in a 'ref' or 'out' parameter cannot be set as read-only. - f.MethodsUsingMe.SelectMany(m => m.MethodsCalled).FirstOrDefault(m => m.Name.Contains("&")) == null - -select new { - f, - f.MethodsReadingMeButNotAssigningMe, - f.MethodsAssigningMe, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns about instance and static fields that -// can be declared as **readonly**. -// -// This source code of this rule is based on the conditon -// *IField.IsImmutable*. -// https://www.ndepend.com/api/webframe.html?NDepend.API~NDepend.CodeModel.IField~IsImmutable.html -// -// A field that matches the condition *IsImmutable* -// is a field that is assigned only by constructors -// of its class. -// -// For an *instance field*, this means its value -// will remain constant through the lifetime -// of the object. -// -// For a *static field*, this means its value will -// remain constant through the lifetime of the -// program. -// - -// -// Declare the field with the C# *readonly* keyword -// (*ReadOnly* in VB.NET). This way the intention -// that the field value shouldn't change is made -// explicit. -//]]> - Avoid non-readonly static fields -// ND1901:AvoidNonReadOnlyStaticFields - -warnif count > 0 -from f in Application.Fields -where f.IsStatic && - !f.IsEnumValue && - !f.IsGeneratedByCompiler && - !f.IsLiteral && - !f.IsInitOnly - -let methodAssigningField = f.MethodsAssigningMe - -select new { - f, - methodAssigningField, - Debt = (2+8*methodAssigningField.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule warns about static fields that are not -// declared as read-only. -// -// In *Object-Oriented-Programming* the natural artifact -// to hold states that can be modified is **instance fields**. -// Such mutable static fields create *confusion* about -// the expected state at runtime and impairs the code -// testability since the same mutable state is re-used for -// each test. -// -// More discussion on the topic can be found here: -// http://codebetter.com/patricksmacchia/2011/05/04/back-to-basics-usage-of-static-members/ -// - -// -// If the *static* field is just assigned once in the program -// lifetime, make sure to declare it as *readonly* and assign -// it inline, or from the static constructor. -// -// Else if methods other than the static constructor need to -// assign the state hold by the static field, refactoring must -// occur to ensure that this state is hold through an instance -// field. -//]]> - Avoid static fields with a mutable field type -// ND1902:AvoidStaticFieldsWithAMutableFieldType - -warnif count > 0 -from f in Application.Fields -where f.IsStatic && - !f.IsEnumValue && - !f.IsGeneratedByCompiler && - !f.IsLiteral - -let fieldType = f.FieldType -where fieldType != null && - !fieldType.IsThirdParty && - !fieldType.IsInterface && - !fieldType.IsImmutable - -select new { - f, - mutableFieldType = fieldType , - isFieldImmutable = f.IsImmutable ? "Immutable" : "Mutable", - isFieldReadOnly = f.IsInitOnly ? "ReadOnly" : "Not ReadOnly", - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns about static fields whose field type -// is mutable. In such case the static field is -// holding a state that can be modified. -// -// In *Object-Oriented-Programming* the natural artifact -// to hold states that can be modified is **instance fields**. -// Hence such static fields create *confusion* about -// the expected state at runtime. -// -// More discussion on the topic can be found here: -// http://codebetter.com/patricksmacchia/2011/05/04/back-to-basics-usage-of-static-members/ -// - -// -// To fix violations of this rule, make sure to -// hold mutable states through objects that are passed -// **explicitly** everywhere they need to be consumed, in -// opposition to mutable object hold by a static field that -// makes it modifiable from a bit everywhere in the program. -//]]> - Structures should be immutable -// ND1903:StructuresShouldBeImmutable - -warnif count > 0 from t in JustMyCode.Types where - t.IsStructure && - !t.IsImmutable - -let mutableFields = t.Fields.Where(f => !f.IsImmutable) - -select new { - t, - t.NbLinesOfCode, - mutableFields, - Debt = (3+2*mutableFields.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// An object is immutable if its state doesn’t change once the -// object has been created. Consequently, a structure or a class -// is immutable if its instances fields are only assigned inline -// or from constructor(s). -// -// But for structure it is a bit different. **Structures are value -// types** which means instances of structures are copied when they are -// passed around (like through a method argument). -// -// So if you change a copy you are changing only that copy, not the -// original and not any other copies which might be around. Such -// situation is very different than what happen with instances of -// classes. Hence developers are not used to work with modified values -// and doing so introduces *confusion* and is *error-prone*. -// - -// -// Make sure matched structures are immutable. This way, all -// automatic copies of an original instance, resulting from being -// *passed by value* will hold the same values and there will be -// no surprises. -// -// If your structure is immutable then if you want to change -// a value, you have to consciously do it by creating a new instance -// of the structure with the modified data. -//]]> - Property Getters should be immutable -// ND1904:PropertyGettersShouldBeImmutable - -warnif count > 0 from m in Application.Methods where - !m.IsGeneratedByCompiler && - m.IsPropertyGetter && - ( ( !m.IsStatic && m.ChangesObjectState) || - ( m.IsStatic && m.ChangesTypeState) ) - -let propertyName = m.SimpleName.Substring(4,m.SimpleName.Length-4) -let setterSimpleName = "set_" + propertyName - - -let fieldsAssigned = m.FieldsAssigned.Where(f => - - // Don't count field that have a name similar to the property name - // to avoid matching lazy initialization situations. - !(propertyName.Length >= 4 && - f.SimpleName.Length >= 4 && - // Don't count the first 3 characters of the field name, - // to avoid special field name formatting like 'm_X' or '_x' - propertyName.EndsWith(f.SimpleName.Substring(3, f.SimpleName.Length -3))) - && - - // Don't count field that are assigned only by the property getter and the related property setter. - f.MethodsAssigningMe.Any(m1 => m1 != m && - !(m1.IsPropertySetter && m1.SimpleName == setterSimpleName))) -where fieldsAssigned.Any() -let otherMethodsAssigningSameFields = fieldsAssigned.SelectMany(f => f.MethodsAssigningMe.Where(m1 => m1 != m)) - -select new { - m, - m.NbLinesOfCode, - fieldsAssigned, - otherMethodsAssigningSameFields, - Debt = (2 + 5*fieldsAssigned.Count() + 5*otherMethodsAssigningSameFields.Count() ).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// It is not expected that a state gets modified when -// accessing a property getter. Hence doing so create -// confusion and property getters should be pure methods, -// they shouldn't assign any field. -// -// This rule doesn't match property getters that assign a field -// not assigned by any other methods than the getter itself -// and the corresponding property setter. Hence this rule avoids -// matching *lazy initialization at first access* of a state. -// In such situation the getter assigns a field at first access -// and from the client point of view, lazy initialization -// is an invisible implementation detail. -// -// A field assigned by a property with a name similar to the -// property name are not count either, also to avoid matching -// *lazy initialization at first access* situations. -// - -// -// Make sure that matched property getters don't assign any -// field. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 2 minutes plus 5 minutes per field assigned and -// 5 minutes per other method assigning such field. -//]]> - A field must not be assigned from outside its parent hierarchy types -// ND1905:AFieldMustNotBeAssignedFromOutsideItsParentHierarchyTypes - -warnif count > 0 -from f in JustMyCode.Fields.Where(f => - (f.IsInternal || f.IsPublic) && - !f.IsGeneratedByCompiler && - !f.IsImmutable && - !f.IsEnumValue) - -let methodsAssignerOutsideOfMyType = f.MethodsAssigningMe.Where( - m =>!m.IsGeneratedByCompiler && - m.ParentType != f.ParentType && - !m.ParentType.DeriveFrom(f.ParentType) ) - -where methodsAssignerOutsideOfMyType.Any() - -select new { - f, - methodsAssignerOutsideOfMyType, - Debt = (5*methodsAssignerOutsideOfMyType.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is related to the rule *Fields should be declared as -// private*. It matches any **public or internal, mutable field** -// that is assigned from outside its parent class and subclasses. -// -// Fields should be considered as **implementation details** -// and as a consequence they should be declared as private. -// -// If something goes wrong with a *non-private field*, -// the culprit can be anywhere, and so in order to track down -// the bug, you may have to look at quite a lot of code. -// -// A private field, by contrast, can only be assigned from -// inside the same class, so if something goes wrong with that, -// there is usually only one source file to look at. -// - -// -// Matched fields must be declared as *protected* and even better -// as *private*. -// -// Alternatively, if the field can reference immutable states, -// it can remain visible from the outside, but then must be -// declared as *readonly*. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 5 minutes per method outside the parent hierarchy -// that assigns the matched field. -//]]> - Don't assign a field from many methods -// ND1906:DontAssignAFieldFromManyMethods - -warnif count > 0 -from f in JustMyCode.Fields where - !f.IsEnumValue && - !f.IsImmutable && - !f.IsInitOnly && - !f.IsGeneratedByCompiler && - !f.IsEventDelegateObject - -let methodsAssigningMe = f.MethodsAssigningMe.Where(m => !m.IsConstructor) - -// The threshold 4 is arbitrary and it should avoid matching too many fields. -// Threshold is even lower for static fields because this reveals situations even more complex. -where methodsAssigningMe.Count() >= (!f.IsStatic ? 4 : 2) - -select new { - f, - methodsAssigningMe, - f.MethodsReadingMeButNotAssigningMe, - f.MethodsUsingMe, - Debt = (4+(f.IsStatic ? 10 : 5)).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// A field assigned from many methods is a symptom of **bug-prone code**. -// Notice that: -// -// • For an instance field, constructor(s) of its class that assign the field are not counted. -// -// • For a static field, the class constructor that assigns the field is not counted. -// -// The default threshold for *instance fields* is equal to *4 or more than 4 methods -// assigning the instance field*. Such situation makes harder to anticipate the -// field state at runtime. The code is then complicated to read, hard to debug -// and hard to maintain. Hard-to-solve bugs due to corrupted state are often the -// consequence of fields *anarchically assigned*. -// -// The situation is even more complicated if the field is *static*. -// Indeed, such situation potentially involves global random accesses from -// various parts of the application. This is why this rule provides a lower -// threshold equals to *2 or more than 2 methods assigning the static field*. -// -// If the object containing such field is meant to be used from multiple threads, -// there are **alarming chances** that the code is unmaintainable and bugged. -// When multiple threads are involved, the rule of thumb is to use immutable objects. -// -// If the field type is a reference type (interfaces, classes, strings, delegates) -// corrupted state might result in a *NullReferenceException*. -// If the field type is a value type (number, boolean, structure) -// corrupted state might result in wrong result not even signaled by an exception -// thrown. -// - -// -// There is no straight advice to refactor the number of methods responsible -// for assigning a field. Sometime the situation is simple enough, like when -// a field that hold an indentation state is assigned by many writer methods. -// Such situation only requires to define two methods *IndentPlus()/IndentMinus()* -// that assign the field, called from all writers methods. -// -// Sometime the solution involves rethinking and then rewriting -// a complex algorithm. Such field can sometime become just a variable accessed -// locally by a method or a *closure*. Sometime, just rethinking the life-time -// and the role of the parent object allows the field to become immutable -// (i.e assigned only by the constructor). -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 4 minutes plus 5 minutes per method assigning the instance field -// or 10 minutes per method assigning the static field. -//]]> - Do not declare read only mutable reference types -// ND1907:DoNotDeclareReadOnlyMutableReferenceTypes - -warnif count > 0 -from f in JustMyCode.Fields where - f.IsInitOnly && - !f.ParentType.IsPrivate && - !f.IsPrivate && - f.FieldType != null && - f.FieldType.IsClass && - !f.FieldType.IsThirdParty && - !f.FieldType.IsImmutable -select new { - f, - f.FieldType, - FieldVisibility = f.Visibility, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// This rule is violated when a *public* or *internal* -// type contains a *public* or *internal* read-only field -// whose field type is a mutable reference type. -// -// This situation gives the false impression that the -// value can't change, when actually it's only the field -// value that can't change, but the object state -// can still change. -// - -// -// To fix a violation of this rule, -// replace the field type with an immutable type, -// or declare the field as *private*. -// -// By default issues of this rule have a **Low** severity -// because they reflect more an advice than a problem. -//]]> - Array fields should not be read only -// ND1908:ArrayFieldsShouldNotBeReadOnly - -warnif count > 0 -from f in Application.Fields where - f.IsInitOnly && - f.IsPubliclyVisible && - f.FieldType != null && - f.FieldType.FullName == "System.Array" -select new { - f, - FieldVisibility = f.Visibility, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// This rule is violated when a publicly visible field -// that holds an array, is declared read-only. -// -// This situation represents a *security vulnerability*. -// Because the field is read-only it cannot be changed to refer -// to a different array. However, the elements of the array -// that are stored in a read-only field can be changed. -// Code that makes decisions or performs operations that are -// based on the elements of a read-only array that can be publicly -// accessed might contain an exploitable security vulnerability. -// - -// -// To fix the security vulnerability that is identified by -// this rule do not rely on the contents of a read-only array -// that can be publicly accessed. It is strongly recommended -// that you use one of the following procedures: -// -// • Replace the array with a strongly typed collection -// that cannot be changed. See for example: -// *System.Collections.Generic.IReadOnlyList* ; -// *System.Collections.Generic.IReadOnlyCollection* ; -// *System.Collections.ReadOnlyCollectionBase* -// -// • Or replace the public field with a method that returns a clone -// of a private array. Because your code does not rely on -// the clone, there is no danger if the elements are modified. -// -// By default issues of this rule have a **Low** severity -// because they reflect more an advice than a problem. -//]]> - Types tagged with ImmutableAttribute must be immutable -// ND1909:TypesTaggedWithImmutableAttributeMustBeImmutable - -warnif count > 0 -from t in Application.Types where - t.HasAttribute ("NDepend.Attributes.ImmutableAttribute".AllowNoMatch()) && - !t.IsImmutable -let culpritFields = t.Fields.Where(f => !f.IsStatic && !f.IsImmutable) -select new { - t, - culpritFields, - Debt = (5+10*culpritFields.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// An object is immutable if its state doesn’t change once the -// object has been created. Consequently, a structure or a class -// is immutable if its instances fields are only assigned inline -// or from constructor(s). -// -// An attribute **NDepend.Attributes.ImmutableAttribute** can be -// used to express in code that a type is immutable. In such -// situation, the present code rule checks continuously that the -// type remains immutable whatever the modification done. -// -// This rule warns when a type that is tagged with -// *ImmutableAttribute* is actually not immutable anymore. -// -// Notice that *FullCoveredAttribute* is defined in *NDepend.API.dll* -// and if you don't want to link this assembly, you can create your -// own *FullCoveredAttribute* and adapt the rule. -// - -// -// First understand which modification broke the type immutability. -// The list of *culpritFields* provided in this rule result can help. -// Then try to refactor the type to bring it back to immutability. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 5 minutes plus 10 minutes per culprit field. -//]]> - Types immutable should be tagged with ImmutableAttribute -// ND1910:TypesImmutableShouldBeTaggedWithImmutableAttribute - -// warnif count > 0 <-- not a code rule per default - -from t in Application.Types where - !t.HasAttribute ("NDepend.Attributes.ImmutableAttribute".AllowNoMatch()) && - t.IsImmutable -select new { - t, - t.NbLinesOfCode, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// An object is immutable if its state doesn’t change once the -// object has been created. Consequently, a structure or a class -// is immutable if its instances fields are only assigned inline -// or from constructor(s). -// -// This code query lists immutable type that are not tagged with -// an **ImmutableAttribute**. By using such attribute, you can express -// in source code the intention that a class is immutable, and -// should remain immutable in the future. Benefits of using -// an **ImmutableAttribute** are twofold: -// -// • Not only the intention is expressed in source code, -// -// • but it is also continuously checked by the rule -// *Types tagged with ImmutableAttribute must be immutable*. -// - -// -// Just tag types matched by this code query with **ImmutableAttribute** -// that can be found in *NDepend.API.dll*, -// or by an attribute of yours defined in your own code -// (in which case this code query must be adapted). -//]]> - Methods tagged with PureAttribute must be pure -// ND1911:MethodsTaggedWithPureAttributeMustBePure - -warnif count > 0 -from m in Application.Methods where - ( m.HasAttribute ("NDepend.Attributes.PureAttribute".AllowNoMatch()) || - m.HasAttribute ("System.Diagnostics.Contract.PureAttribute".AllowNoMatch()) ) && - ( m.ChangesObjectState || m.ChangesTypeState ) && - m.NbLinesOfCode > 0 - -let fieldsAssigned = m.FieldsAssigned - -select new { - m, - m.NbLinesOfCode, - fieldsAssigned, - Debt = 15.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// A method is pure if its execution doesn’t change -// the value of any instance or static field. -// A pure method is just a **function** that output -// a result from inputs. -// Pure methods naturally simplify code by **limiting -// side-effects**. -// -// An attribute **PureAttribute** can be -// used to express in code that a method is pure. In such -// situation, the present code rule checks continuously that the -// method remains pure whatever the modification done. -// -// This rule warns when a method that is tagged with -// *PureAttribute* is actually not pure anymore. -// -// Notice that *NDepend.Attributes.PureAttribute* is defined -// in *NDepend.API.dll* and if you don't want to link this -// assembly, you can also use -// *System.Diagnostics.Contract.PureAttribute* -// or create your own *PureAttribute* and adapt the rule. -// -// Notice that *System.Diagnostics.Contract.PureAttribute* is -// taken account by the compiler only when the VS project has -// Microsoft Code Contract enabled. -// - -// -// First understand which modification broke the method purity. -// Then refactor the method to bring it back to purity. -//]]> - Pure methods should be tagged with PureAttribute -// ND1912:PureMethodsShouldBeTaggedWithPureAttribute - -// warnif count > 0 <-- not a code rule per default - -from m in Application.Methods where - !m.IsGeneratedByCompiler && - !m.HasAttribute ("NDepend.Attributes.PureAttribute".AllowNoMatch()) && - !m.HasAttribute ("System.Diagnostics.Contract.PureAttribute".AllowNoMatch()) && - !m.ChangesObjectState && !m.ChangesTypeState && - m.NbLinesOfCode > 0 -select new { - m, - m.NbLinesOfCode, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// A method is pure if its execution doesn’t change -// the value of any instance or static field. -// Pure methods naturally simplify code by **limiting -// side-effects**. -// -// This code query lists pure methods that are not tagged with -// a **PureAttribute**. By using such attribute, you can express -// in source code the intention that a method is pure, and -// should remain pure in the future. Benefits of using -// a **PureAttribute** are twofold: -// -// • Not only the intention is expressed in source code, -// -// • but it is also continuously checked by the rule -// *Methods tagged with PureAttribute must be pure*. -// -// This code query is not by default defined as a code rule -// because certainly many of the methods of the code base -// are matched. Hence fixing all matches and then -// maintaining the rule unviolated might require a lot of -// work. This may *counter-balance* such rule benefits. -// - -// -// Just tag methods matched by this code query with -// *NDepend.Attributes.PureAttribute* -// that can be found in *NDepend.API.dll*, -// or with *System.Diagnostics.Contract.PureAttribute*, -// or with an attribute of yours defined in your own code -// (in which case this code query must be adapted). -// -// Notice that *System.Diagnostics.Contract.PureAttribute* is -// taken account by the compiler only when the VS project has -// Microsoft Code Contract enabled. -//]]> - - - Instance fields naming convention -// ND2000:InstanceFieldsNamingConvention - -warnif count > 0 from f in JustMyCode.Fields where - !( - // Instance field name starting with a lower-case letter - (f.Name.Length >= 1 && char.IsLetter(f.Name[0]) && char.IsLower(f.Name[0])) || - - // Instance field name starting with "_" followed with a lower-case letter - (f.Name.Length >= 2 && f.Name.StartsWith("_") && char.IsLetter(f.Name[1]) && char.IsLower(f.Name[1])) || - - // Instance field name starting with "m_" followed with an upper-case letter - (f.Name.Length >= 3 && f.Name.StartsWith("m_") && char.IsLetter(f.Name[2]) && char.IsUpper(f.Name[2])) - - ) && - !f.IsStatic && - !f.IsLiteral && - !f.IsGeneratedByCompiler && - !f.IsSpecialName && - !f.IsEventDelegateObject && - - // Don't check naming convention on serializable fields - !f.HasAttribute("System.Xml.Serialization.XmlAttributeAttribute".AllowNoMatch()) && - !f.HasAttribute("System.Runtime.Serialization.DataMemberAttribute".AllowNoMatch()) && - - // Don't warn if a method using the field is also calling a method that has 'ref' and 'out' parameters. - // This could lead to false positive. A field used in a 'ref' or 'out' parameter cannot be set as read-only. - f.MethodsUsingMe.SelectMany(m => m.MethodsCalled).FirstOrDefault(m => m.Name.Contains("&")) == null - -select new { - f, - f.SizeOfInst, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// By default the presents rule supports the 3 most used naming -// conventions for instance fields: -// -// • Instance field name starting with a lower-case letter -// -// • Instance field name starting with "_" followed with a lower-case letter -// -// • Instance field name starting with "m_" followed with an upper-case letter -// -// The rule can be easily adapted to your own company naming convention. -// -// In terms of behavior, a *static field* is something completely different -// than an *instance field*, so it is interesting to differentiate them at -// a glance through a naming convetion. -// -// This is why it is advised to use a specific naming convention for instance -// field like name that starts with **m_**. -// -// Related discussion: -// http://codebetter.com/patricksmacchia/2013/09/04/on-hungarian-notation-for-instance-vs-static-fields-naming/ -// - -// -// Once the rule has been adapted to your own naming convention -// make sure to name all matched instance fields adequately. -//]]> - Static fields naming convention -// ND2001:StaticFieldsNamingConvention - -warnif count > 0 from f in JustMyCode.Fields where - !( - // Static field name starting with an upper-case letter - (f.Name.Length >= 1 && char.IsLetter(f.Name[0]) && char.IsUpper(f.Name[0])) || - - // Static field name starting with "_" followed with an upper-case letter - (f.Name.Length >= 2 && f.Name.StartsWith("_") && char.IsLetter(f.Name[1]) && char.IsUpper(f.Name[1])) || - - // Static field name starting with "s_" followed with an upper-case letter - (f.Name.Length >= 3 && f.Name.StartsWith("s_") && char.IsLetter(f.Name[2]) && char.IsUpper(f.Name[2])) - - ) && - f.IsStatic && - !f.IsLiteral && - !f.IsGeneratedByCompiler && - !f.IsSpecialName && - !f.IsEventDelegateObject -select new { - f, - f.SizeOfInst, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// By default the presents rule supports the 3 most used naming -// conventions for static fields: -// -// • Static field name starting with an upper-case letter -// -// • Static field name starting with "_" followed with an upper-case letter -// -// • Static field name starting with "s_" followed with an upper-case letter -// -// The rule can be easily adapted to your own company naming convention. -// -// In terms of behavior, a *static field* is something completely different -// than an *instance field*, so it is interesting to differentiate them at -// a glance through a naming convetion. -// -// This is why it is advised to use a specific naming convention for static -// field like name that starts with **s_**. -// -// Related discussion: -// http://codebetter.com/patricksmacchia/2013/09/04/on-hungarian-notation-for-instance-vs-static-fields-naming/ -// - -// -// Once the rule has been adapted to your own naming convention -// make sure to name all matched static fields adequately. -//]]> - Interface name should begin with a 'I' -// ND2002:InterfaceNameShouldBeginWithAI - -// Don't query all Application.Types because some interface generated might not start with an 'I' -// like for example when adding a WSDL reference to a project. -warnif count > 0 from t in JustMyCode.Types where - t.IsInterface && - // Don't apply this rule for COM interfaces. - !t.HasAttribute("System.Runtime.InteropServices.ComVisibleAttribute".AllowNoMatch()) - -// Discard outter type(s) name prefix for nested types -let name = !t.IsNested ? - t.Name : - t.Name.Substring(t.Name.LastIndexOf('+') + 1, t.Name.Length - t.Name.LastIndexOf('+') - 1) - -where name[0] != 'I' -select new { - t, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// In the .NET world, interfaces names are commonly prefixed -// with an upper case **I**. This rule warns about interfaces -// whose names don't follow this convention. Because this -// naming convention is widely used and accepted, we -// recommend abiding by this rule. -// -// Typically COM interfaces names don't follow this rule. -// Hence this code rule doesn't take care of interfaces tagged -// with *ComVisibleAttribute*. -// - -// -// Make sure that matched interfaces names are prefixed with -// an upper **I**. -//]]> - Abstract base class should be suffixed with 'Base' -// ND2003:AbstractBaseClassShouldBeSuffixedWithBase - -warnif count > 0 from t in Application.Types where - t.IsAbstract && - t.IsClass && - - t.BaseClass != null && - t.BaseClass.FullName == "System.Object" && - - ((!t.IsGeneric && !t.NameLike (@"Base$")) || - ( t.IsGeneric && !t.NameLike (@"Base<"))) -select new { - t, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns about *abstract classes* whose names are not -// suffixed with **Base**. It is a common practice in the .NET -// world to suffix base classes names with **Base**. -// -// Notice that this rule doesn't match abstract classes that -// are in a middle of a hierarchy chain. -// In other words, only base classes that derive directly -// from *System.Object* are matched. -// - -// -// Suffix the names of matched base classes with **Base**. -//]]> - Exception class name should be suffixed with 'Exception' -// ND2004:ExceptionClassNameShouldBeSuffixedWithException - -warnif count > 0 from t in Application.Types where - t.IsExceptionClass && - - // We use SimpleName, because in case of generic Exception type - // SimpleName suppresses the generic suffix (like ). - !t.SimpleNameLike(@"Exception$") && - !t.SimpleNameLike(@"ExceptionBase$") // Allow the second suffix Base - // for base exception classes. -select new { - t, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule warns about *exception classes* whose names are not -// suffixed with **Exception**. It is a common practice in the .NET -// world to suffix exception classes names with **Exception**. -// -// For exception base classes, the suffix **ExceptionBase** -// is also accepted. -// - -// -// Suffix the names of matched exception classes with **Exception**. -//]]> - Attribute class name should be suffixed with 'Attribute' -// ND2005:AttributeClassNameShouldBeSuffixedWithAttribute - -warnif count > 0 from t in Application.Types where - t.IsAttributeClass && - !t.NameLike (@"Attribute$") -select new { - t, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule warns about *attribute classes* whose names are not -// suffixed with **Attribute**. It is a common practice in the .NET -// world to suffix attribute classes names with **Attribute**. -// - -// -// Suffix the names of matched attribute classes with **Attribute**. -//]]> - Types name should begin with an Upper character -// ND2006:TypesNameShouldBeginWithAnUpperCharacter - -warnif count > 0 -let isAspNetApp = ThirdParty.Assemblies.WithName("System.Web").Any() -from t in JustMyCode.Types where - // The name of a type should begin with an Upper letter. - !t.SimpleNameLike (@"^[A-Z]") && - - // Except if it is generated by compiler. - !t.IsSpecialName && - !t.IsGeneratedByCompiler && - - // Special default ASP.NET type named "_Default" - !(isAspNetApp && t.SimpleName == "_Default") - -select new { - t, - // We show the type simple name - // that doesn't include the parent type name - // for nested types. - t.SimpleName, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns about *types* whose names don't start -// with an Upper character. It is a common practice in the .NET -// world to use **Pascal Casing Style** to name types. -// -// **Pascal Casing Style** : The first letter in the identifier -// and the first letter of each subsequent concatenated word -// are capitalized. For example: *BackColor* -// - -// -// *Pascal Case* the names of matched types. -//]]> - Methods name should begin with an Upper character -// ND2007:MethodsNameShouldBeginWithAnUpperCharacter - -warnif count > 0 -from m in JustMyCode.Methods where - !m.NameLike (@"^[A-Z]") && - !m.IsSpecialName && - !m.IsGeneratedByCompiler && - !m.IsExplicitInterfaceImpl // This can generate unexpected method name. -select new { - m, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns about *methods* whose names don't start -// with an Upper character. It is a common practice in the .NET -// world to use **Pascal Casing Style** to name methods. -// -// **Pascal Casing Style** : The first letter in the identifier -// and the first letter of each subsequent concatenated word -// are capitalized. For example: *ComputeSize* -// - -// -// *Pascal Case* the names of matched methods. -//]]> - Do not name enum values 'Reserved' -// ND2008:DoNotNameEnumValuesReserved - -warnif count > 0 -from f in Application.Fields where - f.IsEnumValue && - f.NameLike (@"Reserved") -select new { - f, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule assumes that an enumeration member -// with a name that contains **"Reserved"** -// is not currently used but is a placeholder to -// be renamed or removed in a future version. -// Renaming or removing a member is a breaking -// change. You should not expect users to ignore -// a member just because its name contains -// **"Reserved"** nor can you rely on users to read or -// abide by documentation. Furthermore, because -// reserved members appear in object browsers -// and smart integrated development environments, -// they can cause confusion as to which members -// are actually being used. -// -// Instead of using a reserved member, add a -// new member to the enumeration in the future -// version. -// -// In most cases, the addition of the new -// member is not a breaking change, as long as the -// addition does not cause the values of the -// original members to change. -// - -// -// To fix a violation of this rule, remove or -// rename the member. -//]]> - Avoid types with name too long -// ND2009:AvoidTypesWithNameTooLong - -warnif count > 0 -from t in JustMyCode.Types -where !t.IsGeneratedByCompiler - -where t.SimpleName.Length > 40 -orderby t.SimpleName.Length descending -select new { - t, - t.SimpleName, - NameLength = t.SimpleName.Length, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// Types with a name too long tend to decrease code readability. -// This might also be an indication that a type is doing too much. -// -// This rule matches types with names with more than 40 characters. -// - -// -// To fix a violation of this rule, rename the type with a shortest name -// or eventually split the type in several more fine-grained types. -//]]> - Avoid methods with name too long -// ND2010:AvoidMethodsWithNameTooLong - -warnif count > 0 - -// First get test method for which we allow long names -let testAttr = ThirdParty.Types.WithNameIn("FactAttribute", "TestAttribute", "TestCaseAttribute") -let testMethods = Methods.TaggedWithAnyAttributes(testAttr).ToHashSetEx() - -from m in JustMyCode.Methods where - - // Explicit Interface Implementation methods are - // discarded because their names are prefixed - // with the interface name. - !m.IsExplicitInterfaceImpl && - !m.IsGeneratedByCompiler && - ((!m.IsSpecialName && m.SimpleName.Length > 40) || - // Property getter/setter are prefixed with "get_" "set_" of length 4. - ( m.IsSpecialName && m.SimpleName.Length - 4 > 40)) && - - // Don't match test methods - !testMethods.Contains(m) && - !m.SimpleName.Contains("Test") && - !m.ParentType.SimpleName.Contains("Test") && - !m.ParentNamespace.Name.Contains("Test") - -orderby m.SimpleName.Length descending - -select new { - m, - m.SimpleName, - NameLength = m.SimpleName.Length - (m.IsSpecialName ? 4 : 0), - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// Methods with a name too long tend to decrease code readability. -// This might also be an indication that a method is doing too much. -// -// This rule matches methods with names with more than 40 characters. -// -// However it is considered as a good practice to name unit tests -// in such a way with a very expressive name, hence this rule doens't match -// methods tagged with *FactAttribute*, *TestAttribute* and *TestCaseAttribute*. -// - -// -// To fix a violation of this rule, rename the method with a shortest name -// that equally conveys the behavior of the method. -// Or eventually split the method into several smaller methods. -//]]> - Avoid fields with name too long -// ND2011:AvoidFieldsWithNameTooLong - -warnif count > 0 from f in JustMyCode.Fields where - !f.IsGeneratedByCompiler && - f.Name.Length > 40 -orderby f.Name descending -select new { - f, - NameLength = f.Name.Length, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// Fields with a name too long tend to decrease code readability. -// -// This rule matches fields with names with more than 40 characters. -// - -// -// To fix a violation of this rule, rename the field with a shortest name -// that equally conveys the same information. -//]]> - Avoid having different types with same name -// ND2012:AvoidHavingDifferentTypesWithSameName - -warnif count > 0 - -// Special type names for which multiple types with such name are allowed. -let nonReportedTypeName = new [] { - "Program", "NamespaceDoc", "Initial", "Startup", "SwaggerConfig", "Constants" } - -// Special type name suffixes for which multiple types with such suffix are allowed. -let nonReportedTypeNameSuffix = new [] { - "Service", "Model", "Controller", "Page", "Pages" } - -// This rule matches also collisions between -// application and third-party types sharing a same name. -let groups = JustMyCode.Types.Union(ThirdParty.Types) - // Discard nested types, whose name is - // prefixed with the parent type name. - .Where(t => !t.IsNested && - !nonReportedTypeName.Contains(t.Name) && - !t.Name.EndsWithAny(nonReportedTypeNameSuffix)) - - // Group types by name. - .GroupBy(t => t.Name) - -from @group in groups - where @group.Count() > 1 - - // Let's see if types with the same name are declared - // in different namespaces. - // (t.FullName is {namespaceName}.{typeName} ) - let groupsFullName = @group.GroupBy(t => t.FullName) - where groupsFullName.Count() > 1 - - // If several types with same name are declared in different namespaces - // eliminate the case where all types are declared in third-party assemblies. - let types= groupsFullName.SelectMany(g => g) - where types.Any(t => !t.IsThirdParty) - // Uncomment this line, to only gets naming collision involving - // both application and third-party types. - // && types.Any(t => t.IsThirdParty) - -orderby types.Count() descending - -select new { - // Order types by parent namespace and assembly name,this way we always get the same type across sessions. - // Without this astute, the same issue would be seen as added/removed when the first type choosen in the group - // was not always the same, a situation that actually happens. - // Also make sure that the chosen type is not a third-party one. - t = types.OrderBy(t => (t.IsThirdParty ? "1" : "0") + t.ParentNamespace.Name + t.ParentAssembly.Name).First(), - - // In the 'types' column, make sure to group matched types - // by parent assemblies and parent namespaces, to get a result - // more readable. - types, - - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule warns about multiple types with same name, -// that are defined in different *application* or -// *third-party* namespaces or assemblies. -// -// Such practice create confusion and also naming collision -// in source files that use different types with same name. -// - -// -// To fix a violation of this rule, rename concerned types. -//]]> - Avoid prefixing type name with parent namespace name -// ND2013:AvoidPrefixingTypeNameWithParentNamespaceName - -warnif count > 0 - -from n in JustMyCode.Namespaces -where n.Name.Length > 0 - -from t in n.ChildTypes -where - JustMyCode.Contains(t) && // Don't warn about generated code - !t.IsGeneratedByCompiler && - !t.IsNested && - t.Name.IndexOf(n.SimpleName) == 0 && - - // The type name is equal to namespace name or the type name contains another - // word that starts with an upper-case letter after the namespace name. - // This way we avoid matching false-positive where namespace name is "Stat" and type name is "Statistic". - (t.Name.Length == n.SimpleName.Length || char.IsUpper(t.Name[n.SimpleName.Length])) -select new { - t, - namespaceName = n.SimpleName, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns about situations where the parent namespace name -// is used as the prefix of a contained type. -// -// For example a type named "RuntimeEnvironment" -// declared in a namespace named "Foo.Runtime" -// should be named "Environment". -// -// Such situation creates naming redundancy with no readability gain. -// - -// -// To fix a violation of this rule, remove the prefix from the type name. -//]]> - Avoid naming types and namespaces with the same identifier -// ND2014:AvoidNamingTypesAndNamespacesWithTheSameIdentifier - -warnif count > 0 -let hashsetShortNames = Namespaces.Where(n => n.Name.Length > 0).Select(n => n.SimpleName).ToHashSetEx() - -from t in JustMyCode.Types -where hashsetShortNames.Contains(t.Name) -select new { - t, - namespaces = Namespaces.Where(n => n.SimpleName == t.Name), - Debt = 12.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule warns when a type and a namespace have the same name. -// -// For example when a type is named *Environment* -// and a namespace is named *Foo.Environment*. -// -// Such situation provokes tedious compiler resolution collision, -// and makes the code less readable because concepts are not -// concisely identified. -// - -// -// To fix a violation of this rule, renamed the concerned type or namespace. -//]]> - Don't call your method Dispose -// ND2015:DontCallYourMethodDispose - -warnif count > 0 - -// Activate this rule only when IDisposable is resolved in third-party code -// else this rule might produce false positives. -let thirdPartyDisposable = ThirdParty.Types.WithFullName("System.IDisposable").FirstOrDefault() -where thirdPartyDisposable != null - -from m in JustMyCode.Methods.WithSimpleName("Dispose") -where !m.ParentType.Implement("System.IDisposable".AllowNoMatch()) - && m.OverriddensBase.Count() == 0 // Can't change the name of an override - && !m.IsNewSlot // new slot also means override of an intreface method -select new { - m, - Debt = 15.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// In .NET programming, the identifier *Dispose* should be kept -// only for implementations of *System.IDisposable*. -// -// This rule warns when a method is named *Dispose()*, -// but the parent type doesn't implement *System.IDisposable*. -// - -// -// To fix a violation of this rule, -// either make the parent type implements *System.IDisposable*, -// or rename the *Dispose()* method with another identifier like: -// *Close() Terminate() Finish() Quit() Exit() Unlock() ShutDown()*… -//]]> - Methods prefixed with 'Try' should return a boolean -// ND2016:MethodsPrefixedWithTryShouldReturnABoolean - -warnif count > 0 -from m in Application.Methods where - m.SimpleNameLike("^Try") && - m.ReturnType != null && - m.ReturnType.FullName != "System.Boolean" -select new { - m, - m.ReturnType, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// When a method has a name prefixed with **Try**, it is expected that -// it returns a *boolean*, that reflects the method execution status, -// *success* or *failure*. -// -// Such method usually returns a result through an *out parameter*. -// For example: *System.Int32.TryParse(int,out string):bool* -// - -// -// To fix a violation of this rule, -// Rename the method, or transform it into an operation that can fail. -//]]> - Properties and fields that represent a collection of items should be named Items. -// ND2017:PropertiesAndFieldsThatRepresentACollectionOfItemsShouldBeNamedItems - -warnif count > 0 - -let collectionTypes = Types.Where(t => - t.Implement("System.Collections.Generic.IEnumerable".AllowNoMatch()) && - t.IsGeneric && - t.FullName != "System.Collections.Generic.IDictionary" && - !t.Implement("System.Collections.Generic.IDictionary".AllowNoMatch()) && - t.FullName != "System.Collections.Generic.IReadOnlyDictionary" && - !t.Implement("System.Collections.Generic.IReadOnlyDictionary".AllowNoMatch())).ToHashSetEx() - -let properties = from m in JustMyCode.Methods -where (m.IsPropertyGetter || m.IsPropertySetter) && - collectionTypes.Contains(m.ReturnType) -select new { Member = (IMember)m, Type = m.ReturnType } - -let fields = from f in JustMyCode.Fields -where collectionTypes.Contains(f.FieldType) -select new { Member = (IMember)f, Type = f.FieldType } - -from pair in properties.Concat(fields) -let identifier = pair.Member.SimpleName -where !identifier.EndsWith("s") && - !identifier.Contains('<') // Remove potential generated fields like backing fields, and also potential generated properties accessors - -let identifierRefined = identifier.Replace("get_", "").Replace("set_", "") -let words = identifierRefined.GetWords() -where !words.Any(word => - word.EndsWith("s") || // Don't warn if any word ends with an s - word == "Empty") // Don't warn if any word in the identifier is Empty - -select new { pair.Member, pair.Type } - -// -// A good practice to make the code more readable and more predictable -// is to name properties and fields typed with a collection of *items* -// with the plural form of *Items*. -// -// Depending on the domain of your application, a proper identifier could be -// *NewDirectories*, *Words*, *Values*, *UpdatedDates*. -// -// Also this rule doesn't warn when any word in the identifier ends with an *s*. -// This way identifiers like **TasksToRun**, **KeysDisabled**, **VersionsSupported**, -// **ChildrenFilesPath**, **DatedValuesDescending**, **ApplicationNodesChanged** -// or **ListOfElementsInResult** are valid and won't be seen as violations. -// -// Moreover this rule won't warn for a field or property with an identifier -// that contain the word **Empty**. -// This is a common pattern to define an immutable and empty collection instance -// shared. -// -// Before inspecting properties and fields, this rule gathers -// application and third-party collection types that might be returned -// by a property or a field. To do so this rule searches types that implement -// *IEnumerable* except: -// -// - Non generic types: Often a non generic type is not seen as a collection. -// For example *System.String* implements *IEnumerable*, but a string -// is rarely named as a collection of characters. In others words, -// we have much more strings in our program named like *FirstName* -// than named like *EndingCharacters*. -// -// - Dictionaries types: A dictionary is more than a collection of pairs, -// it is a mapping from one domain to another. A common practice is to suffix -// the name of a dictionary with *Map* *Table* or *Dictionary*, -// although often dictionaries names satify this rule with names like -// *GuidsToPersons* or *PersonsByNames*. -// - -// -// Just rename the fields and properties accordingly, -// by making plural the word in the identifier -// that describes best the *items* in the collection. -// -// For example: -// -// - **ListOfDir** can be renamed **Directories**. -// -// - **Children** can be renamed **ChildrenItems** -// -// - **QueueForCache** can be renamed **QueueOfItemsForCache** -//]]> - DDD ubiquitous language check -// ND2018:DDDUbiquitousLanguageCheck - -warnif count > 0 - -// Update to your core domain namespace(s) -let coreDomainNamespaces = Application.Namespaces.WithNameLike("TrainTrain.Domain") - -// Update your vocabulary list -let vocabulary = new [] { -"Train", "Coach", "Coaches", "Seat", "Seats", -"Reservation", "Fulfilled", "Booking", "Book", "Reserve", "Confirm" -}.ToHashSetEx() - -let technicalWords = new [] { "get", "set", "Get", "Set", "Add" }.ToHashSetEx() -let multiWordsVocabulary = vocabulary.Where(w => w.GetWords().Length >= 2) - -// Append multi-words words in vocabulary -let multiWordsWords = multiWordsVocabulary.SelectMany(w => w.GetWords()) -let vocabulary2 = vocabulary.Concat(multiWordsWords).ToHashSetEx() -let vocabulary3 = vocabulary2.Concat(technicalWords).ToHashSetEx() - -from ce in coreDomainNamespaces.ChildTypesAndMembers() -let tokens = ce.SimpleName.GetWords().Select(w => w.FirstCharToUpper()).ToArray() - -where !(ce.IsMethod && ce.AsMethod.IsConstructor) && // No vocabulary in ctor - !(ce.IsField && ce.AsField.IsGeneratedByCompiler) && // Remove compiler generated backing fields, their vocabulary is in property - !(vocabulary3.Any(vocable => tokens.Contains(vocable))) - -let wordsNotInVocabulary = tokens.Where(w => !(vocabulary2.Contains(w) && string.IsNullOrEmpty(w))).ToArray() - -select new { ce, - wordsNotInVocabulary = string.Join(", ",wordsNotInVocabulary) -} - -// -// The language used in identifiers of classes, methods and fields of the **core domain**, -// should be based on the **Domain Model**. -// This constraint is known as **ubiquitous language** in **Domain Driven Design (DDD)** -// and it reflects the need to be rigorous with naming, -// since software doesn't cope well with ambiguity. -// -// This rule is disabled per default -// because its source code needs to be customized to work, -// both with the **core domain** namespace name -// (that contains classes and types to be checked), -// and with the list of domain language terms. -// -// If a term needs to be used both with singular and plural forms, -// both forms need to be mentioned, -// like **Seat** and **Seats** for example. -// Notice that this default rule is related with the other default rule -// *Properties and fields that represent a collection of items should be named Items* -// defined in the *Naming Convention* group. -// -// This rule implementation relies on the NDepend API -// **ExtensionMethodsString.GetWords(this string identifier)** -// extension method that extracts terms -// from classes, methods and fields identifiers in a smart way. -// - -// -// For each violation, this rule provides the list of **words not in vocabulary**. -// -// To fix a violation of this rule, either rename the concerned code element -// or update the domain language terms list defined in this rule source code, -// with the missing term(s). -//]]> - Avoid fields with same name in class hierarchy -// ND2019:AvoidFieldsWithSameNameInClassHierarchy - -warnif count > 0 - -// Optimization: only consider fields of derived classes -let derivedClasses = Application.Types.Where(t => - t.IsClass && - !t.IsStatic && - t.DepthOfInheritance > 1 && - t.BaseClasses.ChildFields().Any()) - -from f in derivedClasses.ChildFields() -where JustMyCode.Contains(f) // Make sure it is fixable by targeting only JustMyCode fields -let fieldsOfBaseClassesWithSameName = - f.ParentType - .BaseClasses - .ChildFields() - .Where(cf => cf.Name == f.Name && JustMyCode.Contains(cf)) -where fieldsOfBaseClassesWithSameName.Any() - -select new { - f, - fieldsOfBaseClassesWithSameName, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule matches a field whose name is already used by -// another field in the base class hierarchy. -// -// The C# and VB.NET compilers allow this situation even -// if the base class field is visible to derived classes -// with a visibility different than *private*. -// -// However this situation is certainly a sign of -// an erroneous attempt to redefine a field in a derived class. -// - -// -// Check if the field in the derived class is indeed a redefinition -// of the base class field. Check also that both fields types -// corresponds. If fields are static, double check that only a single -// instance of the referenced object is needed. If all checks are positive -// delete the derived class field and make sure that the base class -// field is visible to derived classes with the *protected* visibility. -// -// If no, rename the field in the derived class and be very careful -// in renaming all usages of this field, they might be related -// with the base class field. -//]]> - Avoid various capitalizations for method name -// ND2020:AvoidVariousCapitalizationsForMethodName - -warnif count > 0 - -let lookup = JustMyCode.Methods.ToLookup(m => m.SimpleName.ToLower()) -from grouping in lookup -where grouping.Count() > 1 -let name = grouping.First().SimpleName -let method = grouping.FirstOrDefault(m => m.SimpleName != name) -where method != null -let nbCapitalizations = grouping.ToLookup(m => m.SimpleName).Count - -// Increase the severity if various capitalizations are found in same class -let differentCapitalizationInSameType = - grouping.ToLookup(m => m.ParentType) - .Any(groupingPerType => groupingPerType.ToLookup(m => m.SimpleName).Count > 1) - -select new { - method, - methods = grouping.ToArray(), - nbCapitalizations, - Debt = (nbCapitalizations * 6).ToMinutes().ToDebt(), - Severity = differentCapitalizationInSameType ? Severity.Critical : Severity.Medium -} - -// -// This rule matches application methods whose names differ -// only in capitalization. -// -// With this rule you'll discover various names like -// *ProcessOrderId* and *ProcessOrderID*. It is important to -// choose a single capitalization used accross the whole application. -// -// Matched methods are not necessarily declared in the same type. -// However if various capitalizations of a method name are found -// within the same type, the issue severity goes from *Medium* -// to *Critical*. Indeed, this situation is not just a matter -// of convention, it is error-prone. It forces type's clients -// to investigate which version of the method to call. -// - -// -// Choose a single capitalization for the method name -// used accross the whole application. -// -// Or alternatively make the distinction clear by having -// different method names that don't only differ by -// capitalization. -// -// The technical-debt for each issue, the estimated cost -// to fix an issue, is proportional to the number -// of capitalizations found (2 minimum). -//]]> - - - Avoid referencing source file out of Visual Studio project directory -// ND2100:AvoidReferencingSourceFileOutOfVisualStudioProjectDirectory - -warnif count > 0 - -from a in Application.Assemblies -where a.VisualStudioProjectFilePath != null -let vsProjDirPathLower = a.VisualStudioProjectFilePath.ParentDirectoryPath.ToString().ToLower() - -from t in a.ChildTypes -where JustMyCode.Contains(t) && t.SourceFileDeclAvailable - -from decl in t.SourceDecls -let sourceFilePathLower = decl.SourceFile.FilePath.ToString().ToLower() -where sourceFilePathLower.IndexOf(vsProjDirPathLower) != 0 -select new { - t, - sourceFilePathLower, - projectFilePath = a.VisualStudioProjectFilePath.ToString(), - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// A source file located outside of the VS project directory can be added through: -// *> Add > Existing Items… > Add As Link* -// -// Doing so can be used to share types definitions across several assemblies. -// This provokes type duplication at binary level. -// Hence maintainability is degraded and subtle versioning bug can appear. -// -// This rule matches types whose source files are not declared under the -// directory that contains the related Visual Studio project file, or under -// any sub-directory of this directory. -// -// This practice can be tolerated for certain types shared across executable assemblies. -// Such type can be responsible for startup related concerns, -// such as registering custom assembly resolving handlers or -// checking the .NET Framework version before loading any custom library. -// - -// -// To fix a violation of this rule, prefer referencing from a VS project -// only source files defined in sub-directories of the VS project file location. -// -// By default issues of this rule have a **Low** severity -// because they reflect more an advice than a problem. -//]]> - Avoid duplicating a type definition across assemblies -// ND2101:AvoidDuplicatingATypeDefinitionAcrossAssemblies - -warnif count > 0 - -let groups = Application.Types - .Where(t => !t.IsGeneratedByCompiler && - // Types created by the test infrastructure - t.FullName != "AutoGeneratedProgram") - .GroupBy(t => t.FullName) -from @group in groups -where @group.Count() > 1 - -// Tricky: This rule is executed on both current snapshot and baseline snapshot (if any). -// Taking t as @group.First() could return any type in the group, and potentially -// it can return different types for current and baseline snapshot. -// As a result issues wouldn't corresponds (since types are different) and the user -// would see issues of this rule as a couple of issues added and removed. -// Ordering types by parent assembly name and then taking the first type, discards this risk. -let types = @group.OrderBy(t => t.ParentAssembly.Name) - -select new { - t = types.First(), - // In the 'types' column, make sure to group matched types by parent assemblies. - typesDefs = types.ToArray(), - Debt = 15.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// A source file located outside of the VS project directory can be added through: -// *> Add > Existing Items… > Add As Link* -// -// This rule warns about using this feature to share code across several assemblies. -// This provokes type duplication at binary level. -// Hence maintainability is degraded and subtle versioning bug can appear. -// -// Each match represents a type duplicated. Unfolding the types in the column -// **typesDefs** will show the multiple definitions across multiple assemblies. -// -// This practice can be tolerated for certain types shared across executable assemblies. -// Such type can be responsible for startup related concerns, -// such as registering custom assembly resolving handlers or -// checking the .NET Framework version before loading any custom library. -// - -// -// To fix a violation of this rule, prefer sharing types through DLLs. -//]]> - Avoid defining multiple types in a source file -// ND2102:AvoidDefiningMultipleTypesInASourceFile - -warnif count > 0 - -// Build a lookup indexed by source files, values being a sequence of types defined in the source file. -let lookup = JustMyCode.Types - // When a source file is referenced by several assemblies, - // type(s) contained in the source file are seen as distinct types, both by the CLR and by NDepend. - // This Distinct clause based on type full-name (type name prefixed with namespace) - // avoids matching the multiple versions of such type. - .Distinct(t => t.FullName) - .Where(t => t.SourceFileDeclAvailable && - // except enumerations, nested types and types generated by compilers! - !t.IsEnumeration && - !t.IsNested && - !t.IsGeneratedByCompiler) - // We use multi-key, since a type can be declared in multiple source files. - .ToMultiKeyLookup(t => t.SourceDecls.Select(d => d.SourceFile)) - -from @group in lookup where @group.Count() > 1 - let sourceFile = @group.Key - - // CQLinq doesn't let indexing result with sourceFile - // so we choose a typeIndex in types, - // preferably the type that has the file name. - let typeWithSourceFileName = @group.FirstOrDefault(t => t.SimpleName == sourceFile.FileNameWithoutExtension) - let typeIndex = typeWithSourceFileName ?? @group.First() - -select new { - typeIndex, - TypesInSourceFile = @group as IEnumerable, - SourceFilePathString = sourceFile.FilePathString, - Debt = 3.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// Defining multiple types in a single source file decreases code readability, -// because developers are used to see all types in a namespace, -// when expanding a folder in the *Visual Studio Solution Explorer*. -// Also doing so, leads to source files with too many lines. -// -// Each match of this rule is a source file that contains several types -// definitions, indexed by one of those types, preferably the one with -// the same name than the source file name without file extension, if any. -// - -// -// To fix a violation of this rule, create a source file for each type. -// -// By default issues of this rule have a **Low** severity -// because they reflect more an advice than a problem. -//]]> - Namespace name should correspond to file location -// ND2103:NamespaceNameShouldCorrespondToFileLocation - -warnif count > 0 - -from a in Application.Assemblies -let assemblyName = a.Name - -from n in a.ChildNamespaces -let namespaceName = n.Name - -// Build the dirShouldContain string -// and then we check which source file path contains dirShouldContain or not -let dirShouldContain = ( - // namespaceName starts with assembly name => gets only components after the assembly name - namespaceName.StartsWith(assemblyName) - ? namespaceName.Substring(assemblyName.Length) - - // namespaceName contains assembly name => gets only components after the assembly name - : namespaceName.Contains("." + assemblyName) - ? namespaceName.Substring(n.Name.IndexOf("." + assemblyName)) - - // namespaceName has more than one part => gets only components after the first part - : namespaceName.Contains(".") - ? namespaceName.Substring(n.Name.IndexOf(".")) - : namespaceName) - - // Replace dots by spaces in namespace name - .Replace('.', ' ') - -where dirShouldContain.Length > 0 - -// Look at source file decl of JustMyCode type's declared in the namespace 'n' -from t in n.ChildTypes -where JustMyCode.Contains(t) && t.SourceFileDeclAvailable - -let sourceDeclConcerned = (from decl in t.SourceDecls - let sourceFilePath = decl.SourceFile.FilePath.ToString() - // Replace dots and path separators by spaces in source files names - where !sourceFilePath.Replace('.',' ').Replace('\\',' ').Contains(dirShouldContain) - select sourceFilePath).ToArray() -where sourceDeclConcerned.Length > 0 - -let justACaseSensitiveIssue = sourceDeclConcerned[0].ToLower().Replace('.',' ').Replace('\\',' ').Contains(dirShouldContain.ToLower()) - -select new { - t, - dirShouldContain, - sourceFilePath = sourceDeclConcerned[0], - nbSourceDeclConcerned = sourceDeclConcerned.Length, - justACaseSensitiveIssue , - Debt = (justACaseSensitiveIssue ? - 1f + sourceDeclConcerned.Length/2f : - 2f + sourceDeclConcerned.Length).ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// For a solid code structure and organization, -// do mirror the namespaces hierarchy and the directories hierarchy containing source files. -// -// Doing so is a widely accepted convention, and not respecting this convention -// will lead to less maintainable and less browsable source code. -// -// This rule matches all types in such source file, whose location doesn't correspond -// to the type parent namespace. If a source file contains several such types (that -// are not necessarily in the same namespace) each type will result in a violation. -// -// If a type is declared in several such source files, the value for the column -// *nbSourceDeclConcerned* in the result, is greater than 1. -// The technical-debt per issue is proportional to *nbSourceDeclConcerned*. -// -// Notice that namespaces and directories names comparison is **case-sensitive**. -// A boolean *justACaseSensitiveIssue* indicates if it is just a case-sensitive issue, -// in which case the technical-debt is divided by two. -// - -// -// To fix a violation of this rule, make sure that the type parent namespace and -// the directory sub-paths that contains the type source file, are mirrored. -// -// Make sure to first check the boolean *justACaseSensitiveIssue*, in which case -// the issue is easier to fix. -// -]]> - Types with source files stored in the same directory, should be declared in the same namespace -// ND2104:TypesWithSourceFilesStoredInTheSameDirectoryShouldBeDeclaredInTheSameNamespace - -warnif count > 0 - -// Group JustMyCode types in a lookup -// where groups are keyed with directories that contain the types' source file(s). -// Note that a type can be contained in several groups -// if it is declared in several source files stored in different directories. -let lookup = JustMyCode.Types.Where(t => t.SourceFileDeclAvailable) - .ToMultiKeyLookup( - t => t.SourceDecls.Select( - decl => decl.SourceFile.FilePath.ParentDirectoryPath).Distinct() - ) - -from groupOfTypes in lookup -let parentNamespaces = groupOfTypes.ParentNamespaces() - -// Select group of types (with source files stored in the same directory) … -// … but contained in several namespaces -where parentNamespaces.Count() > 1 - -// mainNamespaces is the namespace that contains many types -// declared in the directory groupOfTypes .key -let mainNamespace = groupOfTypes - .ToLookup(t => t.ParentNamespace) - .OrderByDescending(g => g.Count()).First().Key - -// Select types with source files stored in the same directory, -// but contained in namespaces different than mainNamespace. -let typesOutOfMainNamespace = groupOfTypes - .Where(t => t.ParentNamespace != mainNamespace && - t.ParentAssembly == mainNamespace.ParentAssembly) - - // Filter types declared on several source files that contain generated methods - // because typically such type contains one or several partial definitions generated. - // These partially generated types would be false positive for the present rule. - .Where(t => t.SourceDecls.Count() == 1 || - t.Methods.Count(m => JustMyCode.Contains(m)) == 0) -where typesOutOfMainNamespace.Count() > 0 - -let typesInMainNamespace = groupOfTypes.Where(t => t.ParentNamespace == mainNamespace) - -select new { - mainNamespace, - typesOutOfMainNamespace, - typesInMainNamespace, - Debt = (2+5*typesOutOfMainNamespace.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// For a solid code structure and organization, do mirror the namespaces -// hierarchy and the directories hierarchy containing source files. -// -// Doing so is a widely accepted convention, and not respecting this convention -// will lead to less maintainable and less browsable code. -// -// Respecting this convention means that types with source files stored in the same directory, -// should be declared in the same namespace. -// -// For each directory that contains several source files, where most types are declared -// in a namespace (what we call the **main namespace**) and a few types are declared -// out of the *main namespace*, this code rule matches: -// -// • The *main namespace* -// -// • **typesOutOfMainNamespace**: Types declared in source files in the *main namespace*'s directory -// but that are not in the *main namespace*. -// -// • *typesInMainNamespace*: And for informational purposes, types declared in source files in the -// *main namespace*'s directory, and that are in the *main namespace*. -// - -// -// Violations of this rule are types in the *typesOutOfMainNamespace* column. -// Typically such type … -// -// • … is contained in the wrong namespace but its source file is stored in the right directory. -// In such situation the type should be contained in *main namespace*. -// -// • … is contained in the right namespace but its source file is stored in the wrong directory -// In such situation the source file of the type must be moved to the proper parent namespace directory. -// -// • … is declared in multiple source files, stored in different directories. -// In such situation it is preferable that all source files are stored in a single directory. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 2 minutes plus 5 minutes per type in *typesOutOfMainNamespace*. -//]]> - Types declared in the same namespace, should have their source files stored in the same directory -// ND2105:TypesDeclaredInTheSameNamespaceShouldHaveTheirSourceFilesStoredInTheSameDirectory - -warnif count > 0 -from @namespace in Application.Namespaces - -// Group types of @namespace in a lookup -// where groups are keyed with directories that contain the types' source file(s). -// Note that a type can be contained in several groups -// if it is declared in several source files stored in different directories. -let lookup = @namespace.ChildTypes.Where( - t => t.SourceFileDeclAvailable && - JustMyCode.Contains(t) && - // Don't match ASP.NET application types declared in Global.asax file - // that typically are in the root directory of the VS project. - t.SourceDecls.First().SourceFile.FileNameWithoutExtension.ToLower() != "global.asax") - .ToMultiKeyLookup( - t => t.SourceDecls.Select( - decl => decl.SourceFile.FilePath.ParentDirectoryPath).Distinct() - ) - -// Are types of @namespaces declared in more than one directory? -where lookup.Count > 1 - -// Infer the main folder, preferably the one that has the same name as the namespace. -let dirs = lookup.Select(types => types.Key) -let mainDirNullable = dirs.Where(d => d.DirectoryName == @namespace.SimpleName).FirstOrDefault() -let mainDir = mainDirNullable ?? dirs.First() - -// Types declared out of mainDir, are types in group of types declared in a directory different than mainDir! -let typesDeclaredOutOfMainDir = - lookup.Where(types => types.Key != mainDir) - .SelectMany(types => types) - - // Filter types declared on several source files that contain generated methods - // because typically such type contains one or several partial definitions generated. - // These partially generated types would be false positive for the present rule. - .Where(t => t.SourceDecls.Count() == 1 || - t.Methods.Count(m => JustMyCode.Contains(m)) == 0) - -where typesDeclaredOutOfMainDir.Count() > 0 - -let typesDeclaredInMainDir = - lookup.Where(types => types.Key == mainDir) - .SelectMany(types => types) - -select new { - @namespace, - typesDeclaredOutOfMainDir, - mainDir = mainDir.ToString(), - typesDeclaredInMainDir, - Debt = (2+5*typesDeclaredOutOfMainDir.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// For a solid code structure and organization, -// do mirror the namespaces hierarchy and the directories hierarchy containing source files. -// -// Doing so is a widely accepted convention, and not respecting this convention -// will lead to less maintainable and less browsable code. -// -// Respecting this convention means that types declared in the same namespace, -// should have their source files stored in the same directory. -// -// For each namespace that contains types whose source files -// are declared in several directories, infer the **main directory**, -// the directory that naturally hosts source files of types, -// preferably the directory whose name corresponds with the namespace -// name. In this context, this code rule matches: -// -// • The namespace -// -// • **typesDeclaredOutOfMainDir**: types in the namespace whose source files -// are stored out of the *main directory*. -// -// • The *main directory* -// -// • *typesDeclaredInMainDir*: for informational purposes, types declared -// in the namespace, whose source files are stored in the *main directory*. -// - -// -// Violations of this rule are types in the **typesDeclaredOutOfMainDir** column. -// Typically such type… -// -// • … is contained in the wrong namespace but its source file is stored in the right directory. -// In such situation the type should be contained in the namespace corresponding to -// the parent directory. -// -// • … is contained in the right namespace but its source file is stored in the wrong directory. -// In such situation the source file of the type must be moved to the *main directory*. -// -// • … is declared in multiple source files, stored in different directories. -// In such situation it is preferable that all source files are stored in a single directory. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 2 minutes plus 5 minutes per type in *typesDeclaredOutOfMainDir*. -//]]> - - - - Mark ISerializable types with SerializableAttribute -// ND2200:MarkISerializableTypesWithSerializableAttribute - -warnif count > 0 - -from t in Application.Types where - t.IsPublic && - !t.IsDelegate && - !t.IsExceptionClass && // Don't match exceptions, since the Exception class - // implements ISerializable, this would generate - // too many false positives. - t.Implement ("System.Runtime.Serialization.ISerializable".AllowNoMatch()) && - !t.HasAttribute ("System.SerializableAttribute".AllowNoMatch()) - -select new { - t, - t.NbLinesOfCode, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// To be recognized by the CLR as serializable, -// types must be marked with the *SerializableAttribute* -// attribute even if the type uses a custom -// serialization routine through implementation of -// the *ISerializable* interface. -// -// This rule matches types that implement *ISerializable* and -// that are not tagged with *SerializableAttribute*. -// - -// -// To fix a violation of this rule, tag the matched type -// with *SerializableAttribute* . -//]]> - Mark assemblies with CLSCompliant (deprecated) -// ND2201:MarkAssembliesWithCLSCompliant - -warnif count > 0 from a in Application.Assemblies where - !a.HasAttribute ("System.CLSCompliantAttribute".AllowNoMatch()) -select new { - a, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule has been deprecated and, as a consequence, it is disabled by default. -// Feel free to re-enable it if it makes sense in your dev environment. -// -// The *Common Language Specification* (CLS) defines naming restrictions, -// data types, and rules to which assemblies must conform if they are to -// be used across programming languages. Good design dictates that all -// assemblies explicitly indicate CLS compliance with **CLSCompliantAttribute**. -// If the attribute is not present on an assembly, the assembly is not compliant. -// -// Notice that it is possible for a CLS-compliant assembly to contain types or -// type members that are not compliant. -// -// This rule matches assemblies that are not tagged with -// **System.CLSCompliantAttribute**. -// - -// -// To fix a violation of this rule, tag the assembly with *CLSCompliantAttribute*. -// -// Instead of marking the whole assembly as non-compliant, you should determine -// which type or type members are not compliant and mark these elements as such. -// If possible, you should provide a CLS-compliant alternative for non-compliant -// members so that the widest possible audience can access all the functionality -// of your assembly. -//]]> - Mark assemblies with ComVisible (deprecated) -// ND2202:MarkAssembliesWithComVisible - -warnif count > 0 from a in Application.Assemblies where - !a.HasAttribute ("System.Runtime.InteropServices.ComVisibleAttribute".AllowNoMatch()) -select new { - a, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule has been deprecated and, as a consequence, it is disabled by default. -// Feel free to re-enable it if it makes sense in your dev environment. -// -// The **ComVisibleAttribute** attribute determines how COM clients access -// managed code. Good design dictates that assemblies explicitly indicate -// COM visibility. COM visibility can be set for a whole assembly and then -// overridden for individual types and type members. If the attribute is not -// present, the contents of the assembly are visible to COM clients. -// -// This rule matches assemblies that are not tagged with -// **System.Runtime.InteropServices.ComVisibleAttribute**. -// - -// -// To fix a violation of this rule, tag the assembly with *ComVisibleAttribute*. -// -// If you do not want the assembly to be visible to COM clients, set the -// attribute value to **false**. -//]]> - Mark attributes with AttributeUsageAttribute -// ND2203:MarkAttributesWithAttributeUsageAttribute - -warnif count > 0 -from t in JustMyCode.Types where - - t.DeriveFrom ("System.Attribute".AllowNoMatch()) -&& !t.HasAttribute ("System.AttributeUsageAttribute".AllowNoMatch()) - -// AttributeUsageAttribute can be deferred to classes that derive from an abstract attribute class -&& !t.IsAbstract - -// AttributeUsageAttribute can de inherited from a base attribute class (that is not System.Attribute) -&& !t.BaseClasses.Any(c => - c.FullName != "System.Attribute" && - c.HasAttribute ("System.AttributeUsageAttribute".AllowNoMatch())) - -select new { - t, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// When you define a custom attribute, mark it by using **AttributeUsageAttribute** -// to indicate where in the source code the custom attribute can be applied. The -// meaning and intended usage of an attribute will determine its valid locations -// in code. For example, you might define an attribute that identifies the person -// who is responsible for maintaining and enhancing each type in a library, and -// that responsibility is always assigned at the type level. In this case, compilers -// should enable the attribute on classes, enumerations, and interfaces, but should -// not enable it on methods, events, or properties. Organizational policies and -// procedures would dictate whether the attribute should be enabled on assemblies. -// -// The **System.AttributeTargets** enumeration defines the targets that you can -// specify for a custom attribute. If you omit *AttributeUsageAttribute*, your -// custom attribute will be valid for all targets, as defined by the **All** value of -// *AttributeTargets* enumeration. -// -// This rule matches attribute classes that are not tagged with -// **System.AttributeUsageAttribute**. -// -// Abstract attribute classes are not matched since AttributeUsageAttribute -// can be deferred to derived classes. -// -// Attribute classes that have a base class tagged with AttributeUsageAttribute -// are not matched since in this case attribute usage is inherited. -// - -// -// To fix a violation of this rule, specify targets for the attribute by using -// *AttributeUsageAttribute* with the proper *AttributeTargets* values. -//]]> - Remove calls to GC.Collect() -// ND2204:RemoveCallsToGCCollect - -warnif count > 0 - -let gcCollectMethods = ThirdParty.Methods.WithFullNameWildcardMatch( - "System.GC.Collect(*)").ToHashSetEx() - -from m in Application.Methods.UsingAny(gcCollectMethods) -select new { - m, - gcCollectMethodCalled = m.MethodsCalled.Intersect(gcCollectMethods), - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// It is preferable to avoid calling **GC.Collect()** -// explicitly in order to avoid some performance pitfall. -// -// More in information on this here: -// http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx -// -// This rule matches application methods that call an -// overload of the method *GC.Collect()*. -// - -// -// Remove matched calls to *GC.Collect()*. -//]]> - Don't call GC.Collect() without calling GC.WaitForPendingFinalizers() -// ND2205:DontCallGCCollectWithoutCallingGCWaitForPendingFinalizers - -warnif count > 0 - -let gcCollectMethods = ThirdParty.Methods.WithFullNameWildcardMatch( - "System.GC.Collect(*)").ToHashSetEx() - -from m in Application.Methods.UsingAny(gcCollectMethods) where - !m.IsUsing ("System.GC.WaitForPendingFinalizers()".AllowNoMatch()) -select new { - m, - gcCollectMethodCalled = m.MethodsCalled.Intersect(gcCollectMethods), - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// It is preferable to avoid calling **GC.Collect()** -// explicitly in order to avoid some performance -// pitfall. This situation is checked through the -// default rules: *Remove calls to GC.Collect()* -// -// But if you wish to call *GC.Collect()* anyway, -// you must do it this way: -// -// GC.Collect(); -// -// GC.WaitForPendingFinalizers(); -// -// GC.Collect(); -// -// To make sure that finalizer got executed, and -// object with finalizer got cleaned properly. -// -// This rule matches application methods that call an -// overload of the method *GC.Collect()*, without calling -// *GC.WaitForPendingFinalizers()*. -// - -// -// To fix a violation of this rule, if you really -// need to call *GC.Collect()*, make sure to call -// *GC.WaitForPendingFinalizers()* properly. -//]]> - Enum Storage should be Int32 -// ND2206:EnumStorageShouldBeInt32 - -warnif count > 0 from f in JustMyCode.Fields where - f.ParentType.IsEnumeration && - f.Name == @"value__" && - f.FieldType != null && - f.FieldType.FullName != "System.Int32" && - !f.IsThirdParty -select new { - f, - f.SizeOfInst, - f.FieldType, - Debt = 7.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// An enumeration is a value type that defines a set of related named constants. -// By default, the **System.Int32** data type is used to store the constant value. -// -// Even though you can change this underlying type, it is not necessary or -// recommended for most scenarios. Note that *no significant performance gain* is -// achieved by using a data type that is smaller than *Int32*. If you cannot use -// the default data type, you should use one of the Common Language System -// (CLS)-compliant integral types, *Byte*, *Int16*, *Int32*, or *Int64* to make -// sure that all values of the enumeration can be represented in CLS-compliant -// programming languages. -// -// This rule matches enumerations whose underlying type used to store -// values is not *System.Int32*. -// - -// -// To fix a violation of this rule, unless size or compatibility issues exist, -// use *Int32*. For situations where *Int32* is not large enough to hold the values, -// use *Int64*. If backward compatibility requires a smaller data type, use -// *Byte* or *Int16*. -//]]> - Do not raise too general exception types -// ND2207:DoNotRaiseTooGeneralExceptionTypes - -warnif count > 0 - -let tooGeneralExceptionTypes = ThirdParty.Types.WithFullNameIn( - "System.Exception", - "System.ApplicationException", - "System.SystemException") - -from m in JustMyCode.Methods.ThatCreateAny(tooGeneralExceptionTypes) -// Make sure we don't match constructor of exception types -// that actually instantiate System.Exception. -where !m.IsConstructor || tooGeneralExceptionTypes.All(t => !m.ParentType.DeriveFrom(t)) -let exceptionsCreated = tooGeneralExceptionTypes.Where(t => m.IsUsing(t)) -select new { - m, - exceptionsCreated, - Debt = (15 + 5*exceptionsCreated.Count()).ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// The following exception types are too general -// to provide sufficient information to the user: -// -// • System.Exception -// -// • System.ApplicationException -// -// • System.SystemException -// -// If you throw such a general exception type in a library or framework, -// it forces consumers to catch all exceptions, -// including unknown exceptions that they do not know how to handle. -// -// This rule matches methods that create an instance of -// such general exception class. -// - -// -// To fix a violation of this rule, change the type of the thrown exception -// to either a more derived type that already exists in the framework, -// or create your own type that derives from *System.Exception*. -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 15 minutes per method matched, plus 5 minutes per too general -// exception types instantiated by the method. -//]]> - Do not raise reserved exception types -// ND2208:DoNotRaiseReservedExceptionTypes - -warnif count > 0 - -let reservedExceptions = ThirdParty.Types.WithFullNameIn( - "System.NullReferenceException", - "System.ExecutionEngineException", - "System.IndexOutOfRangeException", - "System.OutOfMemoryException", - "System.StackOverflowException", - "System.InvalidProgramException", - "System.AccessViolationException", - "System.CannotUnloadAppDomainException", - "System.BadImageFormatException", - "System.DataMisalignedException") - -from m in Application.Methods.ThatCreateAny(reservedExceptions) -let reservedExceptionsCreated = reservedExceptions.Where(t => m.IsUsing(t)) -select new { - m, - reservedExceptionsCreated, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// The following exception types are reserved -// and should be thrown only by the Common Language Runtime: -// -// • System.ExecutionEngineException -// -// • System.IndexOutOfRangeException -// -// • System.NullReferenceException -// -// • System.OutOfMemoryException -// -// • System.StackOverflowException -// -// • System.InvalidProgramException -// -// • System.AccessViolationException -// -// • System.CannotUnloadAppDomainException -// -// • System.BadImageFormatException -// -// • System.DataMisalignedException -// -// Do not throw an exception of such reserved type. -// -// This rule matches methods that create an instance of -// such reserved exception class. -// - -// -// To fix a violation of this rule, change the type of the -// thrown exception to a specific type that is not one of -// the reserved types. -// -// Concerning the particular case of a method throwing -// *System.NullReferenceException*, often the fix will be either -// to throw instead *System.ArgumentNullException*, either to -// use a contract (through MS Code Contracts API or *Debug.Assert()*) -// to signify that a null reference at that point can only be -// the consequence of a bug. -// -// More generally the idea of using a contract instead of throwing -// an exception in case of *corrupted state / bug consequence* detected -// is a powerful idea. It replaces a behavior (throwing exception) -// with a declarative assertion that basically means: at that point a bug -// somehow provoqued the detected corrupted state and continuing -// any processing from now is potentially harmful. The process should be -// shutdown and the circonstances of the failure should be reported -// as a bug to the product team. -//]]> - Uri fields should be of type System.Uri -// ND2209:UriFieldsShouldBeOfTypeSystemUri - -warnif count > 0 from f in Application.Fields where - (f.NameLike (@"Uri$") || - f.NameLike (@"Url$")) && - f.FieldType != null && - f.FieldType.FullName != "System.Uri" -select new { - f, - f.FieldType, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// A field with the name ending with *'Uri'* or *'Url'* is deemed -// to represent a *Uniform Resource Identifier or Locator*. -// Such field should be of type **System.Uri**. -// -// This rule matches fields with the name ending with *'Uri'* or -// *'Url'* that are not typed with *System.Uri*. -// - -// -// Rename the field, or change the field type to *System.Uri*. -// -// By default issues of this rule have a **Low** severity -// because they reflect more an advice than a problem. -//]]> - Types should not extend System.ApplicationException -// ND2210:TypesShouldNotExtendSystemApplicationException - -warnif count > 0 from t in Application.Types where - t.DeriveFrom("System.ApplicationException".AllowNoMatch()) -select new { - t, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// At .NET Framework version 1 time, it was -// recommended to derive new exceptions from -// *ApplicationException*. -// -// The recommendation has changed and new -// exceptions should derive from **System.Exception** -// or one of its subclasses in the *System* namespace. -// -// This rule matches application exception classes -// that derive from *ApplicationException*. -// - -// -// Make sure that matched exception types, -// derive from **System.Exception** or one of its -// subclasses in the *System* namespace. -//]]> - Don't Implement ICloneable -// ND2211:DontImplementICloneable - -warnif count > 0 -from t in Application.Types -where t.Implement("System.ICloneable".AllowNoMatch()) - - // Don't warn for classes that derive from a class that implements ICloneable - && !t.BaseClasses.Any(c => c.Implement("System.ICloneable".AllowNoMatch())) - -select new { - t, - Debt = 1.ToHours().ToDebt(), - Severity = Severity.High -} - -// -// The interface *System.ICloneable* is proposed since the first version of .NET. -// It is considered as a bad API for several reasons. -// -// First, this interface existed even before .NET 2.0 that introduced generics. -// Hence this interface is not generic and the *Clone()* method returns an *object* -// reference that needs to be *downcasted* to be useful. -// -// Second, this interface doesn't make explicit whether the cloning should be -// **deep** or **shallow**. The difference between the two behaviors -// is fundamental (explanation here https://stackoverflow.com/a/184745/27194). -// Not being able to make the intention explicit leads to confusion. -// -// Third, classes that derive from a class that implements *ICloneable* -// **must** also implement the *Clone()* method, and it is easy to forget -// this constraint. -// -// This rule doesn't warn for classes that derive from a class that -// implements ICloneable. In this case the issue must be fixed -// in the base class, or is maybe not fixable if the base class -// is declared in a third-party assembly. -// - -// -// Don't implement anymore this interface. -// -// You can rename the remaining *Clone()* methods to -// *DeepClone()* or *ShallowClone()* with a typed result. -// -// Or you can propose two custom generic interfaces -// *IDeepCloneable* with the single method *DeepClone():T* -// and *IShallowCloneable* with the single method *ShallowClone():T*. -// -// Finally you can write custom NDepend rules to make sure -// that all classes that derive from a class with a virtual -// clone method, override this method. -//]]> - - - Collection properties should be read only -// ND2300:CollectionPropertiesShouldBeReadOnly - -warnif count > 0 - -// First find collectionTypes -let collectionInterfaces = ThirdParty.Types.WithFullNameIn( - "System.Collections.ICollection", - "System.Collections.Generic.ICollection") -where collectionInterfaces.Count() > 0 -let collectionTypes = Types.ThatImplementAny(collectionInterfaces) - .Union(collectionInterfaces) - .ToHashSetEx() - -// Then find all property setters that have an associated -// getter that returns a collection type. -from propGetter in JustMyCode.Methods.Where( - m => m.IsPropertyGetter && - m.ReturnType != null && - collectionTypes.Contains(m.ReturnType)) - -let propSetter = propGetter.ParentType.Methods.WithSimpleName( - propGetter.SimpleName.Replace("get_","set_") - ).FirstOrDefault() - -where propSetter != null && - !propSetter.IsPrivate && // Ignore private setters since this is private implementation detail - !propSetter.ParentType.IsPrivate && // Ignore setters of private types - - // Ignore properties of serializable types - !propSetter.ParentType.TypesUsed.Any(t1 => t1.IsAttributeClass && t1.ParentNamespace.Name == "Newtonsoft.Json") && - !propSetter.ParentType.HasAttribute("System.Runtime.Serialization.DataContractAttribute".AllowNoMatch()) && - !propSetter.ParentType.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) - -select new { - propSetter, - CollectionType = propGetter.ReturnType, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// A writable collection property allows a user to replace the collection with -// a completely different collection. A read-only property stops the collection -// from being replaced but still allows the individual members to be set. If -// replacing the collection is a goal, the preferred *design pattern* is to include -// a method to remove all the elements from the collection and a method to -// re-populate the collection. See the *Clear()* and *AddRange()* methods of the -// *System.Collections.Generic.List* class for an example of this pattern. -// -// Both binary and XML serialization support read-only properties that are -// collections. The *System.Xml.Serialization.XmlSerializer* class has specific -// requirements for types that implement *ICollection* and *System.Collections.IEnumerable* -// in order to be serializable. -// -// This rule matches property setter methods that assign a collection object. -// - -// -// To fix a violation of this rule, make the property read-only and, if -// the design requires it, add methods to clear and re-populate the collection. -//]]> - Don't use .NET 1.x HashTable and ArrayList (deprecated) -// ND2301:DontUseDotNET1HashTableAndArrayList - -warnif count > 0 -let forbiddenTypes = ThirdParty.Types.WithFullNameIn( - "System.Collections.HashTable", - "System.Collections.ArrayList", - "System.Collections.Queue", - "System.Collections.Stack", - "System.Collections.SortedList") -where forbiddenTypes.Count() > 0 -from m in Application.Methods.ThatCreateAny(forbiddenTypes) -let forbiddenTypesUsed = m.MethodsCalled.Where(m1 => m1.IsConstructor && forbiddenTypes.Contains(m1.ParentType)).ParentTypes() -select new { - m, - forbiddenTypesUsed, - Debt = 15.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule has been deprecated and, as a consequence, it is disabled by default. -// Feel free to re-enable it if it makes sense in your dev environment. -// -// This rule warns about application methods that use a non-generic -// collection class, including **ArrayList**, **HashTable**, **Queue**, -// **Stack** or **SortedList**. -// - -// -// **List** should be preferred over **ArrayList**. -// It is generic hence you get strongly typed elements. -// Also, it is faster with *T* as a value types since it avoids boxing. -// -// For the same reasons: -// -// • **Dictionary** should be prevered over **HashTable**. -// -// • **Queue** should be prevered over **Queue**. -// -// • **Stack** should be prevered over **Stack**. -// -// • **SortedDictionary** or **SortedList** should be prevered over **SortedList**. -// -// You can be forced to use *non generic* collections -// because you are using third party code that requires -// working with these classes or because you are -// coding with .NET 1.x, but nowadays this situation should -// question about using newer updates of .NET. -// .NET 1.x is an immature platform conpared to newer .NET -// updates. -//]]> - Caution with List.Contains() -// ND2302:CautionWithListContains - -// warnif count > 0 // This query is n ot a rule per default - -let containsMethods = ThirdParty.Methods.WithFullNameIn( - "System.Collections.Generic.List.Contains(T)", - "System.Collections.Generic.IList.Contains(T)", - "System.Collections.ArrayList.Contains(Object)") - -from m in Application.Methods.UsingAny(containsMethods) -select new { - m, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// This code query matches calls to *List.Contains()* method. -// -// The cost of checking if a list contains an object is proportional -// to the size of the list. In other words it is a *O(N)* operation. -// For large lists and/or frequent calls to *Contains()*, prefer using -// the *System.Collections.Generic.HashSet* class -// where calls to *Contains()* take a constant -// time (*O(0)* operation). -// -// This code query is not a code rule, because more often than not, -// calling *O(N) Contains()* is not a mistake. This code query -// aims at pointing out this potential performance pitfall. -//]]> - Prefer return collection abstraction instead of implementation -// ND2303:PreferReturnCollectionAbstractionInsteadOfImplementation - -// warnif count > 0 // This query is n ot a rule per default - -let implTypes = ThirdParty.Types.WithFullNameIn( - "System.Collections.Generic.List", - "System.Collections.Generic.HashSet", - "System.Collections.Generic.Dictionary") - -from m in Application.Methods.WithReturnTypeIn(implTypes) -select new { - m, - m.ReturnType, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Low -} - -// -// This code query matches methods that return a -// collection implementation, such as *List* -// *HashSet* or *Dictionary*. -// -// Most often than not, clients of a method don't -// need to know the exact implementation of the -// collection returned. It is preferable to return -// a collection interface such as *IList*, -// *ICollection*, *IEnumerable* or -// *IDictionary*. -// -// Using the collection interface instead of the -// implementation shouldn't applies to all cases, -// hence this code query is not code rule. -//]]> - - - P/Invokes should be static and not be publicly visible -// ND2400:PInvokesShouldBeStaticAndNotBePubliclyVisible - -warnif count > 0 from m in Application.Methods where - !m.IsThirdParty && - (m.HasAttribute ("System.Runtime.InteropServices.DllImportAttribute".AllowNoMatch())) && - ( m.IsPubliclyVisible || - !m.IsStatic) -select new { - m, - m.Visibility, - m.IsStatic, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// Methods that are marked with the **DllImportAttribute** attribute -// (or methods that are defined by using the **Declare** keyword in Visual Basic) -// use **Platform Invocation Services** to access unmanaged code. -// -// Such methods should not be exposed. By keeping these methods *private* or *internal*, -// you make sure that your library cannot be used to breach security by allowing -// callers access to unmanaged APIs that they could not call otherwise. -// -// This rule matches methods tagged with *DllImportAttribute* attribute -// that are declared as *public* or declared as *non-static*. -// - -// -// To fix a violation of this rule, change the access level of the method -// and/or declare it as static. -//]]> - Move P/Invokes to NativeMethods class -// ND2401:MovePInvokesToNativeMethodsClass - -warnif count > 0 from m in Application.Methods where - m.HasAttribute ("System.Runtime.InteropServices.DllImportAttribute".AllowNoMatch()) && - m.ParentType.SimpleName != "NativeMethods" -select new { - m, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// **Platform Invocation methods**, such as those that are marked by using the -// **System.Runtime.InteropServices.DllImportAttribute** attribute, or methods -// that are defined by using the **Declare** keyword in Visual Basic, access -// unmanaged code. These methods should be in one of the following classes: -// -// • **NativeMethods** - This class does not suppress stack walks for unmanaged -// code permission. (*System.Security.SuppressUnmanagedCodeSecurityAttribute* -// must not be applied to this class.) This class is for methods that can be -// used anywhere because a stack walk will be performed. -// -// • **SafeNativeMethods** - This class suppresses stack walks for unmanaged -// code permission. (*System.Security.SuppressUnmanagedCodeSecurityAttribute* -//is applied to this class.) This class is for methods that are safe for anyone -// to call. Callers of these methods are not required to perform a full security -// review to make sure that the usage is secure because the methods are harmless -// for any caller. -// -// • **UnsafeNativeMethods** - This class suppresses stack walks for unmanaged -// code permission. (*System.Security.SuppressUnmanagedCodeSecurityAttribute* -// is applied to this class.) This class is for methods that are potentially -// dangerous. Any caller of these methods must perform a full security review -// to make sure that the usage is secure because no stack walk will be performed. -// -// These classes are declared as *static internal*. The methods in these -// classes are *static* and *internal*. -// -// This rule matches *P/Invoke* methods not declared in such *NativeMethods* -// class. -// - -// -// To fix a violation of this rule, move the method to the appropriate -// **NativeMethods** class. For most applications, moving P/Invokes to a new -// class that is named **NativeMethods** is enough. -//]]> - NativeMethods class should be static and internal -// ND2402:NativeMethodsClassShouldBeStaticAndInternal - -warnif count > 0 from t in Application.Types.WithNameIn( - @"NativeMethods", "SafeNativeMethods", "UnsafeNativeMethods") where - t.IsPublic || !t.IsStatic -select new { - t, - t.Visibility, - t.IsStatic, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// In the description of the default rule *Move P/Invokes to NativeMethods class* -// it is explained that *NativeMethods* classes that host *P/Invoke* methods, -// should be declared as *static* and *internal*. -// -// This code rule warns about *NativeMethods* classes that are not declared -// *static* and *internal*. -// - -// -// Matched *NativeMethods* classes must be declared as *static* and *internal*. -//]]> - - - Don't create threads explicitly -// ND2500:DontCreateThreadsExplicitly - -warnif count > 0 from m in Application.Methods where - m.CreateA ("System.Threading.Thread".AllowNoMatch()) -select new { - m, - Debt = 60.ToMinutes().ToDebt(), - Severity = Severity.Critical -} - -// -// This code rule warns about methods that create *threads* explicitly -// by creating an instance of the class *System.Threading.Thread*. -// -// Prefer using the thread pool instead of creating manually your -// own threads. Threads are costly objects. They take approximately -// 200,000 cycles to create and about 100,000 cycles to destroy. -// By default they reserve 1 Mega Bytes of virtual memory for its -// stack and use 2,000-8,000 cycles for each context switch. -// -// As a consequence, it is preferable to let the thread pool -// recycle threads. -// - -// -// Instead of creating explicitly threads, use the **Task Parralel -// Library** *(TPL)* that relies on the CLR thread pool. -// -// Introduction to TPL: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx -// -// TPL and the CLR v4 thread pool: -// http://www.danielmoth.com/Blog/New-And-Improved-CLR-4-Thread-Pool-Engine.aspx -// -// By default issues of this rule have a **Critical** severity -// because creating threads can have severe consequences. -//]]> - Don't use dangerous threading methods -// ND2501:DontUseDangerousThreadingMethods - -warnif count > 0 - -let wrongMethods = ThirdParty.Methods.WithFullNameIn( - - "System.Threading.Thread.Abort()", - "System.Threading.Thread.Abort(Object)", - - "System.Threading.Thread.Sleep(Int32)", - - "System.Threading.Thread.Suspend()", - "System.Threading.Thread.Resume()") - -from m in Application.Methods.UsingAny(wrongMethods) -select new { - m, - suppressCallsTo = m.MethodsCalled.Intersect(wrongMethods), - Debt = 40.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule warns about using the methods -// *Abort()*, *Sleep()*, *Suspend()* or *Resume()* -// declared by the *Thread* class. -// -// • Usage of *Thread.Abort()* is dangerous. -// More information on this here: -// http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation -// -// • Usage of *Thread.Sleep()* is a sign of -// flawed design. More information on this here: -// http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx -// -// • *Suspend()* and *Resume()* are dangerous threading methods, marked as obsolete. -// More information on workaround here: -// http://stackoverflow.com/questions/382173/what-are-alternative-ways-to-suspend-and-resume-a-thread -// - -// -// Suppress calls to *Thread* methods exposed in the -// *suppressCallsTo* column in the rule result. -// -// Use instead facilities offered by the **Task Parralel -// Library** *(TPL)* : -// https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx -//]]> - Monitor TryEnter/Exit must be both called within the same method -// ND2502:MonitorTryEnterExitMustBeBothCalledWithinTheSameMethod - -warnif count > 0 - -let enterMethods = ThirdParty.Methods.WithFullNameWildcardMatchIn( - "System.Threading.Monitor.Enter(*", - "System.Threading.Monitor.TryEnter(*") - -from m in Application.Methods.UsingAny(enterMethods) -where - !m.IsUsing ("System.Threading.Monitor.Exit(Object)".AllowNoMatch()) -select new { - m, - enterMethodsCalled = m.MethodsCalled.Intersect(enterMethods), - Debt = 20.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule warns when **System.Threading.Monitor** *Enter()* -// (or *TryEnter()*) and *Exit() methods are not called within -// the same method. -// -// Doing so makes the code *less readable*, because it gets harder -// to locate when **critical sections** begin and end. -// -// Also, you expose yourself to complex and error-prone scenarios. -// - -// -// Refactor matched methods to make sure that *Monitor critical -// sections* begin and end within the same method. Basics scenarios -// can be handled through the C# **lock** keyword. Using explicitly -// the class *Monitor* should be left for advanced situations, -// that require calls to methods like *Wait()* and *Pulse()*. -// -// More information on using the *Monitor* class can be found here: -// http://www.codeproject.com/Articles/13453/Practical-NET-and-C-Chapter -//]]> - ReaderWriterLock AcquireLock/ReleaseLock must be both called within the same method -// ND2503:ReaderWriterLockAcquireLockReleaseLockMustBeBothCalledWithinTheSameMethod - -warnif count > 0 - -let acquireLockMethods = ThirdParty.Methods.WithFullNameWildcardMatch( - "System.Threading.ReaderWriterLock.Acquire*Lock(*") - -let releaseLockMethods = ThirdParty.Methods.WithFullNameWildcardMatch( - "System.Threading.ReaderWriterLock.Release*Lock(*") - -from m in Application.Methods.UsingAny(acquireLockMethods) - .Except(Application.Methods.UsingAny(releaseLockMethods)) -select new { - m, - acquireLockMethods = m.MethodsCalled.Intersect(acquireLockMethods), - Debt = 20.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule warns when **System.Threading.ReaderWriterLock** -// acquire and release, reader or writer locks methods are not called -// within the same method. -// -// Doing so makes the code *less readable*, because it gets harder -// to locate when **critical sections** begin and end. -// -// Also, you expose yourself to complex and error-prone scenarios. -// - -// -// Refactor matched methods to make sure that *ReaderWriterLock -// read or write critical sections* begin and end within the -// same method. -//]]> - Don't tag instance fields with ThreadStaticAttribute -// ND2504:DontTagInstanceFieldsWithThreadStaticAttribute - -warnif count > 0 -from f in Application.Fields -where !f.IsStatic && - f.HasAttribute ("System.ThreadStaticAttribute".AllowNoMatch()) -select new { - f, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule warns when the attribute **System.ThreadStaticAttribute** -// is tagging *instance* fields. As explained in documentation, this attribute -// is designed to tag only *static* fields. -// https://msdn.microsoft.com/en-us/library/system.threadstaticattribute -// - -// -// Refactor the code to make sure that all fields tagged with -// *ThreadStaticAttribute* are *static*. -//]]> - Method non-synchronized that read mutable states -// ND2505:MethodNonSynchronizedThatReadMutableStates - -from m in Application.Methods where - (m.ReadsMutableObjectState || m.ReadsMutableTypeState) && - !m.IsUsing ("System.Threading.Monitor".AllowNoMatch()) && - !m.IsUsing ("System.Threading.ReaderWriterLock".AllowNoMatch()) -select new { - m, - mutableFieldsUsed = m.FieldsUsed.Where(f => !f.IsImmutable) -} - -// -// Mutable object states are instance fields that -// can be modified through the lifetime of the object. -// -// Mutable type states are static fields that can be -// modified through the lifetime of the program. -// -// This query lists methods that read mutable state -// without synchronizing access. In the case of -// multi-threaded program, doing so can lead to -// state corruption. -// -// This code query is not a code rule because more often -// than not, a match of this query is not an issue. -//]]> - - - Method should not return concrete XmlNode -// ND2600:MethodShouldNotReturnConcreteXmlNode - -warnif count > 0 - -let concreteXmlTypes = ThirdParty.Types.ThatDeriveFromAny( - ThirdParty.Types.WithFullName("System.Xml.XmlNode")) - -from m in Application.Methods.WithReturnTypeIn(concreteXmlTypes) -select new { - m, - m.ReturnType, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns about method whose return type is -// **System.Xml.XmlNode** or any type derived from *XmlNode*. -// -// *XmlNode* implements the interface **System.Xml.Xpath.IXPathNavigable**. -// In most situation, returning this interface instead of the concrete -// type is a better *design* choice that will abstract client code -// from implementation details. -// - -// -// To fix a violation of this rule, change the concrete returned type -// to the suggested interface *IXPathNavigable* and refactor clients -// code if possible. -//]]> - Types should not extend System.Xml.XmlDocument -// ND2601:TypesShouldNotExtendSystemXmlXmlDocument - -warnif count > 0 from t in Application.Types where - t.DeriveFrom("System.Xml.XmlDocument".AllowNoMatch()) -select new { - t, - Debt = 20.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule warns aboud subclasses of **System.Xml.XmlDocument**. -// -// Do not create a subclass of *XmlDocument* if you want to -// create an XML view of an underlying object model or data source. -// - -// -// Instead of subclassing *XmlDocument*, you can use the interface -// **System.Xml.XPath.IXPathNavigable** implemented by the class -// *XmlDocument*. -// -// An alternative of using *XmlDocument*, is to use -// **System.Xml.Linq.XDocument**, aka **LINQ2XML**. -// More information on this can be found here: -// http://stackoverflow.com/questions/1542073/xdocument-or-xmldocument -//]]> - - - Float and Date Parsing must be culture aware -// ND2700:FloatAndDateParsingMustBeCultureAware - -warnif count > 0 - -let cultureUnawareMethods = - (from m in ThirdParty.Types.WithFullNameIn( - "System.DateTime", - "System.Single", - "System.Double", - "System.Decimal").ChildMethods() - where m.NbParameters > 0 && - (m.SimpleName.EqualsAny( - "Parse", "TryParse", "ToString")) && - !m.Name.Contains("IFormatProvider") - select m).ToHashSetEx() - -from m in Application.Methods.UsingAny(cultureUnawareMethods) -let cultureUnawareMethodsCalled = m.MethodsCalled.Intersect(cultureUnawareMethods) -select new { - m, - shouldntCall = cultureUnawareMethodsCalled, - Debt = (5 + 3*cultureUnawareMethodsCalled.Count()).ToMinutes().ToDebt(), - Severity = 5*cultureUnawareMethodsCalled.Count().ToMinutes().ToAnnualInterest() -} - -// -// Globalization is the design and development of applications that support -// localized user interfaces and regional data for users in multiple cultures. -// -// This rule warns about the usage of *non-globalized overloads* of -// the methods **Parse()**, **TryParse()** and **ToString()**, -// of the types **DateTime**, **float**, **double** and **decimal**. -// This is the symptom that your application is *at least partially* -// not globalized. -// -// *Non-globalized overloads* of these methods are the overloads -// that don't take a parameter of type **IFormatProvider**. -// - -// -// Globalize your applicaton and make sure to use the globalized overloads -// of these methods. In the column **MethodsCallingMe** of this rule result -// are listed the methods of your application that call the -// *non-globalized overloads*. -// -// More information on **Creating Globally Aware Applications** here: -// https://msdn.microsoft.com/en-us/library/cc853414(VS.95).aspx -// -// The estimated Debt, which means the effort to fix such issue, -// is equal to 5 minutes per application method calling at least one -// non-culture aware method called, plus 3 minutes per non-culture aware -// method called. -//]]> - - - Mark assemblies with assembly version -// ND2800:MarkAssembliesWithAssemblyVersion - -warnif count > 0 from a in Application.Assemblies where - !a.HasAttribute ("System.Reflection.AssemblyVersionAttribute".AllowNoMatch()) -select new { - a, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// The identity of an assembly is composed of the following information: -// -// • Assembly name -// -// • Version number -// -// • Culture -// -// • Public key (for strong-named assemblies). -// -// The .NET Framework uses the version number to uniquely identify an -// assembly, and to bind to types in strong-named assemblies. The -// version number is used together with version and publisher policy. -// By default, applications run only with the assembly version with -// which they were built. -// -// This rule matches assemblies that are not tagged with -// **System.Reflection.AssemblyVersionAttribute**. -// - -// -// To fix a violation of this rule, add a version number to the assembly -// by using the *System.Reflection.AssemblyVersionAttribute* attribute. -//]]> - Assemblies should have the same version -// ND2801:AssembliesShouldHaveTheSameVersion - -warnif count > 0 -let versionsLookup = Application.Assemblies.ToLookup(a => a.Version, a=> a) -let mostRepresentedVersion = versionsLookup.OrderByDescending(v => v.Count()).First().Key -from v in versionsLookup -where v.Key != mostRepresentedVersion -from a in v.ToArray() -select new { - a , - version = v.Key, - mostRepresentedVersion, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule reports application assemblies that have a version different -// than the version shared by most of application assemblies. -// -// Before fixing these issues, double check if there is a valid reason -// for dealing with more than one assembly version number. -// Typically this happens when the analyzed code base is made of assemblies -// that are not *compiled, developed or deployed* together. -// - -// -// If all assemblies of your application should have the same version number, -// just use the attribute **System.Reflection.AssemblyVersion** in a source -// file shared by the assemblies. -// -// Typically this source file is generated by a dedicated *MSBuild* task -// like this one http://www.msbuildextensionpack.com/help/4.0.5.0/html/d6c3b5e8-00d4-c826-1a73-3cfe637f3827.htm. -// -// Here you can find interesting assemblies versioning advices. -// http://stackoverflow.com/a/3905443/27194 -// -// By default issues of this rule have a severity set to **major** since -// unproper assemblies versioning can lead to complicated deployment problem. -//]]> - - - Public methods returning a reference needs a contract to ensure that a non-null reference is returned -// ND2900:PublicMethodsReturningAReferenceNeedsAContractToEnsureThatANonNullReferenceIsReturned - -warnif count > 0 -let ensureMethods = Application.Methods.WithFullName( - "System.Diagnostics.Contracts.__ContractsRuntime.Ensures(Boolean,String,String)") - -from ensureMethod in ensureMethods -from m in ensureMethod.ParentAssembly.ChildMethods where - m.IsPubliclyVisible && - !m.IsAbstract && - m.ReturnType != null && - // Identify that the return type is a reference type - (m.ReturnType.IsClass || m.ReturnType.IsInterface) && - !m.IsUsing(ensureMethod) && - - // Don't match method not implemented yet! - !m.CreateA("System.NotImplementedException".AllowNoMatch()) - -select new { - m, - ReturnTypeReference = m.ReturnType, - Debt = (5+3*m.MethodsCallingMe.Count()).ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// **Code Contracts** are useful to decrease ambiguity between callers and callees. -// Not ensuring that a reference returned by a method is *non-null* leaves ambiguity -// for the caller. This rule matches methods returning an instance of a reference type -// (class or interface) that doesn't use a **Contract.Ensure()** method. -// -// *Contract.Ensure()* is defined in the **Microsoft Code Contracts for .NET** -// library, and is typically used to write a code contract on returned reference: -// *Contract.Ensures(Contract.Result() != null, "returned reference is not null");* -// https://visualstudiogallery.msdn.microsoft.com/1ec7db13-3363-46c9-851f-1ce455f66970 -// - -// -// Use *Microsoft Code Contracts for .NET* on the public surface of your API, -// to remove most ambiguity presented to your client. Most of such ambiguities -// are about *null* or *not null* references. -// -// Don't use *null* reference if you need to define a method that might not -// return a result. Use instead the **TryXXX()** pattern exposed for example -// in the *System.Int32.TryParse()* method. -// -// The estimated Debt, which means the effort to fix such issue, is equal -// to 5 minutes per public application method that might return a null reference -// plus 3 minutes per method calling such method. -//]]> - - - - Discard generated Assemblies from JustMyCode -// ND3000:DiscardGeneratedAssembliesFromJustMyCode - -notmycode -from a in Application.Assemblies where -// Assemblies generated for Xsl IL compilation for example are tagged with this attribute -a.HasAttribute ("System.CodeDom.Compiler.GeneratedCodeAttribute".AllowNoMatch()) -select a - -// -// This code query is prefixed with **notmycode**. -// This means that all application assemblies matched by this -// code query are removed from the *code base view* **JustMyCode.Assemblies**. -// It also means that all *namespaces*, *types*, *methods* and -// *fields* contained in a matched assembly are removed from -// the code base view *JustMyCode*. -// The code base view *JustMyCode* is used by most default code queries -// and rules. -// -// So far this query only matches application assemblies tagged -// with *System.CodeDom.Compiler.GeneratedCodeAttribute*. -// Make sure to make this query richer to discard your generated -// assemblies from the NDepend rules results. -// -// *notmycode* queries are executed before running others -// queries and rules. Also modifying a *notmycode* query -// provokes re-run of queries and rules that rely -// on the *JustMyCode* code base view. -// -// Several *notmycode* queries can be written to match *assemblies*, -// in which case this results in cumulative effect. -// -// Online documentation: -// https://www.ndepend.com/docs/cqlinq-syntax#NotMyCode -//]]> - Discard generated Namespaces from JustMyCode -// ND3001:DiscardGeneratedNamespacesFromJustMyCode - -notmycode - -// First gather assemblies written with VB.NET -let vbnetAssemblies = Application.Assemblies.Where( - a => a.SourceDecls.Any(decl => decl.SourceFile.FileNameExtension.ToLower() == ".vb")) -// Then find the My namespace and its child namespaces. -let vbnetMyNamespaces = vbnetAssemblies.ChildNamespaces().Where( - n => n.SimpleName == "My" || - n.ParentNamespaces.Any(nParent => nParent.SimpleName == "My")) - - -let generatedNamespace = Application.Namespaces.WithFullNameIn( - // COM/Import mshtml namespace and all its types are not-my-code - "mshtml", - // Roslyn can generate types in these namespaces, consider them as not-my-code - "Microsoft.CodeAnalysis", - "System.Runtime.CompilerServices" - -).Concat(Application.Namespaces.WithSimpleNameIn( - // Don't match types generated by the Refit infrastructure like the class PreserveAttribute - "RefitInternalGenerated")) - -// Discard types generated in Microsoft.Office.* namespaces. -// Issues won't be reported on this code. Especially issues related -// to the rule "Interface name should begin with a 'I'" broken -// by many interfaces in these namespaces. -let microsoftOffices = Application.Namespaces.Where(n => n.Name.StartsWith("Microsoft.Office")) - -from n in vbnetMyNamespaces.Concat(generatedNamespace).Concat(microsoftOffices) -select n - -// -// This code query is prefixed with **notmycode**. -// This means that all application namespaces matched by this -// code query are removed from the *code base view* **JustMyCode.Namespaces**. -// It also means that all *types*, *methods* and *fields* contained in a -// matched namespace are removed from the code base view *JustMyCode*. -// The code base view *JustMyCode* is used by most default code queries -// and rules. -// -// So far this query matches the **My** namespaces generated -// by the VB.NET compiler. This query also matches namespaces named -// **mshtml** or **Microsoft.Office.*** that are generated by tools. -// -// *notmycode* queries are executed before running others -// queries and rules. Also modifying a *notmycode* query -// provokes re-run of queries and rules that rely -// on the *JustMyCode* code base view. -// -// Several *notmycode* queries can be written to match *namespaces*, -// in which case this results in cumulative effect. -// -// Online documentation: -// https://www.ndepend.com/docs/cqlinq-syntax#NotMyCode -//]]> - Discard generated Types from JustMyCode -// ND3002:DiscardGeneratedTypesFromJustMyCode - -notmycode - -// Define some sets to quickly test EntityFramework generated types -let efContexts = Application.Types.Where(t => - t.DeriveFrom("System.Data.Entity.DbContext".AllowNoMatch()) && - t.SourceDecls.Count(sd => sd.SourceFile.FileName.ToLower().EndsWithAny(".context.cs", ".context.vb")) == 1).ToHashSetEx() -let efEntities = Application.Types.UsedByAny(efContexts).ToHashSetEx() -let efMigrations = Application.Types.Where(t => t.DeriveFrom("System.Data.Entity.Migrations.DbMigration".AllowNoMatch())).ToHashSetEx() -let efModelSnapshots = Application.Types.Where(t => t.DeriveFrom("Microsoft.EntityFrameworkCore.Infrastructure.ModelSnapshot".AllowNoMatch())).ToHashSetEx() - -from t in Application.Types where - - // Don't consider anonymous types as JustMyCode - // C# and VB.NET anonymous types generated by the compiler satisfies these conditions - (t.IsGeneratedByCompiler && - t.ParentNamespace.Name.Length == 0 && - t.SimpleNameLike("AnonymousType")) || - - // Resources, Settings, or typed DataSet generated types for example, are tagged with this attribute - t.HasAttribute ("System.CodeDom.Compiler.GeneratedCodeAttribute".AllowNoMatch()) || - - // This attribute identifies a type or member that is not part of the user code for an application. - t.HasAttribute ("System.Diagnostics.DebuggerNonUserCodeAttribute".AllowNoMatch()) || - - // This attribute identifies a type that is defined with COM, elsewhere, so consider it not-my-code. - t.HasAttribute ("System.Runtime.InteropServices.GuidAttribute".AllowNoMatch()) || - - // Delegate types are always generated - t.IsDelegate || - - // Discard ASP.NET page types generated by aspnet_compiler.exe - // See: https://www.ndepend.com/FAQ.aspx#ASPNET - t.ParentNamespace.Name.EqualsAny("ASP", "__ASP") || - - // Discard ASP.NET special types - (t.SimpleName.EqualsAny("Startup","BundleConfig","RouteConfig") && - ThirdParty.Assemblies.WithNameWildcardMatchIn("System.Web*", "Microsoft.AspNetCore*").Any()) || - - // Discard DataSet classes and their nested types - (t.DeriveFrom("System.Data.DataSet".AllowNoMatch()) && - t.HasAttribute("System.ComponentModel.DesignerCategoryAttribute".AllowNoMatch())) || - (t.IsNested && t.ParentType != null && - t.ParentType.DeriveFrom("System.Data.DataSet".AllowNoMatch()) && - t.ParentType.HasAttribute("System.ComponentModel.DesignerCategoryAttribute".AllowNoMatch())) || - - // Discard DataSet TableAdapterManager classes their nested types - (t.SimpleName == "TableAdapterManager" && - t.DeriveFrom("System.ComponentModel.Component".AllowNoMatch())) || - (t.IsNested && t.ParentType != null && - t.ParentType.SimpleName == "TableAdapterManager" && - t.ParentType.DeriveFrom("System.ComponentModel.Component".AllowNoMatch())) || - - // Discard Xamarin form generated types that contain the method LoadDataTemplate() - (t.IsNested && - t.SimpleName.StartsWith("") && - t.Methods.Count(m => m.SimpleName == "LoadDataTemplate") == 1 && - t.ParentAssembly.AssembliesUsed.Count(a => a.Name.StartsWith("Xamarin")) > 0) || - - // Discard Xamarin Resource types - (t.IsNested && - t.ParentType != null && - t.ParentType.Name == "Resource" && - t.ParentAssembly.AssembliesUsed.Count(a => a.Name.StartsWith("Xamarin")) > 0) || - - // Discard Entity Framework generated DB context types and entities types used by DB context types! - efContexts.Contains(t) || - efEntities.Contains(t) || - efMigrations.Contains(t) || - efModelSnapshots.Contains(t) || - - // Discard types generated for code contract - t.FullName.StartsWith("System.Diagnostics.Contracts.__ContractsRuntime") || - t.FullName == "System.Diagnostics.Contracts.RuntimeContractsAttribute" || - - // Discard all types declared in a folder path containing the word "generated" - (t.SourceFileDeclAvailable && - t.SourceDecls.All(s => s.SourceFile.FilePath.ParentDirectoryPath.ToString().ToLower().Contains("generated"))) || - - // Types created by the test infrastructure - t.FullName == "AutoGeneratedProgram" || - - // Discard special types generated by the Roslyn compiler that are typically used by several methods - // and as a consequence, cannot be merged in application code through the option - // NDepend Project Properties > Analysis > Merge Code Generated by Compiler into Application Code - (t.IsGeneratedByCompiler && t.IsNested && t.SimpleName.StartsWith("<>c")) - -select t - -// -// This code query is prefixed with **notmycode**. -// This means that all application types matched by this -// code query are removed from the *code base view* **JustMyCode.Types**. -// It also means that all *methods* and *fields* contained in a -// matched type are removed from the code base view *JustMyCode*. -// The code base view *JustMyCode* is used by most default code queries -// and rules. -// -// So far this query matches several well-identified generated -// types, like the ones tagged with *System.CodeDom.Compiler.GeneratedCodeAttribute*. -// Make sure to make this query richer to discard your generated -// types from the NDepend rules results. -// -// *notmycode* queries are executed before running others -// queries and rules. Also modifying a *notmycode* query -// provokes re-run of queries and rules that rely -// on the *JustMyCode* code base view. -// -// Several *notmycode* queries can be written to match *types*, -// in which case this results in cumulative effect. -// -// Online documentation: -// https://www.ndepend.com/docs/cqlinq-syntax#NotMyCode -//]]> - Discard generated and designer Methods from JustMyCode -// ND3003:DiscardGeneratedAndDesignerMethodsFromJustMyCode - -notmycode - -// -// First define source files paths to discard -// -from a in Application.Assemblies -where a.SourceFileDeclAvailable -let asmSourceFilesPaths = a.SourceDecls.Select(s => s.SourceFile.FilePath) - -let sourceFilesPathsToDiscard = ( - from filePath in asmSourceFilesPaths - let filePathLower= filePath.ToString().ToLower() - where - filePathLower.EndsWithAny( - ".g.cs", // Popular pattern to name generated files. - ".g.vb", - ".generated.cs", - ".generated.vb") || - filePathLower.EndsWithAny( - ".xaml", // notmycode WPF xaml code - ".designer.cs", // notmycode C# Windows Forms designer code - ".designer.vb") // notmycode VB.NET Windows Forms designer code - || - // notmycode methods in source files in a directory containing generated - filePathLower.Contains("generated") - select filePath -).ToHashSetEx() - -// -// Second: discard methods in sourceFilesPathsToDiscard -// -from m in a.ChildMethods -where (m.SourceFileDeclAvailable && - sourceFilesPathsToDiscard.Contains(m.SourceDecls.First().SourceFile.FilePath)) || - // Generated methods might be tagged with this attribute - m.HasAttribute ("System.CodeDom.Compiler.GeneratedCodeAttribute".AllowNoMatch()) || - - // This attributes identifies a type or member that is not part of the user code for an application. - m.HasAttribute ("System.Diagnostics.DebuggerNonUserCodeAttribute".AllowNoMatch()) || - - // Event adder/remover methods generated by the compiler. - ((m.IsEventAdder || m.IsEventRemover) && !m.SourceFileDeclAvailable) || - - // Default/implicit constructor generated by the compiler on class and structures that don't have constructor - (m.IsConstructor && - m.NbParameters == 0 && - (m.IsPublic || (m.IsProtected && m.ParentType.IsAbstract)) && - !m.SourceFileDeclAvailable) - -select new { m, m.NbLinesOfCode } - -// -// This code query is prefixed with **notmycode**. -// This means that all application methods matched by this -// code query are removed from the *code base view* **JustMyCode.Methods**. -// The code base view *JustMyCode* is used by most default code queries -// and rules. -// -// So far this query matches several well-identified generated -// methods, like the ones tagged with *System.CodeDom.Compiler.GeneratedCodeAttribute*, -// or the ones declared in a source file suffixed with *.designer.cs*. -// Make sure to make this query richer to discard your generated -// methods from the NDepend rules results. -// -// *notmycode* queries are executed before running others -// queries and rules. Also modifying a *notmycode* query -// provokes re-run of queries and rules that rely -// on the *JustMyCode* code base view. -// -// Several *notmycode* queries can be written to match *methods*, -// in which case this results in cumulative effect. -// -// Online documentation: -// https://www.ndepend.com/docs/cqlinq-syntax#NotMyCode -//]]> - Discard generated Fields from JustMyCode -// ND3004:DiscardGeneratedFieldsFromJustMyCode - -notmycode - -// Define WindowsForm fields defined as fields assigned by the InitializeComponent() WindowsForm methods. -let winFormInitializeComponentsMethods = - Application.Methods.WithName("InitializeComponent()") - .Where(m => m.ParentType.BaseClass != null && m.ParentType.BaseClass.ParentNamespace.Name == "System.Windows.Forms") -let winFormFields = winFormInitializeComponentsMethods.SelectMany(m => m.FieldsAssigned).ToHashSetEx() - -from f in Application.Fields where - - // Discard WindowsForm fields - winFormFields.Contains(f) || - - // Eliminate "components" generated in Windows Form Control context - (f.Name == "components" && f.ParentType.DeriveFrom("System.Windows.Forms.Control".AllowNoMatch())) || - - // Eliminate tagHelper generated fields - f.Name == "_tagHelperStringValueBuffer" || - - // Eliminate XAML generated fields - // IComponentConnector is XAML specific and is automatically implemented for every Window, Page and UserControl. - f.ParentType.Implement( "System.Windows.Markup.IComponentConnector".AllowNoMatch()) || // WPF IComponentConnector - f.ParentType.Implement("Windows.UI.Xaml.Markup.IComponentConnector".AllowNoMatch()) || // UWP IComponentConnector - - f.HasAttribute ("System.CodeDom.Compiler.GeneratedCodeAttribute".AllowNoMatch()) || - - // Property backing fields generated by the compiler - (f.IsGeneratedByCompiler && !f.IsEventDelegateObject) || - - // Match fields generated by the ASP.NET infrastructure - // in System.Web.UI classes like Page, Control, MasterPage... - (f.FieldType != null && - f.ParentType.BaseClasses.Any(bc => bc.ParentNamespace.Name.StartsWith("System.Web.UI")) && - (f.FieldType.ParentNamespace.Name.StartsWith("System.Web.UI") || - f.FieldType.BaseClasses.Any(bc => bc.ParentNamespace.Name.StartsWith("System.Web.UI")) - )) || - - // Match fields named 'mappingSource' for DataContext classes - (f.Name == "mappingSource" && f.ParentType.DeriveFrom("System.Data.Linq.DataContext".AllowNoMatch())) - -select f - -// -// This code query is prefixed with **notmycode**. -// This means that all application fields matched by this -// code query are removed from the *code base view* **JustMyCode.Fields**. -// The code base view *JustMyCode* is used by most default code queries -// and rules. -// -// This query matches application fields tagged -// with *System.CodeDom.Compiler.GeneratedCodeAttribute*, and -// *Windows Form*, *WPF* and *UWP* fields generated by the designer. -// Make sure to make this query richer to discard your generated -// fields from the NDepend rules results. -// -// *notmycode* queries are executed before running others -// queries and rules. Also modifying a *notmycode* query -// provokes re-run of queries and rules that rely -// on the *JustMyCode* code base view. -// -// Several *notmycode* queries can be written to match *fields*, -// in which case this results in cumulative effect. -// -// Online documentation: -// https://www.ndepend.com/docs/cqlinq-syntax#NotMyCode -//]]> - JustMyCode code elements -from elem in JustMyCode.CodeElements -select new { - elem, - loc = elem.IsCodeContainer ? elem.AsCodeContainer.NbLinesOfCode : null -} - -// -// This code query enumerates all -// *assemblies*, *namespaces*, *types*, *methods* and *fields* -// in your application, that are considered as being your code. -// -// This means concretely that the *ICodeBaseView* **JustMyCode** -// only shows these code elements. This code base view is used by -// many default code rule to avoid being warned on code elements -// that you don't consider as your code - typically the code -// elements generated by a tool. -// -// These code elements are the ones that are not matched -// by any quere prefixed with **notmycode**. -//]]> - NotMyCode code elements -from elem in Application.CodeElements.Where(element => !JustMyCode.Contains(element)) -select new { - elem, - loc = elem.IsCodeContainer ? elem.AsCodeContainer.NbLinesOfCode : null -} - -// -// This code query enumerates all -// *assemblies*, *namespaces*, *types*, *methods* and *fields* -// in your application, that are considered as not being your code. -// -// This means concretely that the *ICodeBaseView* **JustMyCode** -// hide these code elements. This code base view is used by -// many default code rules to avoid being warned on code elements -// that you don't consider as your code - typically the code -// elements generated by a tool. -// -// These code elements are the ones matched by queries prefixed with -// **notmycode**. -//]]> - - - - -from issue in Issues -where issue.WasAdded() -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity }]]> - -from issue in Issues -where issue.WasFixed() -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity }]]> - -from issue in Issues -where !issue.WasAdded() && - (issue.DebtDiff() > Debt.Zero || issue.AnnualInterestDiff() > AnnualInterest.Zero) -select new { - issue, - issue.Debt, debtDiff = issue.DebtDiff(), - issue.AnnualInterest, annualInterestDiff = issue.AnnualInterestDiff(), - issue.Severity -} - -// -// An issue is considered worsened if its *debt* increased since the baseline. -// -// Debt documentation: https://www.ndepend.com/docs/technical-debt#Debt -// -]]> - -from issue in Issues -where issue.Severity == Severity.Blocker -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity } - -// -// An issue with the severity **Blocker** cannot move to production, it must be fixed. -// -// The severity of an issue is inferred from the issue *annual interest* -// and thresholds defined in the NDepend Project Properties > Issue and Debt. -//]]> - -from issue in Issues -where issue.Severity == Severity.Critical -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity } - -// -// An issue with a severity level **Critical** shouldn't move to production. -// It still can for business imperative needs purposes, but at worth it must be fixed during the next iterations. -// -// The severity of an issue is inferred from the issue *annual interest* -// and thresholds defined in the NDepend Project Properties > Issue and Debt. -//]]> - -from issue in Issues -where issue.Severity == Severity.High -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity } - -// -// An issue with a severity level **High** should be fixed quickly, but can wait until the next scheduled interval. -// -// The severity of an issue is inferred from the issue *annual interest* -// and thresholds defined in the NDepend Project Properties > issue and Debt. -//]]> - -from issue in Issues -where issue.Severity == Severity.Medium -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity } - -// -// An issue with a severity level **Medium** is a warning that if not fixed, won't have a significant impact on development. -// -// The severity of an issue is inferred from the issue *annual interest* -// and thresholds defined in the NDepend Project Properties > issue and Debt. -//]]> - -from issue in Issues -where issue.Severity == Severity.Low -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity } - -// -// The severity level **Low** is used by issues that have a zero, or close to zero, -// value for **Annual Interest**. -// -// Issues with a **Low** or **Medium** severity level represents small improvements, -// ways to make the code looks more elegant. -// -// The **Broken Window Theory** https://en.wikipedia.org/wiki/Broken_windows_theory states that: -// -// *"Consider a building with a few broken windows. -// If the windows are not repaired, the tendency is for vandals to break a few more windows. -// Eventually, they may even break into the building, and if it's unoccupied, perhaps become -// squatters or light fires inside."* -// - -// Issues with a *Low* or *Medium* severity level represents the *broken windows* of a code base. -// If they are not fixed, the tendency is for developers to not care for living -// in an elegant code, which will result in extra-maintenance-cost in the long term. -// -// The severity of an issue is inferred from the issue *annual interest* -// and thresholds defined in the NDepend Project Properties > issue and Debt. -//]]> - -from issue in Issues -where issue.Severity.EqualsAny(Severity.Blocker, Severity.Critical, Severity.High) -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity } - -// -// The number of issues with a severity Blocker, Critical or High. -// -// An issue with the severity **Blocker** cannot move to production, it must be fixed. -// -// An issue with a severity level **Critical** shouldn't move to production. -// It still can for business imperative needs purposes, but at worth it must be fixed during the next iterations. -// -// An issue with a severity level **High** should be fixed quickly, but can wait until the next scheduled interval. -//]]> - -from issue in Issues -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity } - -// -// The number of issues no matter the issue severity. -//]]> - -from issue in context.IssuesSet.AllSuppressedIssues -select new { issue, issue.Debt, issue.AnnualInterest, issue.Severity } - -// -// The number of issues suppressed with the usage of SuppressMessage. -// See the suppressed issues documentation here: -// https://www.ndepend.com/docs/suppress-issues -//]]> - - - -from rule in Rules -select new { - rule, - issues = rule.Issues(), - debt = rule.Debt(), - annualInterest = rule.AnnualInterest(), - maxSeverity = rule.IsViolated() && rule.Issues().Any() ? - (Severity?)rule.Issues().Max(i => i.Severity) : null -} - -// -// This trend metric counts the number of active rules. -// This count includes violated and not violated rules. -// This count includes critical and non critical rules. -// -// When no baseline is available, rules that rely on diff are not counted. -// If you observe that this count slightly decreases with no apparent reason, -// the reason is certainly that rules that rely on diff are not counted -// because the baseline is not defined. -//]]> - -from rule in Rules -where rule.IsViolated() -select new { - rule, - issues = rule.Issues(), - debt = rule.Debt(), - annualInterest = rule.AnnualInterest(), - maxSeverity = rule.IsViolated() && rule.Issues().Any() ? - (Severity?)rule.Issues().Max(i => i.Severity) : null -} - -// -// This trend metric counts the number of active rules that are violated. -// This count includes critical and non critical rules. -// -// When no baseline is available, rules that rely on diff are not counted. -// If you observe that this count slightly decreases with no apparent reason, -// the reason is certainly that rules that rely on diff are not counted -// because the baseline is not defined. -//]]> - -from rule in Rules -where rule.IsViolated() && rule.IsCritical -select new { - rule, - issues = rule.Issues(), - debt = rule.Debt(), - annualInterest = rule.AnnualInterest(), - maxSeverity = rule.IsViolated() && rule.Issues().Any() ? - (Severity?)rule.Issues().Max(i => i.Severity) : null -} - -// -// This trend metric counts the number of critical active rules that are violated. -// -// The concept of critical rule is useful to pinpoint certain rules that should not be violated. -// -// When no baseline is available, rules that rely on diff are not counted. -// If you observe that this count slightly decreases with no apparent reason, -// the reason is certainly that rules that rely on diff are not counted -// because the baseline is not defined. -//]]> - - - -from qualityGate in QualityGates -select new { - qualityGate , - qualityGate.ValueString, - qualityGate.Status, -} - -// -// This trend metric counts the number of active quality gates, -// no matter the gate status (Pass, Warn, Fail). -// -// When no baseline is available, quality gates that rely on diff are not counted. -// If you observe that this count slightly decreases with no apparent reason, -// the reason is certainly that quality gates that rely on diff are not counted -// because the baseline is not defined. -//]]> - -from qualityGate in QualityGates -where qualityGate.Warn -select new { - qualityGate , - qualityGate.ValueString, -} - -// -// This trend metric counts the number of active quality gates that warns. -// -// When no baseline is available, quality gates that rely on diff are not counted. -// If you observe that this count slightly decreases with no apparent reason, -// the reason is certainly that quality gates that rely on diff are not counted -// because the baseline is not defined. -//]]> - -from qualityGate in QualityGates -where qualityGate.Fail -select new { - qualityGate , - qualityGate.ValueString, -} - -// -// This trend metric counts the number of active quality gates that fails. -// -// When no baseline is available, quality gates that rely on diff are not counted. -// If you observe that this count slightly decreases with no apparent reason, -// the reason is certainly that quality gates that rely on diff are not counted -// because the baseline is not defined. -//]]> - - - -let timeToDev = codeBase.EffortToDevelop() -let debt = Issues.Sum(i => i.Debt) -select 100d * debt.ToManDay() / timeToDev.ToManDay() - -// -// This Trend Metric name is suffixed with (Metric) -// to avoid query name collision with the Quality Gate with same name. -// -// Infer a percentage from: -// -// • the estimated total time to develop the code base -// -// • and the the estimated total time to fix all issues (the Debt). -// -// Estimated total time to develop the code base is inferred from -// # lines of code of the code base and from the -// *Estimated number of man-day to develop 1000 logicial lines of code* -// setting found in NDepend Project Properties > Issue and Debt. -// -// Debt documentation: https://www.ndepend.com/docs/technical-debt#Debt -// ]]> - -Issues.Sum(i => i.Debt).ToManDay() - -// -// This Trend Metric name is suffixed with (Metric) -// to avoid query name collision with the Quality Gate with same name. -// -// Debt documentation: https://www.ndepend.com/docs/technical-debt#Debt -//]]> - -let debt = Issues.Sum(i => i.Debt) -let debtInBaseline = IssuesInBaseline.Sum(i => i.Debt) -select (debt - debtInBaseline).ToManDay() - -// -// This Trend Metric name is suffixed with (Metric) -// to avoid query name collision with the Quality Gate with same name. -// -// Debt added (or fixed if negative) since baseline. -// -// Debt documentation: https://www.ndepend.com/docs/technical-debt#Debt -//]]> - -Issues.Sum(i => i.AnnualInterest).ToManDay() - -// -// This Trend Metric name is suffixed with (Metric) -// to avoid query name collision with the Quality Gate with same name. -// -// Annual Interest documentation: https://www.ndepend.com/docs/technical-debt#AnnualInterest -//]]> - -let ai = Issues.Sum(i => i.AnnualInterest) -let aiInBaseline = IssuesInBaseline.Sum(i => i.AnnualInterest) -select (ai - aiInBaseline).ToManDay() - -// -// This Trend Metric name is suffixed with (Metric) -// to avoid query name collision with the Quality Gate with same name. -// -// Annual Interest added (or fixed if negative) since baseline. -// -// Annual Interest documentation: https://www.ndepend.com/docs/technical-debt#AnnualInterest -//]]> - -(Issues.Sum(i =>i.Debt).BreakingPoint(Issues.Sum(i =>i.AnnualInterest))).TotalYears() - -// -// The **breaking point** of a set of issues is the **debt** divided by the **annual interest**. -// -// The *debt* is the estimated cost-to-fix the issues. -// -// The *annual interest* is the estimated cost-to-**not**-fix the issues, per year. -// -// Hence the *breaking point* is the point in time from now, when not fixing the issues cost as much as fixing the issue. -// -// Breaking Point documentation: https://www.ndepend.com/docs/technical-debt#BreakingPoint -// ]]> - -let issues = Issues.Where(i => i.Severity.EqualsAny(Severity.Blocker, Severity.Critical, Severity.High)) -select (issues.Sum(i =>i.Debt).BreakingPoint(issues.Sum(i =>i.AnnualInterest))).TotalYears() - -// -// The **breaking point** of a set of issues is the **debt** divided by the **annual interest**. -// -// The *debt* is the estimated cost-to-fix the issues. -// -// The *annual interest* is the estimated cost-to-**not**-fix the issues, per year. -// -// Hence the *breaking point* is the point in time from now, when not fixing the issues cost as much as fixing the issue. -// -// Breaking Point documentation: https://www.ndepend.com/docs/technical-debt#BreakingPoint -// ]]> - - - -codeBase.NbLinesOfCode]]> - -JustMyCode.Methods.Sum(m => m.NbLinesOfCode) - -// JustMyCode is defined by code queries prefixed with 'notmycode' -// in the group 'Defining JustMyCode'. -]]> - -Application.Methods.Except(JustMyCode.Methods).Sum(m => m.NbLinesOfCode) - -// JustMyCode is defined by code queries prefixed with 'notmycode' -// in the group 'Defining JustMyCode'. -]]> - -from a in Application.Assemblies -let nbLocAdded = !a.IsPresentInBothBuilds() - ? a.NbLinesOfCode - : (a.NbLinesOfCode != null && a.OlderVersion().NbLinesOfCode != null) - ? a.NbLinesOfCode - (int)a.OlderVersion().NbLinesOfCode - : 0 -select (double?)nbLocAdded - - -// A value is computed by this Trend Metric query -// only if a Baseline for Comparison is provided. -// See Project Properties > Analysis > Baseline for Comparison -]]> - -Application.Assemblies.SelectMany( - a => a.SourceDecls.Select(sd => sd.SourceFile.FilePathString.ToLower())) -.Distinct() -.Count() - -// -// This trend metric counts the number of source files. -// -// If a value 0 is obtained, it means that at analysis time, -// assemblies PDB files were not available. -// https://www.ndepend.com/docs/ndepend-analysis-inputs-explanation -// -// So far source files cannot be matched by a code query. -// However editing the query "Application Types" and then -// *Group by source file declarations* will list source files -// with types source declarations. -//]]> - -codeBase.NbILInstructions -]]> - -Application.Methods.Except(JustMyCode.Methods).Sum(m => m.NbILInstructions) - -// JustMyCode is defined by code queries prefixed with 'notmycode' -// in the group 'Defining JustMyCode'. -]]> - -codeBase.NbLinesOfComment - -// -// This trend metric returns the number of lines of comment -// counted in application source files. -// -// So far commenting information is only extracted from C# source code -// and VB.NET support is planned. -//]]> - -codeBase.PercentageComment - -// -// This trend metric returns the percentage of comment -// compared to the number of **logical**lines of code. -// -// So far commenting information is only extracted from C# source code -// and VB.NET support is planned. -//]]> - -from a in Application.Assemblies -select new { - a, - Debt = a.AllDebt(), - Issues = a.AllIssues(), - a.NbLinesOfCode -} - -// -// This trend metric query counts all application assemblies. -// For each assembly it shows the estimated **all** technical-debt and **all** issues. -// **All** means debt and issues of the assembly and of its child namespaces, types and members. -//]]> - -from n in Application.Namespaces -select new { - n, - Debt = n.AllDebt(), - Issues = n.AllIssues(), - n.NbLinesOfCode -} - -// -// This trend metric query counts all application namespaces. -// For each namespace it shows the estimated **all** technical-debt and **all** issues. -// **All** means debt and issues of the namespace and of its child types and members. -//]]> - -from t in Application.Types.Where(t => !t.IsGeneratedByCompiler) -select new { - t, - Debt = t.AllDebt(), - Issues = t.AllIssues(), - t.NbLinesOfCode -} - -// -// This trend metric query counts all application types non-generated by compiler. -// For each type it shows the estimated **all** technical-debt and **all** issues. -// **All** means debt and issues of the type and of its child members. -//]]> - -Application.Types.Where(t => t.IsPubliclyVisible && !t.IsGeneratedByCompiler)]]> - -Application.Types.Where(t => t.IsClass && !t.IsGeneratedByCompiler)]]> - -Application.Types.Where(t => t.IsClass && t.IsAbstract && !t.IsGeneratedByCompiler)]]> - -Application.Types.Where(t => t.IsInterface)]]> - -Application.Types.Where(t => t.IsStructure && !t.IsGeneratedByCompiler)]]> - -from m in Application.Methods.Where(m => !m.IsGeneratedByCompiler) -select new { - m, - Debt = m.Debt(), - Issues = m.Issues(), - m.NbLinesOfCode -} - -// -// This trend metric query counts all application methods non-generated by compiler. -// For each method it shows the estimated technical-debt and the issues. -//]]> - -Application.Methods.Where(m => m.IsAbstract)]]> - -Application.Methods.Where(m => !m.IsAbstract && !m.IsGeneratedByCompiler)]]> - -from f in Application.Fields.Where(f => - !f.IsEnumValue && - !f.ParentType.IsEnumeration && - !f.IsGeneratedByCompiler && - !f.IsLiteral) -select new { - f, - Debt = f.AllDebt(), - Issues = f.AllIssues() -} - -// -// This trend metric query counts all application fields non-generated by compiler -// that are not enumeration values nor constant values. -// For each field it shows the estimated technical-debt and the issues. -//]]> - - - -JustMyCode.Methods - .Max(m => m.NbLinesOfCode) - -// Here is the code query to get the (JustMyCode) method with largest # Lines of Code -// JustMyCode.Methods.OrderByDescending(m => m.NbLinesOfCode).Take(1).Select(m => new {m, m.NbLinesOfCode})]]> - -Application.Methods.Where(m => m.NbLinesOfCode > 0) - .Average(m => m.NbLinesOfCode)]]> - -Application.Methods.Where(m => m.NbLinesOfCode >= 3) - .Average(m => m.NbLinesOfCode)]]> - -JustMyCode.Types - .Max(t => t.NbLinesOfCode) - -// Here is the code query to get the (JustMyCode) type with largest # Lines of Code -// JustMyCode.Types.OrderByDescending(t => t.NbLinesOfCode).Take(1).Select(t => new {t, t.NbLinesOfCode})]]> - -Application.Types.Where(t => t.NbLinesOfCode > 0) - .Average(t => t.NbLinesOfCode)]]> - -Application.Methods - .Max(m => m.CyclomaticComplexity) - -// Here is the code query to get the most complex method, according to Cyclomatic Complexity -// Application.Methods.OrderByDescending(m => m.CyclomaticComplexity).Take(1).Select(m => new {m, m.CyclomaticComplexity})]]> - -Application.Methods.Where(m => m.NbLinesOfCode> 0) - .Average(m => m.CyclomaticComplexity)]]> - -Application.Methods - .Max(m => m.ILCyclomaticComplexity) - -// Here is the code query to get the most complex method, according to Cyclomatic Complexity computed from IL code. -// Application.Methods.OrderByDescending(m => m.ILCyclomaticComplexity).Take(1).Select(m => new {m, m.CyclomaticComplexity})]]> - -Application.Methods.Where(m => m.NbILInstructions> 0) - .Average(m => m.ILCyclomaticComplexity)]]> - -Application.Methods - .Max(m => m.ILNestingDepth) - -// Here is the code query to get the method with higher ILNestingDepth. -// Application.Methods.OrderByDescending(m => m.ILNestingDepth).Take(1).Select(m => new {m, m.ILNestingDepth})]]> - -Application.Methods.Where(m => m.NbILInstructions> 0) - .Average(m => m.ILNestingDepth)]]> - -Application.Types - .Max(t => t.NbMethods) - -// Here is the code query to get the (JustMyCode) type with largest # of Methods -// JustMyCode.Types.OrderByDescending(t => t.NbMethods).Take(1).Select(t => new {t, t.Methods})]]> - -Application.Types.Average(t => t.NbMethods)]]> - -Application.Types.Where(t => t.IsInterface) - .Max(t => t.NbMethods) - -// Here is the code query to get the (JustMyCode) type with largest # of Methods -// JustMyCode.Types.OrderByDescending(t => t.NbMethods).Take(1).Select(t => new {t, t.Methods})]]> - -JustMyCode.Types.Where(t => t.IsInterface) - .Average(t => t.NbMethods)]]> - - - -codeBase.PercentageCoverage]]> - -codeBase.NbLinesOfCodeCovered]]> - -codeBase.NbLinesOfCodeNotCovered]]> - -Application.Methods.Where(m => m.ExcludedFromCoverageStatistics).Sum(m => m.NbLinesOfCode).ToNullableDouble() - -// -// **Lines of Code Uncoverable** are lines of code in methods tagged with an *Uncoverable attribute* -// or methods in types or assemblies tagged with an *Uncoverable attribute*. -// -// These methods can be listed with the code query: -// *from m in Application.Methods where m.ExcludedFromCoverageStatistics select new { m, m.NbLinesOfCode }* -// -// Typically the attribute *System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverageAttribute* -// is used as the *Uncoverable attribute*. -// -// An additional custom *Uncoverable attribute* can be defined in the: -// NDepend Project Properties > Analysis > Code Coverage > Un-Coverable attributes. -// -// If coverage is imported from VS coverage technologies those attributes are -// also considered as *Uncoverable attribute*: -// *System.Diagnostics.DebuggerNonUserCodeAttribute* -// *System.Diagnostics.DebuggerHiddenAttribute*. -// -// If coverage data imported at analysis time is not *in-sync* with the analyzed code base, -// this code query will also list methods not defined in the coverage data imported. -//]]> - -Application.Types.Where(t => t.PercentageCoverage == 100) - .Sum(t => t.NbLinesOfCodeCovered) - -// -// A line of code covered by tests is *even more valuable* if it is in a type 100% covered by test. -// -// Covering 90% of a class is not enough. -// -// • It means that this 10% uncovered code is hard-to-test, -// -// • which means that this code is not well-designed, -// -// • which means that it is error-prone. -// -// Better test error-prone code, isn't it? -//]]> - -Application.Methods.Where(m => m.PercentageCoverage == 100) - .Sum(m => m.NbLinesOfCodeCovered) - -// -// The same remark than in the Trend Metric **# Lines of Code in Types 100% Covered** -// applies for method 100% covered. -// -// A line of code covered by tests is *even more valuable* if it is in a method 100% covered by test. -//]]> - - -(from m in JustMyCode.Methods - -// Don't match too short methods -where m.NbLinesOfCode > 10 - -let CC = m.CyclomaticComplexity -let uncov = (100 - m.PercentageCoverage) / 100f -let CRAP = (CC * CC * uncov * uncov * uncov) + CC -where CRAP != null && CRAP > 30 select CRAP) -.Max(CRAP => CRAP) - -// -// **Change Risk Analyzer and Predictor** (i.e. CRAP) is a code metric -// that helps in pinpointing overly complex and untested code. -// Is has been first defined here: -// http://www.artima.com/weblogs/viewpost.jsp?thread=215899 -// -// The Formula is: **CRAP(m) = CC(m)^2 * (1 – cov(m)/100)^3 + CC(m)** -// -// • where *CC(m)* is the *cyclomatic complexity* of the method *m* -// -// • and *cov(m)* is the *percentage coverage* by tests of the method *m* -// -// Matched methods cumulates two highly *error prone* code smells: -// -// • A complex method, difficult to develop and maintain. -// -// • Non 100% covered code, difficult to refactor without any regression bug. -// -// The higher the CRAP score, the more painful to maintain and error prone is the method. -// -// An arbitrary threshold of 30 is fixed for this code rule as suggested by inventors. -// -// Notice that no amount of testing will keep methods with a Cyclomatic Complexity -// higher than 30, out of CRAP territory. -// -// Notice that CRAP score is not computed for too short methods -// with less than 10 lines of code. -// -// To list methods with higher C.R.A.P scores, please refer to the default rule: -// *Test and Code Coverage* > *C.R.A.P method code metric* -//]]> - - -(from m in JustMyCode.Methods - -// Don't match too short methods -where m.NbLinesOfCode > 10 - -let CC = m.CyclomaticComplexity -let uncov = (100 - m.PercentageCoverage) / 100f -let CRAP = (CC * CC * uncov * uncov * uncov) + CC -where CRAP != null && CRAP > 30 select CRAP) -.Average(CRAP => CRAP) - -// -// **Change Risk Analyzer and Predictor** (i.e. CRAP) is a code metric -// that helps in pinpointing overly complex and untested code. -// Is has been first defined here: -// http://www.artima.com/weblogs/viewpost.jsp?thread=215899 -// -// The Formula is: **CRAP(m) = CC(m)^2 * (1 – cov(m)/100)^3 + CC(m)** -// -// • where *CC(m)* is the *cyclomatic complexity* of the method *m* -// -// • and *cov(m)* is the *percentage coverage* by tests of the method *m* -// -// Matched methods cumulates two highly *error prone* code smells: -// -// • A complex method, difficult to develop and maintain. -// -// • Non 100% covered code, difficult to refactor without any regression bug. -// -// The higher the CRAP score, the more painful to maintain and error prone is the method. -// -// An arbitrary threshold of 30 is fixed for this code rule as suggested by inventors. -// -// Notice that no amount of testing will keep methods with a Cyclomatic Complexity -// higher than 30, out of CRAP territory. -// -// Notice that CRAP score is not computed for too short methods -// with less than 10 lines of code. -// -// To list methods with higher C.R.A.P scores, please refer to the default rule: -// *Test and Code Coverage* > *C.R.A.P method code metric* -//]]> - - - -from a in ThirdParty.Assemblies -select new { a, a.AssembliesUsingMe }]]> - -from n in ThirdParty.Namespaces -select new { n, n.NamespacesUsingMe }]]> - -from t in ThirdParty.Types -select new { t, t.TypesUsingMe }]]> - -from m in ThirdParty.Methods -select new { m, m.MethodsCallingMe }]]> - -from f in ThirdParty.Fields -where !f.ParentType.IsEnumeration -select new { f, f.MethodsUsingMe }]]> - -from elem in ThirdParty.CodeElements -where !(elem.IsField && elem.AsField.ParentType.IsEnumeration) -let users = elem.IsMethod ? elem.AsMethod.MethodsCallingMe.Cast() : - elem.IsField ? elem.AsField.MethodsUsingMe.Cast() : - elem.IsType ? elem.AsType.TypesUsingMe.Cast() : - elem.IsNamespace ? elem.AsNamespace.NamespacesUsingMe.Cast() : - elem.AsAssembly.AssembliesUsingMe.Cast() -select new { elem, users } -]]> - - - - New assemblies -from a in Application.Assemblies where a.WasAdded() -select new { a, a.NbLinesOfCode } - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *assemblies* that have been added since the *baseline*. -//]]> - Assemblies removed -from a in codeBase.OlderVersion().Application.Assemblies where a.WasRemoved() -select new { a, a.NbLinesOfCode } - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *assemblies* that have been removed since the *baseline*. -//]]> - Assemblies where code was changed -from a in Application.Assemblies where a.CodeWasChanged() -select new { - a, - a.NbLinesOfCode, - oldNbLinesOfCode = a.OlderVersion().NbLinesOfCode.GetValueOrDefault() , - delta = (int) a.NbLinesOfCode.GetValueOrDefault() - a.OlderVersion().NbLinesOfCode.GetValueOrDefault() -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *assemblies* in which, code has been changed since the *baseline*. -//]]> - New namespaces -from n in Application.Namespaces where - !n.ParentAssembly.WasAdded() && - n.WasAdded() -select new { n, n.NbLinesOfCode } - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *namespaces* that have been added since the *baseline*. -//]]> - Namespaces removed -from n in codeBase.OlderVersion().Application.Namespaces where - !n.ParentAssembly.WasRemoved() && - n.WasRemoved() -select new { n, n.NbLinesOfCode } - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *namespaces* that have been removed since the *baseline*. -//]]> - Namespaces where code was changed -from n in Application.Namespaces where n.CodeWasChanged() -select new { - n, - n.NbLinesOfCode, - oldNbLinesOfCode = n.OlderVersion().NbLinesOfCode.GetValueOrDefault() , - delta = (int) n.NbLinesOfCode.GetValueOrDefault() - n.OlderVersion().NbLinesOfCode.GetValueOrDefault() -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *namespaces* in which, code has been changed since the *baseline*. -//]]> - New types -from t in Application.Types where - !t.ParentNamespace.WasAdded() && - t.WasAdded() && - !t.IsGeneratedByCompiler -select new { t, t.NbLinesOfCode } - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *types* that have been added since the *baseline*. -//]]> - Types removed -from t in codeBase.OlderVersion().Application.Types where - !t.ParentNamespace.WasRemoved() && - t.WasRemoved() && - !t.IsGeneratedByCompiler -select new { t, t.NbLinesOfCode } - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *types* that have been removed since the *baseline*. -//]]> - Types where code was changed - -from t in Application.Types where t.CodeWasChanged() -//select new { t, t.NbLinesOfCode } -select new { - t, - t.NbLinesOfCode, - oldNbLinesOfCode = t.OlderVersion().NbLinesOfCode , - delta = (int?) t.NbLinesOfCode - t.OlderVersion().NbLinesOfCode -} -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *types* in which, code has been changed since the *baseline*. -// -// To visualize changes in code, right-click a matched type and select: -// -// • Compare older and newer versions of source file -// -// • Compare older and newer versions disassembled with Reflector -//]]> - Heuristic to find types moved from one namespace or assembly to another -let typesRemoved = codeBase.OlderVersion().Types.Where(t => t.WasRemoved()) -let typesAdded = Types.Where(t => t.WasAdded()) - -from tMoved in typesAdded.Join( - typesRemoved, - t => t.Name, - t => t.Name, - (tNewer, tOlder) => new { tNewer, - OlderParentNamespace = tOlder.ParentNamespace, - OlderParentAssembly = tOlder.ParentAssembly } ) -select tMoved - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *types* moved from one namespace or assembly to another. -// The heuristic implemented consists in making a **join LINQ query** on -// type name (without namespace prefix), applied to the two sets of types *added* -// and types *removed*. -//]]> - Types directly using one or several types changed -let typesChanged = Application.Types.Where(t => t.CodeWasChanged()).ToHashSetEx() - -from t in JustMyCode.Types.UsingAny(typesChanged) where - !t.CodeWasChanged() && - !t.WasAdded() -let typesChangedUsed = t.TypesUsed.Intersect(typesChanged) -select new { t, typesChangedUsed } - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists types *unchanged* since the *baseline* -// but that use directly some *types* where code has been changed -// since the *baseline*. -// -// For such matched type, the code hasen't been changed, but still the overall -// behavior might have been changed. -// -// The query result includes types changed directly used, -//]]> - Types indirectly using one or several types changed -let typesChanged = Application.Types.Where(t => t.CodeWasChanged()).ToHashSetEx() - -// 'depth' represents a code metric defined on types using -// directly or indirectly any type where code was changed. -let depth = JustMyCode.Types.DepthOfIsUsingAny(typesChanged) - -from t in depth.DefinitionDomain where - !t.CodeWasChanged() && - !t.WasAdded() - -let typesChangedDirectlyUsed = t.TypesUsed.Intersect(typesChanged) -let depthOfUsingTypesChanged = depth[t] -orderby depthOfUsingTypesChanged - -select new { - t, - depthOfUsingTypesChanged, - typesChangedDirectlyUsed -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists types *unchanged* since the *baseline* -// but that **use directly or indirectly** some *types* where -// code has been changed since the *baseline*. -// -// For such matched type, the code hasen't been changed, but still the overall -// behavior might have been changed. -// -// The query result includes types changed directly used, and the **depth of usage** -// of types indirectly used, *depth of usage* as defined in the documentation of -// *DepthOfIsUsingAny()* NDepend API method: -// https://www.ndepend.com/api/webframe.html?NDepend.API~NDepend.CodeModel.ExtensionMethodsSequenceUsage~DepthOfIsUsingAny.html -//]]> - New methods -from m in Application.Methods where - !m.ParentType.WasAdded() && - m.WasAdded() && - !m.IsGeneratedByCompiler -select new { m, m.NbLinesOfCode } - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *methods* that have been added since the *baseline*. -//]]> - Methods removed -from m in codeBase.OlderVersion().Application.Methods where - !m.ParentType.WasRemoved() && - m.WasRemoved() && - !m.IsGeneratedByCompiler -select new { m, m.NbLinesOfCode } - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *methods* that have been removed since the *baseline*. -//]]> - Methods where code was changed -from m in Application.Methods where m.CodeWasChanged() -select new { - m, - m.NbLinesOfCode, - oldNbLinesOfCode = m.OlderVersion().NbLinesOfCode , - delta = (int?) m.NbLinesOfCode - m.OlderVersion().NbLinesOfCode -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *methods* in which, code has been changed since the *baseline*. -// -// To visualize changes in code, right-click a matched method and select: -// -// • Compare older and newer versions of source file -// -// • Compare older and newer versions disassembled with Reflector -//]]> - Methods directly calling one or several methods changed -let methodsChanged = Application.Methods.Where(m => m.CodeWasChanged()).ToHashSetEx() - -from m in JustMyCode.Methods.UsingAny(methodsChanged ) where - !m.CodeWasChanged() && - !m.WasAdded() -let methodsChangedCalled = m.MethodsCalled.Intersect(methodsChanged) -select new { - m, - methodsChangedCalled -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists methods *unchanged* since the *baseline* -// but that call directly some *methods* where code has been changed -// since the *baseline*. -// -// For such matched method, the code hasen't been changed, but still the overall -// behavior might have been changed. -// -// The query result includes methods changed directly used, -//]]> - Methods indirectly calling one or several methods changed -let methodsChanged = Application.Methods.Where(m => m.CodeWasChanged()).ToHashSetEx() - -// 'depth' represents a code metric defined on methods using -// directly or indirectly any method where code was changed. -let depth = JustMyCode.Methods.DepthOfIsUsingAny(methodsChanged) - -from m in depth.DefinitionDomain where - !m.CodeWasChanged() && - !m.WasAdded() - -let methodsChangedDirectlyUsed = m.MethodsCalled.Intersect(methodsChanged) -let depthOfUsingMethodsChanged = depth[m] -orderby depthOfUsingMethodsChanged - -select new { - m, - depthOfUsingMethodsChanged, - methodsChangedDirectlyUsed -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists methods *unchanged* since the *baseline* -// but that **use directly or indirectly** some *methods* where -// code has been changed since the *baseline*. -// -// For such matched method, the code hasen't been changed, but still the overall -// behavior might have been changed. -// -// The query result includes methods changed directly used, and the **depth of usage** -// of methods indirectly used, *depth of usage* as defined in the documentation of -// *DepthOfIsUsingAny()* NDepend API method: -// https://www.ndepend.com/api/webframe.html?NDepend.API~NDepend.CodeModel.ExtensionMethodsSequenceUsage~DepthOfIsUsingAny.html -//]]> - New fields -from f in Application.Fields where - !f.ParentType.WasAdded() && - f.WasAdded() && - !f.IsGeneratedByCompiler -select f - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *fields* that have been added since the *baseline*. -//]]> - Fields removed -from f in codeBase.OlderVersion().Application.Fields where - !f.ParentType.WasRemoved() && - f.WasRemoved() && - !f.IsGeneratedByCompiler -select f - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *fields* that have been removed since the *baseline*. -//]]> - Third party types that were not used and that are now used -from t in ThirdParty.Types where t.IsUsedRecently() -select new { - t, - t.Methods, - t.Fields, - t.TypesUsingMe -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *types* defined in **third-party assemblies**, that were not -// used at *baseline* time, and that are now used. -//]]> - Third party types that were used and that are not used anymore -from t in codeBase.OlderVersion().Types where t.IsNotUsedAnymore() -select new { - t, - t.Methods, - t.Fields, - TypesThatUsedMe = t.TypesUsingMe -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *types* defined in **third-party assemblies**, that were -// used at *baseline* time, and that are not used anymore. -//]]> - Third party methods that were not used and that are now used -from m in ThirdParty.Methods where - m.IsUsedRecently() && - !m.ParentType.IsUsedRecently() -select new { - m, - m.MethodsCallingMe -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *methods* defined in **third-party assemblies**, that were not -// used at *baseline* time, and that are now used. -//]]> - Third party methods that were used and that are not used anymore -from m in codeBase.OlderVersion().Methods where - m.IsNotUsedAnymore() && - !m.ParentType.IsNotUsedAnymore() -select new { - m, - MethodsThatCalledMe = m.MethodsCallingMe -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *methods* defined in **third-party assemblies**, that were -// used at *baseline* time, and that are not used anymore. -//]]> - Third party fields that were not used and that are now used -from f in ThirdParty.Fields where - f.IsUsedRecently() && - !f.ParentType.IsUsedRecently() -select new { - f, - f.MethodsUsingMe -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *fields* defined in **third-party assemblies**, that were not -// used at *baseline* time, and that are now used. -//]]> - Third party fields that were used and that are not used anymore -from f in codeBase.OlderVersion().Fields where - f.IsNotUsedAnymore() && - !f.ParentType.IsNotUsedAnymore() -select new { - f, - MethodsThatUsedMe = f.MethodsUsingMe -} - -// -// This query is executed only if a *baseline for comparison* is defined (*diff mode*). -// -// This code query lists *fields* defined in **third-party assemblies**, that were -// used at *baseline* time, and that are not used anymore. -//]]> - - - Most used types (Rank) -(from t in Application.Types - where !t.IsGeneratedByCompiler - orderby t.Rank descending - select new { t, t.Rank, t.TypesUsingMe }).Take(100) - -// -// **TypeRank** values are computed by applying -// the **Google PageRank** algorithm on the -// graph of types' dependencies. Types with -// high *Rank* are the most used ones. Not necessarily -// the ones with the most users types, but the ones -// used by many types, themselves having a lot of -// types users. -// -// See the definition of the TypeRank metric here: -// https://www.ndepend.com/docs/code-metrics#TypeRank -// -// This code query lists the 100 application types -// with the higher rank. -// -// The main consequence of being used a lot for a -// type is that each change (both *syntax change* -// and *behavior change*) will result in potentially -// a lot of **pain** since most types clients will be -// **impacted**. -// -// Hence it is preferable that types with higher -// *TypeRank*, are **interfaces**, that are typically -// less subject changes. -// -// Also interfaces avoid clients relying on -// implementations details. Hence, when the behavior of -// classes implementing an interface changes, this -// shouldn't impact clients of the interface. -// This is *in essence* the -// **Liskov Substitution Principle**. -// http://en.wikipedia.org/wiki/Liskov_substitution_principle -//]]> - Most used methods (Rank) -(from m in Application.Methods - where !m.IsGeneratedByCompiler - orderby m.Rank descending - select new { m, m.Rank, m.MethodsCallingMe }).Take(100) - -// -// **MethodRank** values are computed by applying -// the **Google PageRank** algorithm on the -// graph of methods' dependencies. Methods with -// high *Rank* are the most used ones. Not necessarily -// the ones with the most callers methods, but the ones -// called by many methods, themselves having a lot -// of callers. -// -// See the definition of the MethodRank metric here: -// https://www.ndepend.com/docs/code-metrics#MethodRank -// -// This code query lists the 100 application methods -// with the higher rank. -// -// The main consequence of being used a lot for a -// method is that each change (both *signature change* -// and *behavior change*) will result in potentially -// a lot of **pain** since most methods callers will be -// **impacted**. -// -// Hence it is preferable that methods with highest -// *MethodRank*, are **abstract methods**, that are -// typically less subject to signature changes. -// -// Also abstract methods avoid callers relying on -// implementations details. Hence, when the code -// of a method implementing an abstract method changes, -// this shouldn't impact callers of the abstract method. -// This is *in essence* the -// **Liskov Substitution Principle**. -// http://en.wikipedia.org/wiki/Liskov_substitution_principle -//]]> - Most used assemblies (#AssembliesUsingMe) -(from a in Assemblies orderby a.AssembliesUsingMe.Count() descending - select new { a, a.AssembliesUsingMe }).Take(100) - -// -// This code query lists the 100 *application* and *third-party* -// assemblies, with the higher number of assemblies users. -//]]> - Most used namespaces (#NamespacesUsingMe ) -(from n in Namespaces orderby n.NbNamespacesUsingMe descending - select new { n, n.NamespacesUsingMe }).Take(100) - -// -// This code query lists the 100 *application* and *third-party* -// namespaces, with the higher number of namespaces users. -//]]> - Most used types (#TypesUsingMe ) -(from t in Types orderby t.NbTypesUsingMe descending - where !t.IsGeneratedByCompiler - select new { t, t.TypesUsingMe }).Take(100) - -// -// This code query lists the 100 *application* and *third-party* -// types, with the higher number of types users. -//]]> - Most used methods (#MethodsCallingMe ) -(from m in Methods orderby m.NbMethodsCallingMe - where !m.IsGeneratedByCompiler - select new { m, m.MethodsCallingMe }).Take(100) - -// -// This code query lists the 100 *application* and *third-party* -// methods, with the higher number of methods callers. -//]]> - Namespaces that use many other namespaces (#NamespacesUsed ) -(from n in Application.Namespaces orderby n.NbNamespacesUsed descending - select new { n, n.NamespacesUsed }).Take(100) - -// -// This code query lists the 100 *application* namespaces -// with the higher number of namespaces used. -//]]> - Types that use many other types (#TypesUsed ) -(from t in Application.Types orderby t.NbTypesUsed descending - select new { t, t.TypesUsed, isMyCode = JustMyCode.Contains(t) }).Take(100) - -// -// This code query lists the 100 *application* types -// with the higher number of types used. -//]]> - Methods that use many other methods (#MethodsCalled ) -(from m in Application.Methods orderby m.NbMethodsCalled descending - select new { m, m.MethodsCalled, isMyCode = JustMyCode.Contains(m) }).Take(100) - -// -// This code query lists the 100 *application* methods -// with the higher number of methods called. -//]]> - High-level to low-level assemblies (Level) -from a in Application.Assemblies orderby a.Level descending -select new { a, a.Level } - -// -// This code query lists assemblies ordered by **Level** values. -// See the definition of the *AssemblyLevel* metric here: -// https://www.ndepend.com/docs/code-metrics#Level -//]]> - High-level to low-level namespaces (Level) -from n in Application.Namespaces orderby n.Level descending -select new { n, n.Level } - -// -// This code query lists namespaces ordered by **Level** values. -// See the definition of the *NamespaceLevel* metric here: -// https://www.ndepend.com/docs/code-metrics#Level -//]]> - High-level to low-level types (Level) -from t in Application.Types orderby t.Level descending -select new { t, t.Level } - -// -// This code query lists types ordered by **Level** values. -// See the definition of the *TypeLevel* metric here: -// https://www.ndepend.com/docs/code-metrics#Level -//]]> - High-level to low-level methods (Level) -from m in Application.Methods orderby m.Level descending -select new { m, m.Level } - -// -// This code query lists methods ordered by **Level** values. -// See the definition of the *MethodLevel* metric here: -// https://www.ndepend.com/docs/code-metrics#Level -//]]> - - - - Check that the assembly Asm1 is not using the assembly Asm2 -// ND9901:ExplicitId9901 -warnif count > 0 from a in Application.Assemblies where - a.IsUsing ("Asm2".AllowNoMatch().MatchAssembly()) && - (a.Name == @"Asm1") -select new { - a, - Debt = 30.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to be warned if a particular assembly is using -// another particular assembly. -// -// Such rule can be generated for assemblies **A** and **B**: -// -// • by right clicking the cell in the *Dependency Matrix* -// with **B** in row and **A** in column, -// -// • or by right-clicking the concerned arrow in the *Dependency -// Graph* from **A** to **B**, -// -// and in both cases, click the menu -// **Generate a code rule that warns if this dependency exists** -// -// The generated rule will look like this one. -// It is now up to you to adapt this rule to check exactly -// your needs. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that the namespace N1.N2 is not using the namespace N3.N4.N5 -// ND9902:ExplicitId9902 - -warnif count > 0 from n in Application.Namespaces where - n.IsUsing ("N3.N4.N5".AllowNoMatch().MatchNamespace()) && - (n.Name == @"N1.N2") -select new { - n, - Debt = 30.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to be warned if a particular namespace is using -// another particular namespace. -// -// Such rule can be generated for namespaces **A** and **B**: -// -// • by right clicking the cell in the *Dependency Matrix* -// with **B** in row and **A** in column, -// -// • or by right-clicking the concerned arrow in the *Dependency -// Graph* from **A** to **B**, -// -// and in both cases, click the menu -// **Generate a code rule that warns if this dependency exists** -// -// The generated rule will look like this one. -// It is now up to you to adapt this rule to check exactly -// your needs. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that the assembly Asm1 is only using the assemblies Asm2, Asm3 or mscorlib -// ND9903:ExplicitId9903 - -warnif count > 0 from a in Application.Assemblies where - ( !a.IsUsing ("Asm2".AllowNoMatch().MatchAssembly()) || - !a.IsUsing ("Asm3".AllowNoMatch().MatchAssembly()) || - !a.IsUsing ("mscorlib".MatchAssembly()) || - a.AssembliesUsed.Count() != 3) // Must not be used more than 3 assemblies -&& - (a.Name == @"Asm1") -select new { - a, - a.AssembliesUsed, - Debt = 30.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to enforce that a particular assembly -// is only using a particular set of assemblies. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that the namespace N1.N2 is only using the namespaces N3.N4, N5 or System -// ND9904:ExplicitId9904 - -warnif count > 0 from n in Application.Namespaces where - ( !n.IsUsing("N3.N4".AllowNoMatch().MatchNamespace()) || - !n.IsUsing("N5".AllowNoMatch().MatchNamespace()) || - !n.IsUsing("System".MatchNamespace()) || - n.NamespacesUsed.Count() != 3) // Must not be used more than 3 assemblies - // AsmCe = Efferent Coupling for assembly -&& - (n.Name == @"N1.N2") -select new { - n, - n.NamespacesUsed, - Debt = 30.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to enforce that a particular namespace -// **is only using** a particular set of namespaces. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that AsmDrawing is the only assembly that is using System.Drawing -// ND9905:ExplicitId9905 - -warnif count> 0 from a in Application.Assemblies where - a.IsUsing ("System.Drawing".AllowNoMatch().MatchAssembly()) && - !(a.Name == @"AsmDrawing") -select new { - a, - Debt = 30.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to enforce that a particular assembly -// is **only used by** another particular assembly. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that only 3 assemblies are using System.Drawing -// ND9906:ExplicitId9906 - -warnif count != 3 from a in Application.Assemblies where - a.IsUsing ("System.Drawing".AllowNoMatch().MatchAssembly()) -select new { - a, - Debt = 30.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to enforce that a particular assembly -// is **only used by** 3 any others assemblies. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that all methods that call Foo.Fct1() also call Foo.Fct2(Int32) -// ND9907:ExplicitId9907 - -warnif count > 0 from m in Application.Methods where - m.IsUsing ("Foo.Fct1()".AllowNoMatch()) && - !m.IsUsing ("Foo.Fct2(Int32)".AllowNoMatch()) -select new { - m, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to enforce that if a method calls a particular method, -// it must call another particular method. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that all types that derive from Foo, also implement IFoo -// ND9908:ExplicitId9908 - -warnif count > 0 from t in Application.Types where - t.DeriveFrom ("Foo".AllowNoMatch().MatchType()) && - !t.Implement ("IFoo".AllowNoMatch().MatchType()) -select new { - t, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to enforce that all classes that derive from a particular base class, -// also implement a particular interface. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that all types that has the attribute FooAttribute are declared in the namespace N1.N2* -// ND9909:ExplicitId9909 - -warnif count > 0 from t in - Application.Namespaces.WithNameWildcardMatchNotIn("N1.N2*").ChildTypes() - where - t.HasAttribute ("FooAttribute".AllowNoMatch()) -select new { - t, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to enforce that all types that are tagged -// with a particular attribute, are declared in a -// particular namespace. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that all synchronization objects are only used from the namespaces under MyNamespace.Sync -// ND9910:ExplicitId9910 - -warnif count > 0 from n in Application.Namespaces - where - (n.IsUsing ("System.Threading.Monitor".AllowNoMatch()) || - n.IsUsing ("System.Threading.ReaderWriterLock".AllowNoMatch()) || - n.IsUsing ("System.Threading.Mutex".AllowNoMatch()) || - n.IsUsing ("System.Threading.EventWaitHandle".AllowNoMatch()) || - n.IsUsing ("System.Threading.Semaphore".AllowNoMatch()) || - n.IsUsing ("System.Threading.Interlocked".AllowNoMatch())) - && !n.NameLike (@"^MyNamespace.Sync") -select new { - n, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to enforce that all synchronization objects -// are used from a particular namespace. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - - - Check that the namespace N1.N2 is 100% covered by tests -// ND9911:ExplicitId9911 - -warnif count > 0 from n in Application.Namespaces where - (n.Name == @"N1.N2") && - n.PercentageCoverage < 100 -select new { - n, - n.PercentageCoverage, - Debt = n.NbLinesOfCodeNotCovered.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This is a sample rule that shows how to check -// if a particular namespace is 100% covered by tests. -// Both the string **@"N1.N2"** and the threshold **100** can be adapted to your own needs. -// -// To execute this sample rule, coverage data must be imported. -// More info here: https://www.ndepend.com/docs/code-coverage -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that the assembly Asm is 100% covered by tests -// ND9912:ExplicitId9912 - -warnif count > 0 from a in Application.Assemblies where - (a.Name == @"Asm") && - a.PercentageCoverage < 100 -select new { - a, - a.PercentageCoverage, - Debt = a.NbLinesOfCodeNotCovered.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This is a sample rule that shows how to check -// if a particular assembly is 100% covered by tests. -// Both the string **@"Asm"** and the threshold **100** can be adapted to your own needs. -// -// To execute this sample rule, coverage data must be imported. -// More info here: https://www.ndepend.com/docs/code-coverage -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that the class Namespace.Foo is 100% covered by tests -// ND9913:ExplicitId9913 - -warnif count > 0 from t in Application.Types where - (t.FullName == @"Namespace.Foo") && - t.PercentageCoverage < 100 -select new { - t, - t.PercentageCoverage, - Debt = t.NbLinesOfCodeNotCovered.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// This is a sample rule that shows how to check -// if a particular class is 100% covered by tests. -// Both the string **@"Namespace.Foo"** and the threshold **100** -// can be adapted to your own needs. -// -// To execute this sample rule, coverage data must be imported. -// More info here: https://www.ndepend.com/docs/code-coverage -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that the class Namespace.Foo.Method(Int32) is 100% covered by tests -// ND9914:ExplicitId9914 - -warnif count > 0 from t in Application.Types where - (t.FullName == @"Namespace.Foo.Method(Int32)") && - t.PercentageCoverage < 100 -select new { - t, - t.PercentageCoverage, - Debt = t.NbLinesOfCodeNotCovered.ToMinutes().ToDebt(), - Severity = Severity.High -} - - -// -// This is a sample rule that shows how to check -// if a particular method is 100% covered by tests. -// Both the string **@"Namespace.Foo.Method(Int32)"** and the threshold **100** -// can be adapted to your own needs. -// -// To execute this sample rule, coverage data must be imported. -// More info here: https://www.ndepend.com/docs/code-coverage -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - - - Check that all types that derive from Foo, has a name that ends up with Foo -// ND9915:ExplicitId9915 - -warnif count > 0 from t in Application.Types where - t.DeriveFrom ("Foo".AllowNoMatch().MatchType()) && - !t.NameLike (@"Foo$") -select new { - t, - t.NbLinesOfCode, - Debt = 5.ToMinutes().ToDebt(), - Severity = Severity.Medium -} - -// -// This rule is a *sample rule that can be adapted to your need*. -// -// It shows how to enforce that all classes that derive from -// a particular class, are named with a particular suffix. -// - -// -// This is a *sample rule* there is nothing to fix *as is*. -//]]> - Check that all namespaces begins with CompanyName.ProductName -// ND9916:ExplicitId9916 - -warnif count > 0 from n in Application.Namespaces where - !n.NameLike (@"^CompanyName.ProductName") -select new { - n, - n.NbLinesOfCode, - Debt = 10.ToMinutes().ToDebt(), - Severity = Severity.High -} - -// -// A practice widely adopted is that, in a product source code, -// all namespaces start with "CompanyName.ProductName". -// -// This rule must be adapted with your own **"CompanyName.ProductName"**. -// - -// -// Update all namespaces definitions in source code to satisfy this rule. -//]]> - - - - - \ No newline at end of file diff --git a/FlightSystem2.sln b/FlightSystem2.sln deleted file mode 100644 index 3178281..0000000 --- a/FlightSystem2.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29509.3 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FlightSystem.Api", "FlightSystem2\FlightSystem.Api.csproj", "{AA40BB18-8E8F-487E-A92C-E889C3FE76E4}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {AA40BB18-8E8F-487E-A92C-E889C3FE76E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AA40BB18-8E8F-487E-A92C-E889C3FE76E4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AA40BB18-8E8F-487E-A92C-E889C3FE76E4}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AA40BB18-8E8F-487E-A92C-E889C3FE76E4}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {72AC3982-563B-4F89-8859-9A2ABD654EA9} - EndGlobalSection - GlobalSection(NDepend) = preSolution - Project = ".\FlightSystem2.ndproj" - EndGlobalSection -EndGlobal diff --git a/FlightSystem2/.vscode/launch.json b/FlightSystem2/.vscode/launch.json new file mode 100644 index 0000000..49e5438 --- /dev/null +++ b/FlightSystem2/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + // Use IntelliSense to find out which attributes exist for C# debugging + // Use hover for the description of the existing attributes + // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md + "version": "0.2.0", + "configurations": [ + { + "name": ".NET Core Launch (web)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/bin/Debug/netcoreapp3.0/FlightSystem.Api.dll", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + // Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser + "serverReadyAction": { + "action": "openExternally", + "pattern": "^\\s*Now listening on:\\s+(https?://\\S+)" + }, + "env": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "sourceFileMap": { + "/Views": "${workspaceFolder}/Views" + } + }, + { + "name": ".NET Core Attach", + "type": "coreclr", + "request": "attach", + "processId": "${command:pickProcess}" + } + ] +} \ No newline at end of file diff --git a/FlightSystem2/.vscode/tasks.json b/FlightSystem2/.vscode/tasks.json new file mode 100644 index 0000000..511202c --- /dev/null +++ b/FlightSystem2/.vscode/tasks.json @@ -0,0 +1,42 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "build", + "command": "dotnet", + "type": "process", + "args": [ + "build", + "${workspaceFolder}/FlightSystem.Api.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "publish", + "command": "dotnet", + "type": "process", + "args": [ + "publish", + "${workspaceFolder}/FlightSystem.Api.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + }, + { + "label": "watch", + "command": "dotnet", + "type": "process", + "args": [ + "watch", + "run", + "${workspaceFolder}/FlightSystem.Api.csproj", + "/property:GenerateFullPaths=true", + "/consoleloggerparameters:NoSummary" + ], + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/FlightSystem2/Dockerfile b/FlightSystem2/Dockerfile deleted file mode 100644 index bcf3934..0000000 --- a/FlightSystem2/Dockerfile +++ /dev/null @@ -1,19 +0,0 @@ -FROM mcr.microsoft.com/dotnet/core/aspnet:3.0-buster-slim AS base -WORKDIR /app -EXPOSE 343 - -FROM mcr.microsoft.com/dotnet/core/sdk:3.0-buster AS build -WORKDIR /src -COPY ["FlightSystem2/FlightSystem.Api.csproj", "FlightSystem2/"] -RUN dotnet restore "FlightSystem2/FlightSystem.Api.csproj" -COPY . . -WORKDIR "/src/FlightSystem2" -RUN dotnet build "FlightSystem.Api.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "FlightSystem.Api.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "FlightSystem.Api.dll"] \ No newline at end of file diff --git a/FlightSystem2/Dockerfile.json b/FlightSystem2/Dockerfile.json deleted file mode 100644 index a827866..0000000 --- a/FlightSystem2/Dockerfile.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "compilerOptions": { - "noImplicitAny": false, - "noEmitOnError": true, - "removeComments": false, - "sourceMap": true, - "target": "es5" - }, - "exclude": [ - "node_modules", - "wwwroot" - ] -} diff --git a/FlightSystem2/Src/Application/Interfaces/AManagers/ARouteManager.cs b/FlightSystem2/Src/Application/Interfaces/AManagers/ARouteManager.cs deleted file mode 100644 index 13adaeb..0000000 --- a/FlightSystem2/Src/Application/Interfaces/AManagers/ARouteManager.cs +++ /dev/null @@ -1,12 +0,0 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using System.Collections.Generic; - -namespace FlightSystem.Api.Src.Application.Interfaces.AManagers -{ - public abstract class ARouteManager : AManager - { - - public abstract List ManageRoutes(ITripParams tripPar); - - } -} diff --git a/FlightSystem2/Src/Application/Interfaces/AServices/IResponseBody.cs b/FlightSystem2/Src/Application/Interfaces/AServices/IResponseBody.cs deleted file mode 100644 index fa55db2..0000000 --- a/FlightSystem2/Src/Application/Interfaces/AServices/IResponseBody.cs +++ /dev/null @@ -1,10 +0,0 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using System.Collections.Generic; - -namespace FlightSystem.Api.Src.Integration.Common.Interfaces -{ - public interface IResponseBody - { - IEnumerable Entities { get; } - } -} diff --git a/FlightSystem2/Src/Application/Interfaces/Data/IAirportData.cs b/FlightSystem2/Src/Application/Interfaces/Data/IAirportData.cs deleted file mode 100644 index b002c4f..0000000 --- a/FlightSystem2/Src/Application/Interfaces/Data/IAirportData.cs +++ /dev/null @@ -1,10 +0,0 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using System.Collections.Generic; - -namespace FlightSystem.Api.Src.Application.Interfaces.Data -{ - public interface IAirportData - { - public List GetAll(); - } -} diff --git a/FlightSystem2/Src/Application/Interfaces/Data/IBackupData.cs b/FlightSystem2/Src/Application/Interfaces/Data/IBackupData.cs deleted file mode 100644 index a2429f9..0000000 --- a/FlightSystem2/Src/Application/Interfaces/Data/IBackupData.cs +++ /dev/null @@ -1,9 +0,0 @@ -using FlightSystem.Api.Src.Domain.Interfaces; - -namespace FlightSystem.Api.Src.Application.Interfaces.Data -{ - public interface IBackupData - { - void SetBackup(IEntity entity); - } -} diff --git a/FlightSystem2/Src/Application/Interfaces/Data/ICountryData.cs b/FlightSystem2/Src/Application/Interfaces/Data/ICountryData.cs deleted file mode 100644 index b2b6bd0..0000000 --- a/FlightSystem2/Src/Application/Interfaces/Data/ICountryData.cs +++ /dev/null @@ -1,10 +0,0 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using System.Collections.Generic; - -namespace FlightSystem.Api.Src.Application.Interfaces.Data -{ - public interface ICountryData - { - public List GetAll(); - } -} diff --git a/FlightSystem2/Src/Application/Interfaces/Data/IFlagImageData.cs b/FlightSystem2/Src/Application/Interfaces/Data/IFlagImageData.cs deleted file mode 100644 index 7125df5..0000000 --- a/FlightSystem2/Src/Application/Interfaces/Data/IFlagImageData.cs +++ /dev/null @@ -1,10 +0,0 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using System.Collections.Generic; - -namespace FlightSystem.Api.Src.Application.Interfaces.Data -{ - public interface IFlagImageData - { - public void AddFlagsToCountries(IEnumerable countries); - } -} diff --git a/FlightSystem2/Src/Application/Interfaces/Data/ILocationsData.cs b/FlightSystem2/Src/Application/Interfaces/Data/ILocationsData.cs deleted file mode 100644 index 656d3b2..0000000 --- a/FlightSystem2/Src/Application/Interfaces/Data/ILocationsData.cs +++ /dev/null @@ -1,9 +0,0 @@ -using FlightSystem.Api.Src.Domain.Interfaces; - -namespace FlightSystem.Api.Src.Application.Interfaces.Data -{ - public interface ILocationsData - { - ILocations GetLocationsAll(); - } -} diff --git a/FlightSystem2/Src/Domain/Interfaces/IAirport.cs b/FlightSystem2/Src/Domain/Interfaces/IAirport.cs deleted file mode 100644 index 0c0fa73..0000000 --- a/FlightSystem2/Src/Domain/Interfaces/IAirport.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace FlightSystem.Api.Src.Domain.Interfaces -{ - public interface IAirport : IEntity - { - public string fullName { get; set; } - - public string type { get; } - - public string code { get; set; } - - public string cityName { get; set; } - - public string countryName { get; set; } - } -} diff --git a/FlightSystem2/Src/Domain/Interfaces/IEntity.cs b/FlightSystem2/Src/Domain/Interfaces/IEntity.cs deleted file mode 100644 index f80ed28..0000000 --- a/FlightSystem2/Src/Domain/Interfaces/IEntity.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FlightSystem.Api.Src.Domain.Interfaces -{ - public interface IEntity - { - } -} diff --git a/FlightSystem2/Src/Integration/Config/IntegrationConfig.cs b/FlightSystem2/Src/Integration/Config/IntegrationConfig.cs deleted file mode 100644 index 3b4162c..0000000 --- a/FlightSystem2/Src/Integration/Config/IntegrationConfig.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace FlightSystem.Api.Src.Integration.Config -{ - public static class IntegrationConfig - { - - public static string GetDataBaseUri() { return "bolt://localhost:7687"; } - - public static string GetDataBaseUsername() { return "neo4j"; } - public static string GetDataBasePassword() { return "labas"; } - - public static string GetBackupFilePath() { return @"C:\Users\Paulius\Desktop\Flags\Dummies\"; } - - public static string GetBackupFileType() { return ".json"; } - - public static string GetFlagsFilePath() { return @"C:\Users\Paulius\Desktop\Flags\Flags\"; } - - public static string GetFlagsFileType() { return ".png"; } - - - } -} diff --git a/Neo4J/Dockerfile b/Neo4J/Dockerfile new file mode 100644 index 0000000..e75a613 --- /dev/null +++ b/Neo4J/Dockerfile @@ -0,0 +1,3 @@ +#Neo4j with some plugins. +FROM neo4j:3.5.12 +CMD [ "neo4j" ] \ No newline at end of file diff --git a/Rebus.sln b/Rebus.sln new file mode 100644 index 0000000..f0b748c --- /dev/null +++ b/Rebus.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29509.3 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RebusCore", "RebusCore\RebusCore.csproj", "{AA40BB18-8E8F-487E-A92C-E889C3FE76E4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RebusNeo", "RebusNeo\RebusNeo.csproj", "{346EEC11-1E4E-47D8-9FFE-0080CAE18ACE}" + ProjectSection(ProjectDependencies) = postProject + {AA40BB18-8E8F-487E-A92C-E889C3FE76E4} = {AA40BB18-8E8F-487E-A92C-E889C3FE76E4} + EndProjectSection +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RebusAdmin", "RebusAdmin\RebusAdmin.csproj", "{3B6D1248-3FFF-463F-9D28-912269199158}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AA40BB18-8E8F-487E-A92C-E889C3FE76E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA40BB18-8E8F-487E-A92C-E889C3FE76E4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA40BB18-8E8F-487E-A92C-E889C3FE76E4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA40BB18-8E8F-487E-A92C-E889C3FE76E4}.Release|Any CPU.Build.0 = Release|Any CPU + {346EEC11-1E4E-47D8-9FFE-0080CAE18ACE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {346EEC11-1E4E-47D8-9FFE-0080CAE18ACE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {346EEC11-1E4E-47D8-9FFE-0080CAE18ACE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {346EEC11-1E4E-47D8-9FFE-0080CAE18ACE}.Release|Any CPU.Build.0 = Release|Any CPU + {3B6D1248-3FFF-463F-9D28-912269199158}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B6D1248-3FFF-463F-9D28-912269199158}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B6D1248-3FFF-463F-9D28-912269199158}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B6D1248-3FFF-463F-9D28-912269199158}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {72AC3982-563B-4F89-8859-9A2ABD654EA9} + EndGlobalSection +EndGlobal diff --git a/RebusAdmin/AsyncHelper.cs b/RebusAdmin/AsyncHelper.cs new file mode 100644 index 0000000..b2093db --- /dev/null +++ b/RebusAdmin/AsyncHelper.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading; + +namespace RebusAdmin +{ + public static class AsyncHelper + { + private static readonly TaskFactory _taskFactory = new + TaskFactory(CancellationToken.None, + TaskCreationOptions.None, + TaskContinuationOptions.None, + TaskScheduler.Default); + + public static TResult RunSync(Func> func) + => _taskFactory + .StartNew(func) + .Unwrap() + .GetAwaiter() + .GetResult(); + + public static void RunSync(Func func) + => _taskFactory + .StartNew(func) + .Unwrap() + .GetAwaiter() + .GetResult(); + } +} \ No newline at end of file diff --git a/RebusAdmin/Controllers/BalanceController.cs b/RebusAdmin/Controllers/BalanceController.cs new file mode 100644 index 0000000..4d5fc73 --- /dev/null +++ b/RebusAdmin/Controllers/BalanceController.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusAdmin.Logic; +using System.Net.Http; +using System.Net.Http.Headers; + +namespace RebusAdmin.Controllers +{ + [ApiController] + [Route("[controller]")] + public class BalanceController : ControllerBase + { + private readonly ILogger _logger; + + public BalanceController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string GetAdmin(int userid, string amount) + { + return AsyncHelper.RunSync(() => WebApiClient.Call(IntegrationConfig.GetRebusNeoUri() + "balanceadmin?userid=" + userid + "&amount=" + amount)); + } + } +} diff --git a/RebusAdmin/Controllers/BanUserController.cs b/RebusAdmin/Controllers/BanUserController.cs new file mode 100644 index 0000000..1dae432 --- /dev/null +++ b/RebusAdmin/Controllers/BanUserController.cs @@ -0,0 +1,25 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusAdmin.Logic; + +namespace RebusAdmin.Controllers +{ + [ApiController] + [Route("[controller]")] + public class BanUserController : ControllerBase + { + private readonly ILogger _logger; + + public BanUserController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string Get(string userName, string action) + { + return AsyncHelper.RunSync(() => WebApiClient.Call(IntegrationConfig.GetRebusNeoUri() + "banuser?username=" + userName + "&action=" + action)); + } + } +} diff --git a/RebusAdmin/Controllers/DataUpdateController.cs b/RebusAdmin/Controllers/DataUpdateController.cs new file mode 100644 index 0000000..b08a466 --- /dev/null +++ b/RebusAdmin/Controllers/DataUpdateController.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusAdmin.Logic; + +namespace RebusAdmin.Controllers +{ + [ApiController] + [Route("[controller]")] + public class DataUpdate : ControllerBase + { + private readonly ILogger _logger; + private DataUpdateManager _dataUpdateManager = new DataUpdateManager(); + + public DataUpdate(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string Get(int days) + { + _dataUpdateManager.updateAllDataFromFiles(days); + return _dataUpdateManager.GetResult(days); + } + } +} diff --git a/RebusAdmin/Controllers/ReportController.cs b/RebusAdmin/Controllers/ReportController.cs new file mode 100644 index 0000000..9a1ea4a --- /dev/null +++ b/RebusAdmin/Controllers/ReportController.cs @@ -0,0 +1,27 @@ +using System; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusAdmin.Logic; +using System.Net.Http; +using System.Net.Http.Headers; + +namespace RebusAdmin.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ReportController : ControllerBase + { + private readonly ILogger _logger; + + public ReportController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string Get() + { + return AsyncHelper.RunSync(() => WebApiClient.Call(IntegrationConfig.GetRebusNeoUri() + "report")); + } + } +} diff --git a/RebusAdmin/Controllers/StatusCheckController.cs b/RebusAdmin/Controllers/StatusCheckController.cs new file mode 100644 index 0000000..ac754d2 --- /dev/null +++ b/RebusAdmin/Controllers/StatusCheckController.cs @@ -0,0 +1,25 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusAdmin.Logic; + +namespace RebusAdmin.Controllers +{ + [ApiController] + [Route("[controller]")] + public class StatusCheckController : ControllerBase + { + private readonly ILogger _logger; + private StatusManager _statusManager = new StatusManager(); + + public StatusCheckController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string Get() + { + return _statusManager.checkStatus().ToString(); + } + } +} diff --git a/RebusAdmin/Dockerfile b/RebusAdmin/Dockerfile new file mode 100644 index 0000000..ae0ac41 --- /dev/null +++ b/RebusAdmin/Dockerfile @@ -0,0 +1,24 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base +WORKDIR /app +ENV ASPNETCORE_URLS=http://+:5003 +EXPOSE 5003 + +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build +WORKDIR /src +COPY ["RebusAdmin/RebusAdmin.csproj", "RebusAdmin/"] +RUN dotnet restore "RebusAdmin/RebusAdmin.csproj" +COPY . . +WORKDIR "/src/RebusAdmin" +RUN dotnet build "RebusAdmin.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "RebusAdmin.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "RebusAdmin.dll"] + +COPY RebusAdmin/Files /app/files \ No newline at end of file diff --git a/RebusAdmin/Files/Airports.csv b/RebusAdmin/Files/Airports.csv new file mode 100644 index 0000000..62d1ddd --- /dev/null +++ b/RebusAdmin/Files/Airports.csv @@ -0,0 +1,29 @@ +Code;Airport;City;Country Code +LHR;Heathrow Airport;London;GB +CDG;Charles de Gaulle Airport;Paris;FR +AMS;Amsterdam Airport Schiphol;Amsterdam;NL +FRA;Frankfurt Airport;Frankfurt;DE +ISL;Istanbul Ataturk Airport;Istanbul;TR +MAD;Adolfo Suarez Madrid-Barajas Airport;Madrid;ES +BCN;Barcelona El Prat Airport;Barcelona;ES +LGW;London-Gatwick Airport;London;GB +MUC;Munich Airport;Munich;DE +FCO;Leonardo da Vinci-Fiumicino Airport;Rome;IT +SVO;Sheremetyevo International Airport;Moscow;RU +ORY;Paris-Orly Airport;Paris;FR +CPH;Copenhagen Airport;Copenhagen;DK +BRU;Brussels Airport;Brussels;BE +HEL;Helsinki Airport;Helsinki;FI +LTN;Luton Airport;London;GB +ATL;Hartsfield-Jackson Atlanta International Airport;Atlanta;US +ORD;Chicago OHare International Airport;Chicago;US +LAX;Los Angeles International Airport;Los Angeles;US +JFK;John F. Kennedy International Airport;New York City;US +DEN;Denver International Airport;Denver;US +IAD;Washington Dulles International Airport;Washington;US +LGA;LaGuardia Airport;New York City;US +YYZ;Toronto Pearson International Airport;Toronto;CA +PEK;Beijing Capital International Airport;Beijing;CN +SYD;Sydney Airport;Sydney;AU +AKL;Auckland Airport;Auckland;NZ +VNO;Vilnius Airport;Vilnius;LT diff --git a/RebusAdmin/Files/Cities.csv b/RebusAdmin/Files/Cities.csv new file mode 100644 index 0000000..5d46f5a --- /dev/null +++ b/RebusAdmin/Files/Cities.csv @@ -0,0 +1,25 @@ +City;Country Code +London;GB +Paris;FR +Amsterdam;NL +Frankfurt;DE +Istanbul;TR +Madrid;ES +Barcelona;ES +Munich;DE +Rome;IT +Moscow;RU +Copenhagen;DK +Brussels;BE +Helsinki;FI +Atlanta;US +Chicago;US +Los Angeles;US +New York City;US +Denver;US +Washington;US +Toronto;CA +Beijing;CN +Sydney;AU +Auckland;NZ +Vilnius;LT diff --git a/RebusAdmin/Files/Countries.csv b/RebusAdmin/Files/Countries.csv new file mode 100644 index 0000000..f83e538 --- /dev/null +++ b/RebusAdmin/Files/Countries.csv @@ -0,0 +1,18 @@ +Country;Code +United Kingdom;GB +France;FR +Netherlands;NL +Germany;DE +Turkey;TR +Spain;ES +Italy;IT +Russia;RU +Denmark;DK +Belgium;BE +Finland;FI +USA;US +Canada;CA +China;CN +Australia;AU +New Zeland;NZ +Lithuania;LT \ No newline at end of file diff --git a/RebusAdmin/Files/Flights.csv b/RebusAdmin/Files/Flights.csv new file mode 100644 index 0000000..62c3739 --- /dev/null +++ b/RebusAdmin/Files/Flights.csv @@ -0,0 +1,164 @@ +Code;Fom;To;Departs;Arrives;Price;Arrives on next day +BA-100;LHR;CDG;05:30;06:20;40;FALSE +BA-102;LHR;CDG;07:30;08:20;70;FALSE +BA-104;LHR;CDG;08:10;09:00;50;FALSE +BA-106;LHR;CDG;12:30;13:20;50;FALSE +BA-108;LHR;CDG;17:30;18:20;50;FALSE +BA-110;LHR;CDG;20:30;21:20;40;FALSE +BA-112;LHR;CDG;22:30;23:20;40;FALSE +BA-114;LHR;CDG;23:30;00:20;40;TRUE +BA-200;LHR;FRA;06:00;07:30;60;FALSE +BA-210;LHR;FRA;08:00;09:30;40;FALSE +BA-220;LHR;FRA;11:00;12:30;50;FALSE +BA-230;LHR;FRA;16:00;17:30;40;FALSE +BA-240;LHR;FRA;19:00;20:30;40;FALSE +BA-250;LHR;FRA;20:30;22:00;80;FALSE +BA-300;LHR;BCN;08:00;10:30;60;FALSE +BA-310;LHR;BCN;11:00;13:30;70;FALSE +BA-320;LHR;BCN;20:30;23:00;120;FALSE +BA-400;LHR;MUC;05:30;07:30;70;FALSE +BA-410;LHR;MUC;11:00;13:00;50;FALSE +BA-420;LHR;MUC;17:00;19:00;40;FALSE +BA-430;LHR;MUC;22:00;23:55;40;TRUE +BA-500;LHR;ISL;08:30;12:30;110;FALSE +BA-510;LHR;ISL;19:30;23:30;90;FALSE +BA-600;LHR;ORY;07:30;08:30;30;FALSE +BA-610;LHR;ORY;12:30;13:30;30;FALSE +BA-10001;LHR;JFK;06:30;15:20;420;FALSE +BA-10002;LHR;YYZ;07:30;16:20;480;FALSE +BA-700;LGW;HEL;07:30;10:00;70;FALSE +BA-710;LGW;HEL;10:30;13:00;50;FALSE +BA-720;LGW;HEL;17:30;20:00;70;FALSE +BA-730;LGW;HEL;21:30;23:55;100;TRUE +BA-800;LGW;CDG;07:00;08:00;40;FALSE +BA-810;LGW;CDG;09:00;10:00;40;FALSE +BA-820;LGW;CDG;15:00;16:00;50;FALSE +BA-830;LGW;CDG;21:00;22:00;40;FALSE +BA-900;LGW;FRA;23:00;23:55;25;TRUE +BA-1000;LTN;MAD;06:00;08:00;60;FALSE +BA-1010;LTN;MAD;10:00;12:00;70;FALSE +BA-1020;LTN;MAD;15:00;17:00;70;FALSE +BA-1030;LTN;MAD;19:00;21:00;80;FALSE +BA-1040;LTN;MAD;23:00;01:00;50;TRUE +AY-100;HEL;LHR;06:00;08:30;170;FALSE +AY-110;HEL;LHR;09:00;11:30;70;FALSE +AY-120;HEL;LHR;17:00;19:30;80;FALSE +AY-130;HEL;LHR;19:00;21:30;70;FALSE +AY-140;HEL;LHR;21:00;23:30;60;FALSE +AY-200;HEL;CDG;08:00;10:30;80;FALSE +AY-210;HEL;CDG;17:00;19:30;80;FALSE +AY-220;HEL;CDG;20:00;22:30;90;FALSE +AY-300;HEL;AMS;07:00;09:30;80;FALSE +AY-310;HEL;AMS;11:00;13:30;70;FALSE +AY-320;HEL;AMS;18:00;20:30;50;FALSE +AY-400;HEL;SVO;08:00;10:00;100;FALSE +AY-410;HEL;SVO;13:00;15:00;80;FALSE +AY-420;HEL;SVO;21:00;23:00;80;FALSE +AY-500;HEL;BRU;12:00;14:20;60;FALSE +AY-510;HEL;BRU;22:00;00:20;50;TRUE +IB-100;MAD;CDG;07:00;09:00;30;FALSE +IB-110;MAD;CDG;11:00;13:00;70;FALSE +IB-120;MAD;CDG;13:30;15:30;70;FALSE +IB-130;MAD;CDG;16:30;18:30;50;FALSE +IB-140;MAD;CDG;19:30;21:30;100;FALSE +IB-200;MAD;BCN;05:30;06:30;30;FALSE +IB-210;MAD;BCN;07:30;08:30;50;FALSE +IB-220;MAD;BCN;11:30;12:30;40;FALSE +IB-230;MAD;BCN;14:30;15:30;40;FALSE +IB-240;MAD;BCN;17:30;18:30;20;FALSE +IB-250;MAD;BCN;19:30;20:30;40;FALSE +IB-260;MAD;BCN;20:30;21:30;50;FALSE +IB-270;MAD;BCN;21:30;22:30;25;FALSE +IB-280;MAD;BCN;22:30;23:30;45;FALSE +IB-300;MAD;LHR;05:45;08:00;70;FALSE +IB-310;MAD;LHR;08:45;11:00;60;FALSE +IB-320;MAD;LHR;12:45;15:00;50;FALSE +IB-330;MAD;LHR;18:45;21:00;90;FALSE +IB-340;MAD;LHR;20:45;23:00;70;FALSE +IB-350;MAD;LHR;22:45;01:00;60;TRUE +IB-400;MAD;LGW;09:00;11:15;90;FALSE +IB-410;MAD;LGW;15:00;17:15;70;FALSE +IB-420;MAD;LGW;22:00;00:15;60;TRUE +IB-500;MAD;CPH;07:00;09:30;80;FALSE +IB-510;MAD;CPH;11:00;13:30;80;FALSE +IB-520;MAD;CPH;18:00;20:30;60;FALSE +IB-530;MAD;CPH;22:00;00:30;80;TRUE +IB-610;MAD;FRA;07:30;10:00;70;FALSE +IB-620;MAD;FRA;09:30;12:00;80;FALSE +IB-630;MAD;FRA;15:30;18:00;60;FALSE +IB-640;MAD;FRA;20:30;23:00;50;FALSE +IB-70000;MAD;IAD;21:30;06:20;500;TRUE +IB-800;BCN;CDG;06:00;08:00;50;FALSE +IB-810;BCN;CDG;11:00;13:00;70;FALSE +IB-820;BCN;CDG;17:00;19:00;50;FALSE +IB-830;BCN;CDG;20:00;22:00;40;FALSE +AF-100;CDG;LHR;06:00;06:50;50;FALSE +AF-110;CDG;LHR;08:00;08:50;30;FALSE +AF-120;CDG;LHR;12:00;12:50;30;FALSE +AF-130;CDG;LHR;15:00;15:50;50;FALSE +AF-140;CDG;LHR;18:00;18:50;30;FALSE +AF-150;CDG;LHR;22:00;22:50;50;FALSE +AF-200;CDG;AMS;06:30;07:30;50;FALSE +AF-210;CDG;AMS;09:30;10:30;50;FALSE +AF-220;CDG;AMS;11:30;13:30;40;FALSE +AF-230;CDG;AMS;19:30;20:30;40;FALSE +AF-240;CDG;AMS;22:30;23:30;30;FALSE +AF-30000;CDG;ISL;04:30;13:20;160;FALSE +AF-40000;CDG;FCO;05:30;14:20;120;FALSE +AF-50000;CDG;CPH;06:30;15:20;120;FALSE +AF-600;CDG;BRU;07:30;08:10;30;FALSE +AF-610;CDG;BRU;10:30;11:10;30;FALSE +AF-620;CDG;BRU;18:30;19:10;20;FALSE +AF-700;ORY;LTN;08:30;09:20;30;FALSE +AF-710;ORY;LTN;14:30;15:20;30;FALSE +AF-720;ORY;LTN;21:30;22:20;30;FALSE +AF-800;ORY;BRU;09:30;18:20;40;FALSE +LH-100;FRA;MUC;06:00;06:40;30;FALSE +LH-110;FRA;MUC;08:00;08:40;30;FALSE +LH-120;FRA;MUC;11:00;11:40;40;FALSE +LH-130;FRA;MUC;15:00;15:40;20;FALSE +LH-140;FRA;MUC;19:00;19:40;20;FALSE +LH-150;FRA;MUC;23:00;23:40;30;FALSE +LH-200;FRA;LHR;07:00;08:30;50;FALSE +LH-210;FRA;LHR;13:00;14:30;40;FALSE +LH-220;FRA;LHR;15:00;16:30;40;FALSE +LH-230;FRA;LHR;19:00;20:30;50;FALSE +LH-300;FRA;CDG;06:30;07:50;40;FALSE +LH-310;FRA;CDG;09:30;10:50;50;FALSE +LH-320;FRA;CDG;15:30;16:50;40;FALSE +LH-330;FRA;CDG;19:30;20:50;30;FALSE +LH-340;FRA;CDG;23:30;00:50;40;TRUE +LH-400;FRA;MAD;06:30;08:30;70;FALSE +LH-410;FRA;MAD;08:30;10:30;60;FALSE +LH-420;FRA;MAD;14:30;16:30;60;FALSE +LH-430;FRA;MAD;19:30;21:30;60;FALSE +LH-510;FRA;VNO;08:30;10:30;60;FALSE +LH-520;FRA;VNO;18:30;20:30;60;FALSE +SN-100;BRU;LHR;06:00;07:00;30;FALSE +SN-110;BRU;LHR;09:00;10:00;20;FALSE +SN-120;BRU;LHR;15:00;16:00;30;FALSE +SN-130;BRU;LHR;23:00;23:55;20;FALSE +SN-200;BRU;CDG;09:00;09:40;25;FALSE +SN-210;BRU;CDG;15:00;15:40;30;FALSE +SN-220;BRU;CDG;19:00;19:40;20;FALSE +SN-230;BRU;CDG;23:00;23:40;20;FALSE +SN-300;BRU;ORY;06:30;07:10;30;FALSE +SN-310;BRU;ORY;13:30;14:10;20;FALSE +SN-400;BRU;AMS;06:00;06:30;25;FALSE +SN-410;BRU;AMS;08:00;08:30;30;FALSE +SN-420;BRU;AMS;14:00;14:30;20;FALSE +SN-440;BRU;AMS;17:00;17:30;35;FALSE +SN-450;BRU;AMS;19:00;19:30;20;FALSE +SN-460;BRU;AMS;23:00;23:30;25;FALSE +SN-500;BRU;MAD;16:00;18:30;60;FALSE +SN-510;BRU;MAD;22:00;00:30;70;TRUE +SN-600;BRU;BCN;07:00;09:10;70;FALSE +SN-610;BRU;BCN;16:00;18:10;50;FALSE +SN-700;BRU;FRA;07:00;07:50;20;FALSE +SN-710;BRU;FRA;12:00;12:50;40;FALSE +SN-720;BRU;FRA;20:00;20:50;35;FALSE +SN-800;BRU;MUC;17:00;18:10;40;FALSE +SN-900;BRU;CPH;11:00;12:20;40;FALSE +SN-1000;BRU;HEL;06:00;08:20;40;FALSE +SN-1010;BRU;HEL;18:00;20:20;70;FALSE +SN-1100;BRU;LGW;23:00;23:50;20;FALSE diff --git a/RebusAdmin/Logic/DataUpdateManager.cs b/RebusAdmin/Logic/DataUpdateManager.cs new file mode 100644 index 0000000..0dc72c6 --- /dev/null +++ b/RebusAdmin/Logic/DataUpdateManager.cs @@ -0,0 +1,308 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace RebusAdmin.Logic +{ + public class Airports + { + public string Code { get; set; } + public string Name { get; set; } + public string City { get; set; } + public string Country { get; set; } + } + + public class Flights + { + public string Company { get; set; } + public int FlightNum { get; set; } + public string From { get; set; } + public string To { get; set; } + public string Departs { get; set; } + public string Arrives { get; set; } + public bool ArrOnNextDay { get; set; } + public int Price { get; set; } + } + + public class Countries + { + public string Name { get; set; } + + public string Code { get; set; } + } + + public class Cities + { + public string Name { get; set; } + public string Country { get; set; } + } + + public class DataUpdateManager + { + private int _nodes = 0; + private int _relations = 0; + + private readonly DateTime _start = new DateTime(2020, 01, 01); + private DateTime _end = new DateTime(2019, 12, 31); + + public string GetResult(int days) { return "Database deleted and created! Nodes:" + _nodes.ToString() + ", Relations:" + (_relations + 2 * days).ToString(); } + + public void updateAllDataFromFiles(int days) + { + _end = _end.AddDays(days); + + DeleteAll(); + CreateCountries(); + CreateCities(); + CreateAirports(); + CreateAirportDays(); + CreateFlights(); + + Console.WriteLine(GetResult(days)); + } + + public void DeleteAll() + { + Neo4JContext.RunQuery("MATCH (n) DETACH DELETE n"); + } + + public void CreateCountries() + { + List countries = ReadCountries(); + foreach (var i in countries) + { + Neo4JContext.RunQuery("create (:country{name:'" + i.Name + "', code:'" + i.Code + "'})"); + _nodes++; + } + } + + public void CreateCities() + { + List cities = ReadCities(); + foreach (var i in cities) + { + Neo4JContext.RunQuery("create (:city{name:'" + i.Name + "', country:'" + i.Country + "'})"); + _nodes++; + Neo4JContext.RunQuery("match (a:city{name:'" + i.Name + "'}), (b:country{code:'" + i.Country + "'}) merge (a)-[:inCountry]->(b)"); + _relations++; + } + } + + public void CreateAirports() + { + List airports = ReadAirports(); + foreach (var i in airports) + { + Neo4JContext.RunQuery("create (:airport{name:'" + i.Code + "', fullName:'" + i.Name + "', city:'" + i.City + "', country:'" + i.Country + "'})"); + _nodes++; + Neo4JContext.RunQuery("match (a:airport{name:'" + i.Code + "'}), (b:city{name:'" + i.City + "'}) merge (a)-[:inCity]->(b)"); + _relations++; + } + } + + private void CreateAirportDays() + { + List airports = ReadAirports(); + foreach (var i in airports) + { + DateTime begin = _start; + DateTime end = _end; + + for (DateTime date = begin; date <= end; date = date.AddDays(1)) + { + string dateStr = date.ToString("yyyy-M-d", System.Globalization.CultureInfo.InvariantCulture); + + Neo4JContext.RunQuery("create (:airportDay{name:'" + i.Code + ":" + dateStr + "'})"); + _nodes++; + Neo4JContext.RunQuery("match (a:airportDay{name:'" + i.Code + ":" + dateStr + "'}), (b:airport{name:'" + i.Code + "'}) merge (a)<-[:hasDay]-(b)"); + _relations++; + } + } + System.Console.WriteLine("Main setup finished. New Nodes:" + _nodes.ToString() + ", Relations:" + (_relations).ToString()); + } + + public void CreateFlights() + { + List flights = ReadFlights(); + + string dateStr, dateNextStr; + + DateTime begin = _start; + DateTime end = _end; + + int num = 1; + for (DateTime date = begin; date <= end; date = date.AddDays(1)) + { + + int nodes = 0; + int relations = 0; + + foreach (var i in flights) + { + + if (i.ArrOnNextDay == false) + { + dateStr = date.ToString("yyyy-M-d", System.Globalization.CultureInfo.InvariantCulture); + dateNextStr = date.AddDays(1).ToString("yyyy-M-d", System.Globalization.CultureInfo.InvariantCulture); + + Neo4JContext.RunQuery("create (:flight{name:'" + i.Company + "-" + num + i.FlightNum + "', departs: time('" + i.Departs + "'), arrives: time('" + i.Arrives + "'), price: " + i.Price + "})"); + Neo4JContext.RunQuery("match (a:flight{name:'" + i.Company + "-" + num + i.FlightNum + "'}), (b:airportDay{name:'" + i.From + ":" + dateStr + "'}) merge (b)-[:" + i.To + "flight]->(a)"); + Neo4JContext.RunQuery("match (a:flight{name:'" + i.Company + "-" + num + i.FlightNum + "'}), (b:airportDay{name:'" + i.To + ":" + dateStr + "'}) merge (a)-[:" + i.To + "flight]->(b)"); + + Neo4JContext.RunQuery("create (:flight{name:'" + i.Company + "-" + num + (i.FlightNum + 1) + "', departs: time('" + i.Departs + "'), arrives: time('" + i.Arrives + "'), price: " + i.Price + "})"); + Neo4JContext.RunQuery("match (a:flight{name:'" + i.Company + "-" + num + (i.FlightNum + 1) + "'}), (b:airportDay{name:'" + i.From + ":" + dateStr + "'}) merge (b)<-[:" + i.To + "flight]-(a)"); + Neo4JContext.RunQuery("match (a:flight{name:'" + i.Company + "-" + num + (i.FlightNum + 1) + "'}), (b:airportDay{name:'" + i.To + ":" + dateStr + "'}) merge (a)<-[:" + i.To + "flight]-(b)"); + + nodes += 2; + relations += 4; + } + else if (date != end) + { + dateStr = date.ToString("yyyy-M-d", System.Globalization.CultureInfo.InvariantCulture); + dateNextStr = date.AddDays(1).ToString("yyyy-M-d", System.Globalization.CultureInfo.InvariantCulture); + + Neo4JContext.RunQuery("create (:flight{name:'" + i.Company + "-" + num + i.FlightNum + "', departs: time('" + i.Departs + "'), arrives: time('" + i.Arrives + "'), price: " + i.Price + "})"); + Neo4JContext.RunQuery("match (a:flight{name:'" + i.Company + "-" + num + i.FlightNum + "'}), (b:airportDay{name:'" + i.From + ":" + dateStr + "'}) merge (b)-[:" + i.To + "flight]->(a)"); + Neo4JContext.RunQuery("match (a:flight{name:'" + i.Company + "-" + num + i.FlightNum + "'}), (b:airportDay{name:'" + i.To + ":" + dateNextStr + "'}) merge (a)-[:" + i.To + "flight]->(b)"); + + dateNextStr = date.ToString("yyyy-M-d", System.Globalization.CultureInfo.InvariantCulture); + dateStr = date.AddDays(1).ToString("yyyy-M-d", System.Globalization.CultureInfo.InvariantCulture); + + Neo4JContext.RunQuery("create (:flight{name:'" + i.Company + "-" + num + (i.FlightNum + 1) + "', departs: time('" + i.Departs + "'), arrives: time('" + i.Arrives + "'), price: " + i.Price + "})"); + Neo4JContext.RunQuery("match (a:flight{name:'" + i.Company + "-" + num + (i.FlightNum + 1) + "'}), (b:airportDay{name:'" + i.From + ":" + dateStr + "'}) merge (b)<-[:" + i.To + "flight]-(a)"); + Neo4JContext.RunQuery("match (a:flight{name:'" + i.Company + "-" + num + (i.FlightNum + 1) + "'}), (b:airportDay{name:'" + i.To + ":" + dateNextStr + "'}) merge (a)<-[:" + i.To + "flight]-(b)"); + + nodes += 2; + _relations += 4; + } + } + _nodes += nodes; + _relations += relations; + System.Console.WriteLine("Loading days. Day " + num + ". New Nodes:" + _nodes.ToString() + ", Relations:" + (_relations).ToString()); + + num++; + } + } + + + public List ReadCountries() + { + List retList = new List(); + + using (var reader = new StreamReader(IntegrationConfig.GetDataLocation() + "Countries.csv")) + { + int i = 0; + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + var values = line.Split(';'); + + if (i != 0) + { + Countries country = new Countries() + { + Name = values[0], + Code = values[1] + }; + retList.Add(country); + } + i++; + } + } + return retList; + } + + public List ReadCities() + { + List retList = new List(); + + using (var reader = new StreamReader(IntegrationConfig.GetDataLocation() + "Cities.csv")) + { + int i = 0; + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + var values = line.Split(';'); + + if (i != 0) + { + Cities city = new Cities() + { + Name = values[0], + Country = values[1] + }; + retList.Add(city); + } + i++; + } + } + + return retList; + } + + public List ReadAirports() + { + List retList = new List(); + + using (var reader = new StreamReader(IntegrationConfig.GetDataLocation() + "Airports.csv")) + { + int i = 0; + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + var values = line.Split(';'); + + if (i != 0) + { + Airports airport = new Airports() + { + Code = values[0], + Name = values[1], + City = values[2], + Country = values[3] + }; + retList.Add(airport); + } + i++; + } + } + + return retList; + } + + public List ReadFlights() + { + List retList = new List(); + + using (var reader = new StreamReader(IntegrationConfig.GetDataLocation() + "Flights.csv")) + { + var i = 0; + while (!reader.EndOfStream) + { + var line = reader.ReadLine(); + var values = line.Split(';'); + + if (i != 0) + { + var flight = new Flights() + { + Company = values[0].Substring(0, 2), + FlightNum = int.Parse(values[0].Substring(3)), + From = values[1], + To = values[2], + Departs = values[3], + Arrives = values[4], + Price = int.Parse(values[5]), + ArrOnNextDay = bool.Parse(values[6]) + }; + retList.Add(flight); + } + i++; + } + } + + return retList; + } + + } +} diff --git a/RebusAdmin/Logic/IntegrationConfig.cs b/RebusAdmin/Logic/IntegrationConfig.cs new file mode 100644 index 0000000..625dcdb --- /dev/null +++ b/RebusAdmin/Logic/IntegrationConfig.cs @@ -0,0 +1,15 @@ +namespace RebusAdmin.Logic +{ + public static class IntegrationConfig + { + + public static string GetDataBaseUri() { return OperatingSys.IsLinux() ? "bolt://neo4j:7687" : "bolt://localhost:7687"; } + + public static string GetRebusNeoUri() { return "http://rebusneo:5002/"; } + + public static string GetDataBaseUsername() { return "neo4j"; } + public static string GetDataBasePassword() { return "123"; } + + public static string GetDataLocation() { return OperatingSys.IsLinux() ? "/app/files/" : @"C:\Users\pkuprevicius\Documents\dotnet\FlightSystem2\RebusAdmin\Files\"; } + } +} diff --git a/RebusAdmin/Logic/Neo4JContext.cs b/RebusAdmin/Logic/Neo4JContext.cs new file mode 100644 index 0000000..304a79d --- /dev/null +++ b/RebusAdmin/Logic/Neo4JContext.cs @@ -0,0 +1,40 @@ +using System; +using System.Threading.Tasks; +using Neo4j.Driver.V1; + +namespace RebusAdmin.Logic +{ + public static class Neo4JContext + { + public static readonly ISession session = GraphDatabase.Driver(IntegrationConfig.GetDataBaseUri(), AuthTokens.Basic(IntegrationConfig.GetDataBaseUsername(), IntegrationConfig.GetDataBasePassword())).Session(); + + public static IStatementResult RunQuery(string query) + { + try + { + return session.Run(query); + } + catch (Exception ex) + { + throw ex; + } + finally + { + } + } + + public static bool RunTestQuery() + { + try + { + var rez = session.Run(""); + } + catch (ServiceUnavailableException) + { + return false; + } + + return true; + } + } +} diff --git a/RebusAdmin/Logic/OperatingSys.cs b/RebusAdmin/Logic/OperatingSys.cs new file mode 100644 index 0000000..7c1d1ab --- /dev/null +++ b/RebusAdmin/Logic/OperatingSys.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +public static class OperatingSys +{ + public static bool IsWindows() => + RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + public static bool IsLinux() => + RuntimeInformation.IsOSPlatform(OSPlatform.Linux); +} \ No newline at end of file diff --git a/RebusAdmin/Logic/StatusManager.cs b/RebusAdmin/Logic/StatusManager.cs new file mode 100644 index 0000000..1d080ba --- /dev/null +++ b/RebusAdmin/Logic/StatusManager.cs @@ -0,0 +1,13 @@ +using System; + +namespace RebusAdmin.Logic +{ + public class StatusManager + { + public bool checkStatus() + { + return false; + } + + } +} diff --git a/RebusAdmin/Program.cs b/RebusAdmin/Program.cs new file mode 100644 index 0000000..b4666bc --- /dev/null +++ b/RebusAdmin/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace RebusAdmin +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/RebusAdmin/Properties/launchSettings.json b/RebusAdmin/Properties/launchSettings.json new file mode 100644 index 0000000..be2030b --- /dev/null +++ b/RebusAdmin/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:48195", + "sslPort": 44333 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "statuscheck", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "RebusAdmin": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "statuscheck", + "applicationUrl": "https://localhost:6004;http://localhost:6003", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/RebusAdmin/RebusAdmin.csproj b/RebusAdmin/RebusAdmin.csproj new file mode 100644 index 0000000..9cc9513 --- /dev/null +++ b/RebusAdmin/RebusAdmin.csproj @@ -0,0 +1,12 @@ + + + + netcoreapp3.1 + + + + + + + + diff --git a/RebusAdmin/Startup.cs b/RebusAdmin/Startup.cs new file mode 100644 index 0000000..2aa7627 --- /dev/null +++ b/RebusAdmin/Startup.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.HttpsPolicy; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace RebusAdmin +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpsRedirection(); + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + } + } +} diff --git a/RebusAdmin/WebApiClient.cs b/RebusAdmin/WebApiClient.cs new file mode 100644 index 0000000..548bc8c --- /dev/null +++ b/RebusAdmin/WebApiClient.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading.Tasks; + +namespace RebusAdmin +{ + class WebApiClient + { + private static readonly HttpClient client = new HttpClient(); + private static string response = "httpError"; + + public static async Task Call(string uri) + { + // Call asynchronous network methods in a try/catch block to handle exceptions. + try + { + response = await client.GetStringAsync(uri); + } + catch (HttpRequestException) + { + //FIXME + return response; + } + System.Console.WriteLine(response); + return response; + } + } +} \ No newline at end of file diff --git a/RebusAdmin/appsettings.Development.json b/RebusAdmin/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/RebusAdmin/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/FlightSystem2/appsettings.json b/RebusAdmin/appsettings.json similarity index 100% rename from FlightSystem2/appsettings.json rename to RebusAdmin/appsettings.json diff --git a/RebusCore/Dockerfile b/RebusCore/Dockerfile new file mode 100644 index 0000000..033bd71 --- /dev/null +++ b/RebusCore/Dockerfile @@ -0,0 +1,24 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base +WORKDIR /app +ENV ASPNETCORE_URLS=http://+:5001 +EXPOSE 5001 + +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build +WORKDIR /src +COPY ["RebusCore/RebusCore.csproj", "RebusCore/"] +RUN dotnet restore "RebusCore/RebusCore.csproj" +COPY . . +WORKDIR "/src/RebusCore" +RUN dotnet build "RebusCore.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "RebusCore.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "RebusCore.dll"] + +COPY RebusCore/Files /app/files \ No newline at end of file diff --git a/RebusCore/Files/flags/ad.png b/RebusCore/Files/flags/ad.png new file mode 100644 index 0000000..c58bc93 Binary files /dev/null and b/RebusCore/Files/flags/ad.png differ diff --git a/RebusCore/Files/flags/ae.png b/RebusCore/Files/flags/ae.png new file mode 100644 index 0000000..088ef8e Binary files /dev/null and b/RebusCore/Files/flags/ae.png differ diff --git a/RebusCore/Files/flags/af.png b/RebusCore/Files/flags/af.png new file mode 100644 index 0000000..995b351 Binary files /dev/null and b/RebusCore/Files/flags/af.png differ diff --git a/RebusCore/Files/flags/ag.png b/RebusCore/Files/flags/ag.png new file mode 100644 index 0000000..5f8b4f6 Binary files /dev/null and b/RebusCore/Files/flags/ag.png differ diff --git a/RebusCore/Files/flags/al.png b/RebusCore/Files/flags/al.png new file mode 100644 index 0000000..2e516ec Binary files /dev/null and b/RebusCore/Files/flags/al.png differ diff --git a/RebusCore/Files/flags/am.png b/RebusCore/Files/flags/am.png new file mode 100644 index 0000000..d529a99 Binary files /dev/null and b/RebusCore/Files/flags/am.png differ diff --git a/RebusCore/Files/flags/ao.png b/RebusCore/Files/flags/ao.png new file mode 100644 index 0000000..4cdfb0e Binary files /dev/null and b/RebusCore/Files/flags/ao.png differ diff --git a/RebusCore/Files/flags/ar.png b/RebusCore/Files/flags/ar.png new file mode 100644 index 0000000..b31ec92 Binary files /dev/null and b/RebusCore/Files/flags/ar.png differ diff --git a/RebusCore/Files/flags/at.png b/RebusCore/Files/flags/at.png new file mode 100644 index 0000000..993b903 Binary files /dev/null and b/RebusCore/Files/flags/at.png differ diff --git a/RebusCore/Files/flags/au.png b/RebusCore/Files/flags/au.png new file mode 100644 index 0000000..ef20b66 Binary files /dev/null and b/RebusCore/Files/flags/au.png differ diff --git a/RebusCore/Files/flags/az.png b/RebusCore/Files/flags/az.png new file mode 100644 index 0000000..3fb7ccb Binary files /dev/null and b/RebusCore/Files/flags/az.png differ diff --git a/RebusCore/Files/flags/ba.png b/RebusCore/Files/flags/ba.png new file mode 100644 index 0000000..fa4d609 Binary files /dev/null and b/RebusCore/Files/flags/ba.png differ diff --git a/RebusCore/Files/flags/bb.png b/RebusCore/Files/flags/bb.png new file mode 100644 index 0000000..5e87a9e Binary files /dev/null and b/RebusCore/Files/flags/bb.png differ diff --git a/RebusCore/Files/flags/bd.png b/RebusCore/Files/flags/bd.png new file mode 100644 index 0000000..c55d3d7 Binary files /dev/null and b/RebusCore/Files/flags/bd.png differ diff --git a/RebusCore/Files/flags/be.png b/RebusCore/Files/flags/be.png new file mode 100644 index 0000000..449446a Binary files /dev/null and b/RebusCore/Files/flags/be.png differ diff --git a/RebusCore/Files/flags/bf.png b/RebusCore/Files/flags/bf.png new file mode 100644 index 0000000..f3a7288 Binary files /dev/null and b/RebusCore/Files/flags/bf.png differ diff --git a/RebusCore/Files/flags/bg.png b/RebusCore/Files/flags/bg.png new file mode 100644 index 0000000..ec03d4f Binary files /dev/null and b/RebusCore/Files/flags/bg.png differ diff --git a/RebusCore/Files/flags/bh.png b/RebusCore/Files/flags/bh.png new file mode 100644 index 0000000..61bad9b Binary files /dev/null and b/RebusCore/Files/flags/bh.png differ diff --git a/RebusCore/Files/flags/bi.png b/RebusCore/Files/flags/bi.png new file mode 100644 index 0000000..d82b894 Binary files /dev/null and b/RebusCore/Files/flags/bi.png differ diff --git a/RebusCore/Files/flags/bj.png b/RebusCore/Files/flags/bj.png new file mode 100644 index 0000000..220c8e7 Binary files /dev/null and b/RebusCore/Files/flags/bj.png differ diff --git a/RebusCore/Files/flags/bn.png b/RebusCore/Files/flags/bn.png new file mode 100644 index 0000000..944a7be Binary files /dev/null and b/RebusCore/Files/flags/bn.png differ diff --git a/RebusCore/Files/flags/bo.png b/RebusCore/Files/flags/bo.png new file mode 100644 index 0000000..aec2cc6 Binary files /dev/null and b/RebusCore/Files/flags/bo.png differ diff --git a/RebusCore/Files/flags/br.png b/RebusCore/Files/flags/br.png new file mode 100644 index 0000000..b54f27d Binary files /dev/null and b/RebusCore/Files/flags/br.png differ diff --git a/RebusCore/Files/flags/bs.png b/RebusCore/Files/flags/bs.png new file mode 100644 index 0000000..43db8c1 Binary files /dev/null and b/RebusCore/Files/flags/bs.png differ diff --git a/RebusCore/Files/flags/bt.png b/RebusCore/Files/flags/bt.png new file mode 100644 index 0000000..dfd0d6a Binary files /dev/null and b/RebusCore/Files/flags/bt.png differ diff --git a/RebusCore/Files/flags/bw.png b/RebusCore/Files/flags/bw.png new file mode 100644 index 0000000..0a74b72 Binary files /dev/null and b/RebusCore/Files/flags/bw.png differ diff --git a/RebusCore/Files/flags/by.png b/RebusCore/Files/flags/by.png new file mode 100644 index 0000000..1c9f546 Binary files /dev/null and b/RebusCore/Files/flags/by.png differ diff --git a/RebusCore/Files/flags/bz.png b/RebusCore/Files/flags/bz.png new file mode 100644 index 0000000..ef37f6d Binary files /dev/null and b/RebusCore/Files/flags/bz.png differ diff --git a/RebusCore/Files/flags/ca.png b/RebusCore/Files/flags/ca.png new file mode 100644 index 0000000..524f815 Binary files /dev/null and b/RebusCore/Files/flags/ca.png differ diff --git a/RebusCore/Files/flags/cd.png b/RebusCore/Files/flags/cd.png new file mode 100644 index 0000000..a96eccf Binary files /dev/null and b/RebusCore/Files/flags/cd.png differ diff --git a/RebusCore/Files/flags/cf.png b/RebusCore/Files/flags/cf.png new file mode 100644 index 0000000..e203020 Binary files /dev/null and b/RebusCore/Files/flags/cf.png differ diff --git a/RebusCore/Files/flags/cg.png b/RebusCore/Files/flags/cg.png new file mode 100644 index 0000000..7afcfe0 Binary files /dev/null and b/RebusCore/Files/flags/cg.png differ diff --git a/RebusCore/Files/flags/ch.png b/RebusCore/Files/flags/ch.png new file mode 100644 index 0000000..bd447d2 Binary files /dev/null and b/RebusCore/Files/flags/ch.png differ diff --git a/RebusCore/Files/flags/ci.png b/RebusCore/Files/flags/ci.png new file mode 100644 index 0000000..14fceff Binary files /dev/null and b/RebusCore/Files/flags/ci.png differ diff --git a/RebusCore/Files/flags/ck.png b/RebusCore/Files/flags/ck.png new file mode 100644 index 0000000..d74b82d Binary files /dev/null and b/RebusCore/Files/flags/ck.png differ diff --git a/RebusCore/Files/flags/cl.png b/RebusCore/Files/flags/cl.png new file mode 100644 index 0000000..741c34b Binary files /dev/null and b/RebusCore/Files/flags/cl.png differ diff --git a/RebusCore/Files/flags/cm.png b/RebusCore/Files/flags/cm.png new file mode 100644 index 0000000..e2a308b Binary files /dev/null and b/RebusCore/Files/flags/cm.png differ diff --git a/RebusCore/Files/flags/cn.png b/RebusCore/Files/flags/cn.png new file mode 100644 index 0000000..7474dde Binary files /dev/null and b/RebusCore/Files/flags/cn.png differ diff --git a/RebusCore/Files/flags/co.png b/RebusCore/Files/flags/co.png new file mode 100644 index 0000000..477177b Binary files /dev/null and b/RebusCore/Files/flags/co.png differ diff --git a/RebusCore/Files/flags/cr.png b/RebusCore/Files/flags/cr.png new file mode 100644 index 0000000..62cc81a Binary files /dev/null and b/RebusCore/Files/flags/cr.png differ diff --git a/RebusCore/Files/flags/cu.png b/RebusCore/Files/flags/cu.png new file mode 100644 index 0000000..4ada582 Binary files /dev/null and b/RebusCore/Files/flags/cu.png differ diff --git a/RebusCore/Files/flags/cv.png b/RebusCore/Files/flags/cv.png new file mode 100644 index 0000000..e45bfdb Binary files /dev/null and b/RebusCore/Files/flags/cv.png differ diff --git a/RebusCore/Files/flags/cy.png b/RebusCore/Files/flags/cy.png new file mode 100644 index 0000000..e909ebc Binary files /dev/null and b/RebusCore/Files/flags/cy.png differ diff --git a/RebusCore/Files/flags/cz.png b/RebusCore/Files/flags/cz.png new file mode 100644 index 0000000..acfac56 Binary files /dev/null and b/RebusCore/Files/flags/cz.png differ diff --git a/RebusCore/Files/flags/de.png b/RebusCore/Files/flags/de.png new file mode 100644 index 0000000..2dbd9b0 Binary files /dev/null and b/RebusCore/Files/flags/de.png differ diff --git a/RebusCore/Files/flags/dj.png b/RebusCore/Files/flags/dj.png new file mode 100644 index 0000000..147636a Binary files /dev/null and b/RebusCore/Files/flags/dj.png differ diff --git a/RebusCore/Files/flags/dk.png b/RebusCore/Files/flags/dk.png new file mode 100644 index 0000000..65d6b35 Binary files /dev/null and b/RebusCore/Files/flags/dk.png differ diff --git a/RebusCore/Files/flags/dm.png b/RebusCore/Files/flags/dm.png new file mode 100644 index 0000000..04a92e1 Binary files /dev/null and b/RebusCore/Files/flags/dm.png differ diff --git a/RebusCore/Files/flags/do.png b/RebusCore/Files/flags/do.png new file mode 100644 index 0000000..12e017e Binary files /dev/null and b/RebusCore/Files/flags/do.png differ diff --git a/RebusCore/Files/flags/dz.png b/RebusCore/Files/flags/dz.png new file mode 100644 index 0000000..4670a0e Binary files /dev/null and b/RebusCore/Files/flags/dz.png differ diff --git a/RebusCore/Files/flags/ec.png b/RebusCore/Files/flags/ec.png new file mode 100644 index 0000000..9289152 Binary files /dev/null and b/RebusCore/Files/flags/ec.png differ diff --git a/RebusCore/Files/flags/ee.png b/RebusCore/Files/flags/ee.png new file mode 100644 index 0000000..1067640 Binary files /dev/null and b/RebusCore/Files/flags/ee.png differ diff --git a/RebusCore/Files/flags/eg.png b/RebusCore/Files/flags/eg.png new file mode 100644 index 0000000..238d535 Binary files /dev/null and b/RebusCore/Files/flags/eg.png differ diff --git a/RebusCore/Files/flags/eh.png b/RebusCore/Files/flags/eh.png new file mode 100644 index 0000000..b8cf7ef Binary files /dev/null and b/RebusCore/Files/flags/eh.png differ diff --git a/RebusCore/Files/flags/er.png b/RebusCore/Files/flags/er.png new file mode 100644 index 0000000..546b1b9 Binary files /dev/null and b/RebusCore/Files/flags/er.png differ diff --git a/RebusCore/Files/flags/es.png b/RebusCore/Files/flags/es.png new file mode 100644 index 0000000..a8ad334 Binary files /dev/null and b/RebusCore/Files/flags/es.png differ diff --git a/RebusCore/Files/flags/et.png b/RebusCore/Files/flags/et.png new file mode 100644 index 0000000..e807c21 Binary files /dev/null and b/RebusCore/Files/flags/et.png differ diff --git a/RebusCore/Files/flags/fi.png b/RebusCore/Files/flags/fi.png new file mode 100644 index 0000000..1ad1797 Binary files /dev/null and b/RebusCore/Files/flags/fi.png differ diff --git a/RebusCore/Files/flags/fj.png b/RebusCore/Files/flags/fj.png new file mode 100644 index 0000000..6bf7371 Binary files /dev/null and b/RebusCore/Files/flags/fj.png differ diff --git a/RebusCore/Files/flags/fm.png b/RebusCore/Files/flags/fm.png new file mode 100644 index 0000000..32d5b83 Binary files /dev/null and b/RebusCore/Files/flags/fm.png differ diff --git a/RebusCore/Files/flags/fr.png b/RebusCore/Files/flags/fr.png new file mode 100644 index 0000000..a768476 Binary files /dev/null and b/RebusCore/Files/flags/fr.png differ diff --git a/RebusCore/Files/flags/ga.png b/RebusCore/Files/flags/ga.png new file mode 100644 index 0000000..32c68bf Binary files /dev/null and b/RebusCore/Files/flags/ga.png differ diff --git a/RebusCore/Files/flags/gb.png b/RebusCore/Files/flags/gb.png new file mode 100644 index 0000000..3b6668e Binary files /dev/null and b/RebusCore/Files/flags/gb.png differ diff --git a/RebusCore/Files/flags/gd.png b/RebusCore/Files/flags/gd.png new file mode 100644 index 0000000..9a68c48 Binary files /dev/null and b/RebusCore/Files/flags/gd.png differ diff --git a/RebusCore/Files/flags/ge.png b/RebusCore/Files/flags/ge.png new file mode 100644 index 0000000..108e367 Binary files /dev/null and b/RebusCore/Files/flags/ge.png differ diff --git a/RebusCore/Files/flags/gh.png b/RebusCore/Files/flags/gh.png new file mode 100644 index 0000000..c2bf9d0 Binary files /dev/null and b/RebusCore/Files/flags/gh.png differ diff --git a/RebusCore/Files/flags/gm.png b/RebusCore/Files/flags/gm.png new file mode 100644 index 0000000..b929a80 Binary files /dev/null and b/RebusCore/Files/flags/gm.png differ diff --git a/RebusCore/Files/flags/gn.png b/RebusCore/Files/flags/gn.png new file mode 100644 index 0000000..be43ceb Binary files /dev/null and b/RebusCore/Files/flags/gn.png differ diff --git a/RebusCore/Files/flags/gq.png b/RebusCore/Files/flags/gq.png new file mode 100644 index 0000000..db9415a Binary files /dev/null and b/RebusCore/Files/flags/gq.png differ diff --git a/RebusCore/Files/flags/gr.png b/RebusCore/Files/flags/gr.png new file mode 100644 index 0000000..0944411 Binary files /dev/null and b/RebusCore/Files/flags/gr.png differ diff --git a/RebusCore/Files/flags/gt.png b/RebusCore/Files/flags/gt.png new file mode 100644 index 0000000..76c5db7 Binary files /dev/null and b/RebusCore/Files/flags/gt.png differ diff --git a/RebusCore/Files/flags/gw.png b/RebusCore/Files/flags/gw.png new file mode 100644 index 0000000..06dc510 Binary files /dev/null and b/RebusCore/Files/flags/gw.png differ diff --git a/RebusCore/Files/flags/gy.png b/RebusCore/Files/flags/gy.png new file mode 100644 index 0000000..4f9b232 Binary files /dev/null and b/RebusCore/Files/flags/gy.png differ diff --git a/RebusCore/Files/flags/hn.png b/RebusCore/Files/flags/hn.png new file mode 100644 index 0000000..b1a9f5f Binary files /dev/null and b/RebusCore/Files/flags/hn.png differ diff --git a/RebusCore/Files/flags/hr.png b/RebusCore/Files/flags/hr.png new file mode 100644 index 0000000..6820513 Binary files /dev/null and b/RebusCore/Files/flags/hr.png differ diff --git a/RebusCore/Files/flags/ht.png b/RebusCore/Files/flags/ht.png new file mode 100644 index 0000000..ba76abd Binary files /dev/null and b/RebusCore/Files/flags/ht.png differ diff --git a/RebusCore/Files/flags/hu.png b/RebusCore/Files/flags/hu.png new file mode 100644 index 0000000..eeee4ca Binary files /dev/null and b/RebusCore/Files/flags/hu.png differ diff --git a/RebusCore/Files/flags/id.png b/RebusCore/Files/flags/id.png new file mode 100644 index 0000000..e96bc74 Binary files /dev/null and b/RebusCore/Files/flags/id.png differ diff --git a/RebusCore/Files/flags/ie.png b/RebusCore/Files/flags/ie.png new file mode 100644 index 0000000..52e2ad4 Binary files /dev/null and b/RebusCore/Files/flags/ie.png differ diff --git a/RebusCore/Files/flags/il.png b/RebusCore/Files/flags/il.png new file mode 100644 index 0000000..c85c9d3 Binary files /dev/null and b/RebusCore/Files/flags/il.png differ diff --git a/RebusCore/Files/flags/in.png b/RebusCore/Files/flags/in.png new file mode 100644 index 0000000..be5acd3 Binary files /dev/null and b/RebusCore/Files/flags/in.png differ diff --git a/RebusCore/Files/flags/iq.png b/RebusCore/Files/flags/iq.png new file mode 100644 index 0000000..f370c9a Binary files /dev/null and b/RebusCore/Files/flags/iq.png differ diff --git a/RebusCore/Files/flags/ir.png b/RebusCore/Files/flags/ir.png new file mode 100644 index 0000000..7f647fa Binary files /dev/null and b/RebusCore/Files/flags/ir.png differ diff --git a/RebusCore/Files/flags/is.png b/RebusCore/Files/flags/is.png new file mode 100644 index 0000000..74c9e03 Binary files /dev/null and b/RebusCore/Files/flags/is.png differ diff --git a/RebusCore/Files/flags/it.png b/RebusCore/Files/flags/it.png new file mode 100644 index 0000000..d62e50b Binary files /dev/null and b/RebusCore/Files/flags/it.png differ diff --git a/RebusCore/Files/flags/jm.png b/RebusCore/Files/flags/jm.png new file mode 100644 index 0000000..a960d3d Binary files /dev/null and b/RebusCore/Files/flags/jm.png differ diff --git a/RebusCore/Files/flags/jo.png b/RebusCore/Files/flags/jo.png new file mode 100644 index 0000000..e8b743a Binary files /dev/null and b/RebusCore/Files/flags/jo.png differ diff --git a/RebusCore/Files/flags/jp.png b/RebusCore/Files/flags/jp.png new file mode 100644 index 0000000..fd1ce05 Binary files /dev/null and b/RebusCore/Files/flags/jp.png differ diff --git a/RebusCore/Files/flags/ke.png b/RebusCore/Files/flags/ke.png new file mode 100644 index 0000000..dbf559f Binary files /dev/null and b/RebusCore/Files/flags/ke.png differ diff --git a/RebusCore/Files/flags/kg.png b/RebusCore/Files/flags/kg.png new file mode 100644 index 0000000..c0fe3e5 Binary files /dev/null and b/RebusCore/Files/flags/kg.png differ diff --git a/RebusCore/Files/flags/kh.png b/RebusCore/Files/flags/kh.png new file mode 100644 index 0000000..b9340f7 Binary files /dev/null and b/RebusCore/Files/flags/kh.png differ diff --git a/RebusCore/Files/flags/ki.png b/RebusCore/Files/flags/ki.png new file mode 100644 index 0000000..6b718d1 Binary files /dev/null and b/RebusCore/Files/flags/ki.png differ diff --git a/RebusCore/Files/flags/km.png b/RebusCore/Files/flags/km.png new file mode 100644 index 0000000..dbbd8f4 Binary files /dev/null and b/RebusCore/Files/flags/km.png differ diff --git a/RebusCore/Files/flags/kn.png b/RebusCore/Files/flags/kn.png new file mode 100644 index 0000000..028bef5 Binary files /dev/null and b/RebusCore/Files/flags/kn.png differ diff --git a/RebusCore/Files/flags/kp.png b/RebusCore/Files/flags/kp.png new file mode 100644 index 0000000..3fee379 Binary files /dev/null and b/RebusCore/Files/flags/kp.png differ diff --git a/RebusCore/Files/flags/kr.png b/RebusCore/Files/flags/kr.png new file mode 100644 index 0000000..d0022a2 Binary files /dev/null and b/RebusCore/Files/flags/kr.png differ diff --git a/RebusCore/Files/flags/kw.png b/RebusCore/Files/flags/kw.png new file mode 100644 index 0000000..7f701d5 Binary files /dev/null and b/RebusCore/Files/flags/kw.png differ diff --git a/RebusCore/Files/flags/kz.png b/RebusCore/Files/flags/kz.png new file mode 100644 index 0000000..1c23ee1 Binary files /dev/null and b/RebusCore/Files/flags/kz.png differ diff --git a/RebusCore/Files/flags/la.png b/RebusCore/Files/flags/la.png new file mode 100644 index 0000000..9758884 Binary files /dev/null and b/RebusCore/Files/flags/la.png differ diff --git a/RebusCore/Files/flags/lb.png b/RebusCore/Files/flags/lb.png new file mode 100644 index 0000000..80b3256 Binary files /dev/null and b/RebusCore/Files/flags/lb.png differ diff --git a/RebusCore/Files/flags/lc.png b/RebusCore/Files/flags/lc.png new file mode 100644 index 0000000..dc2de27 Binary files /dev/null and b/RebusCore/Files/flags/lc.png differ diff --git a/RebusCore/Files/flags/li.png b/RebusCore/Files/flags/li.png new file mode 100644 index 0000000..c635a63 Binary files /dev/null and b/RebusCore/Files/flags/li.png differ diff --git a/RebusCore/Files/flags/lk.png b/RebusCore/Files/flags/lk.png new file mode 100644 index 0000000..12e6a00 Binary files /dev/null and b/RebusCore/Files/flags/lk.png differ diff --git a/RebusCore/Files/flags/lr.png b/RebusCore/Files/flags/lr.png new file mode 100644 index 0000000..0b998ab Binary files /dev/null and b/RebusCore/Files/flags/lr.png differ diff --git a/RebusCore/Files/flags/ls.png b/RebusCore/Files/flags/ls.png new file mode 100644 index 0000000..ba9ed2d Binary files /dev/null and b/RebusCore/Files/flags/ls.png differ diff --git a/RebusCore/Files/flags/lt.png b/RebusCore/Files/flags/lt.png new file mode 100644 index 0000000..257930e Binary files /dev/null and b/RebusCore/Files/flags/lt.png differ diff --git a/RebusCore/Files/flags/lu.png b/RebusCore/Files/flags/lu.png new file mode 100644 index 0000000..64b5dd6 Binary files /dev/null and b/RebusCore/Files/flags/lu.png differ diff --git a/RebusCore/Files/flags/lv.png b/RebusCore/Files/flags/lv.png new file mode 100644 index 0000000..fb8345e Binary files /dev/null and b/RebusCore/Files/flags/lv.png differ diff --git a/RebusCore/Files/flags/ly.png b/RebusCore/Files/flags/ly.png new file mode 100644 index 0000000..b90fc93 Binary files /dev/null and b/RebusCore/Files/flags/ly.png differ diff --git a/RebusCore/Files/flags/ma.png b/RebusCore/Files/flags/ma.png new file mode 100644 index 0000000..8e62535 Binary files /dev/null and b/RebusCore/Files/flags/ma.png differ diff --git a/RebusCore/Files/flags/mc.png b/RebusCore/Files/flags/mc.png new file mode 100644 index 0000000..194a09e Binary files /dev/null and b/RebusCore/Files/flags/mc.png differ diff --git a/RebusCore/Files/flags/md.png b/RebusCore/Files/flags/md.png new file mode 100644 index 0000000..0071401 Binary files /dev/null and b/RebusCore/Files/flags/md.png differ diff --git a/RebusCore/Files/flags/me.png b/RebusCore/Files/flags/me.png new file mode 100644 index 0000000..0952236 Binary files /dev/null and b/RebusCore/Files/flags/me.png differ diff --git a/RebusCore/Files/flags/mg.png b/RebusCore/Files/flags/mg.png new file mode 100644 index 0000000..4b4b065 Binary files /dev/null and b/RebusCore/Files/flags/mg.png differ diff --git a/RebusCore/Files/flags/mh.png b/RebusCore/Files/flags/mh.png new file mode 100644 index 0000000..3e7a4b5 Binary files /dev/null and b/RebusCore/Files/flags/mh.png differ diff --git a/RebusCore/Files/flags/mk.png b/RebusCore/Files/flags/mk.png new file mode 100644 index 0000000..4cb42a7 Binary files /dev/null and b/RebusCore/Files/flags/mk.png differ diff --git a/RebusCore/Files/flags/ml.png b/RebusCore/Files/flags/ml.png new file mode 100644 index 0000000..182848a Binary files /dev/null and b/RebusCore/Files/flags/ml.png differ diff --git a/RebusCore/Files/flags/mm.png b/RebusCore/Files/flags/mm.png new file mode 100644 index 0000000..f486f85 Binary files /dev/null and b/RebusCore/Files/flags/mm.png differ diff --git a/RebusCore/Files/flags/mn.png b/RebusCore/Files/flags/mn.png new file mode 100644 index 0000000..dd0bb39 Binary files /dev/null and b/RebusCore/Files/flags/mn.png differ diff --git a/RebusCore/Files/flags/mr.png b/RebusCore/Files/flags/mr.png new file mode 100644 index 0000000..caf0365 Binary files /dev/null and b/RebusCore/Files/flags/mr.png differ diff --git a/RebusCore/Files/flags/mt.png b/RebusCore/Files/flags/mt.png new file mode 100644 index 0000000..b4d686e Binary files /dev/null and b/RebusCore/Files/flags/mt.png differ diff --git a/RebusCore/Files/flags/mu.png b/RebusCore/Files/flags/mu.png new file mode 100644 index 0000000..03f524f Binary files /dev/null and b/RebusCore/Files/flags/mu.png differ diff --git a/RebusCore/Files/flags/mv.png b/RebusCore/Files/flags/mv.png new file mode 100644 index 0000000..ab4aae4 Binary files /dev/null and b/RebusCore/Files/flags/mv.png differ diff --git a/RebusCore/Files/flags/mw.png b/RebusCore/Files/flags/mw.png new file mode 100644 index 0000000..bf38ff9 Binary files /dev/null and b/RebusCore/Files/flags/mw.png differ diff --git a/RebusCore/Files/flags/mx.png b/RebusCore/Files/flags/mx.png new file mode 100644 index 0000000..783148b Binary files /dev/null and b/RebusCore/Files/flags/mx.png differ diff --git a/RebusCore/Files/flags/my.png b/RebusCore/Files/flags/my.png new file mode 100644 index 0000000..5a9ae41 Binary files /dev/null and b/RebusCore/Files/flags/my.png differ diff --git a/RebusCore/Files/flags/mz.png b/RebusCore/Files/flags/mz.png new file mode 100644 index 0000000..0e4c983 Binary files /dev/null and b/RebusCore/Files/flags/mz.png differ diff --git a/RebusCore/Files/flags/na.png b/RebusCore/Files/flags/na.png new file mode 100644 index 0000000..620937b Binary files /dev/null and b/RebusCore/Files/flags/na.png differ diff --git a/RebusCore/Files/flags/ne.png b/RebusCore/Files/flags/ne.png new file mode 100644 index 0000000..0762231 Binary files /dev/null and b/RebusCore/Files/flags/ne.png differ diff --git a/RebusCore/Files/flags/ng.png b/RebusCore/Files/flags/ng.png new file mode 100644 index 0000000..ba56e9e Binary files /dev/null and b/RebusCore/Files/flags/ng.png differ diff --git a/RebusCore/Files/flags/ni.png b/RebusCore/Files/flags/ni.png new file mode 100644 index 0000000..9b6dbf5 Binary files /dev/null and b/RebusCore/Files/flags/ni.png differ diff --git a/RebusCore/Files/flags/nl.png b/RebusCore/Files/flags/nl.png new file mode 100644 index 0000000..aeb72b6 Binary files /dev/null and b/RebusCore/Files/flags/nl.png differ diff --git a/RebusCore/Files/flags/no.png b/RebusCore/Files/flags/no.png new file mode 100644 index 0000000..e14f90f Binary files /dev/null and b/RebusCore/Files/flags/no.png differ diff --git a/RebusCore/Files/flags/np.png b/RebusCore/Files/flags/np.png new file mode 100644 index 0000000..fd0cd6e Binary files /dev/null and b/RebusCore/Files/flags/np.png differ diff --git a/RebusCore/Files/flags/nr.png b/RebusCore/Files/flags/nr.png new file mode 100644 index 0000000..7214086 Binary files /dev/null and b/RebusCore/Files/flags/nr.png differ diff --git a/RebusCore/Files/flags/nu.png b/RebusCore/Files/flags/nu.png new file mode 100644 index 0000000..c7d8797 Binary files /dev/null and b/RebusCore/Files/flags/nu.png differ diff --git a/RebusCore/Files/flags/nz.png b/RebusCore/Files/flags/nz.png new file mode 100644 index 0000000..1f25035 Binary files /dev/null and b/RebusCore/Files/flags/nz.png differ diff --git a/RebusCore/Files/flags/om.png b/RebusCore/Files/flags/om.png new file mode 100644 index 0000000..05c99d9 Binary files /dev/null and b/RebusCore/Files/flags/om.png differ diff --git a/RebusCore/Files/flags/pa.png b/RebusCore/Files/flags/pa.png new file mode 100644 index 0000000..96d4c8e Binary files /dev/null and b/RebusCore/Files/flags/pa.png differ diff --git a/RebusCore/Files/flags/pe.png b/RebusCore/Files/flags/pe.png new file mode 100644 index 0000000..e4d623e Binary files /dev/null and b/RebusCore/Files/flags/pe.png differ diff --git a/RebusCore/Files/flags/pg.png b/RebusCore/Files/flags/pg.png new file mode 100644 index 0000000..5011a16 Binary files /dev/null and b/RebusCore/Files/flags/pg.png differ diff --git a/RebusCore/Files/flags/ph.png b/RebusCore/Files/flags/ph.png new file mode 100644 index 0000000..41ddff2 Binary files /dev/null and b/RebusCore/Files/flags/ph.png differ diff --git a/RebusCore/Files/flags/pk.png b/RebusCore/Files/flags/pk.png new file mode 100644 index 0000000..76020fe Binary files /dev/null and b/RebusCore/Files/flags/pk.png differ diff --git a/RebusCore/Files/flags/pl.png b/RebusCore/Files/flags/pl.png new file mode 100644 index 0000000..d4db002 Binary files /dev/null and b/RebusCore/Files/flags/pl.png differ diff --git a/RebusCore/Files/flags/ps.png b/RebusCore/Files/flags/ps.png new file mode 100644 index 0000000..540f058 Binary files /dev/null and b/RebusCore/Files/flags/ps.png differ diff --git a/RebusCore/Files/flags/pt.png b/RebusCore/Files/flags/pt.png new file mode 100644 index 0000000..e0619bc Binary files /dev/null and b/RebusCore/Files/flags/pt.png differ diff --git a/RebusCore/Files/flags/pw.png b/RebusCore/Files/flags/pw.png new file mode 100644 index 0000000..be101ba Binary files /dev/null and b/RebusCore/Files/flags/pw.png differ diff --git a/RebusCore/Files/flags/py.png b/RebusCore/Files/flags/py.png new file mode 100644 index 0000000..b2e2d5c Binary files /dev/null and b/RebusCore/Files/flags/py.png differ diff --git a/RebusCore/Files/flags/qa.png b/RebusCore/Files/flags/qa.png new file mode 100644 index 0000000..0e615fe Binary files /dev/null and b/RebusCore/Files/flags/qa.png differ diff --git a/RebusCore/Files/flags/ro.png b/RebusCore/Files/flags/ro.png new file mode 100644 index 0000000..57f34f3 Binary files /dev/null and b/RebusCore/Files/flags/ro.png differ diff --git a/RebusCore/Files/flags/rs.png b/RebusCore/Files/flags/rs.png new file mode 100644 index 0000000..7273ea7 Binary files /dev/null and b/RebusCore/Files/flags/rs.png differ diff --git a/RebusCore/Files/flags/ru.png b/RebusCore/Files/flags/ru.png new file mode 100644 index 0000000..79d2101 Binary files /dev/null and b/RebusCore/Files/flags/ru.png differ diff --git a/RebusCore/Files/flags/rw.png b/RebusCore/Files/flags/rw.png new file mode 100644 index 0000000..5b859ea Binary files /dev/null and b/RebusCore/Files/flags/rw.png differ diff --git a/RebusCore/Files/flags/sa.png b/RebusCore/Files/flags/sa.png new file mode 100644 index 0000000..0df5b92 Binary files /dev/null and b/RebusCore/Files/flags/sa.png differ diff --git a/RebusCore/Files/flags/sb.png b/RebusCore/Files/flags/sb.png new file mode 100644 index 0000000..832f7ce Binary files /dev/null and b/RebusCore/Files/flags/sb.png differ diff --git a/RebusCore/Files/flags/sc.png b/RebusCore/Files/flags/sc.png new file mode 100644 index 0000000..a497589 Binary files /dev/null and b/RebusCore/Files/flags/sc.png differ diff --git a/RebusCore/Files/flags/sd.png b/RebusCore/Files/flags/sd.png new file mode 100644 index 0000000..386f3e8 Binary files /dev/null and b/RebusCore/Files/flags/sd.png differ diff --git a/RebusCore/Files/flags/se.png b/RebusCore/Files/flags/se.png new file mode 100644 index 0000000..9e12578 Binary files /dev/null and b/RebusCore/Files/flags/se.png differ diff --git a/RebusCore/Files/flags/sg.png b/RebusCore/Files/flags/sg.png new file mode 100644 index 0000000..7e89814 Binary files /dev/null and b/RebusCore/Files/flags/sg.png differ diff --git a/RebusCore/Files/flags/si.png b/RebusCore/Files/flags/si.png new file mode 100644 index 0000000..3692b66 Binary files /dev/null and b/RebusCore/Files/flags/si.png differ diff --git a/RebusCore/Files/flags/sk.png b/RebusCore/Files/flags/sk.png new file mode 100644 index 0000000..8a73d64 Binary files /dev/null and b/RebusCore/Files/flags/sk.png differ diff --git a/RebusCore/Files/flags/sl.png b/RebusCore/Files/flags/sl.png new file mode 100644 index 0000000..b68450d Binary files /dev/null and b/RebusCore/Files/flags/sl.png differ diff --git a/RebusCore/Files/flags/sm.png b/RebusCore/Files/flags/sm.png new file mode 100644 index 0000000..43f1965 Binary files /dev/null and b/RebusCore/Files/flags/sm.png differ diff --git a/RebusCore/Files/flags/sn.png b/RebusCore/Files/flags/sn.png new file mode 100644 index 0000000..fcd0244 Binary files /dev/null and b/RebusCore/Files/flags/sn.png differ diff --git a/RebusCore/Files/flags/so.png b/RebusCore/Files/flags/so.png new file mode 100644 index 0000000..819490b Binary files /dev/null and b/RebusCore/Files/flags/so.png differ diff --git a/RebusCore/Files/flags/sr.png b/RebusCore/Files/flags/sr.png new file mode 100644 index 0000000..9ac4337 Binary files /dev/null and b/RebusCore/Files/flags/sr.png differ diff --git a/RebusCore/Files/flags/ss.png b/RebusCore/Files/flags/ss.png new file mode 100644 index 0000000..dbc04d5 Binary files /dev/null and b/RebusCore/Files/flags/ss.png differ diff --git a/RebusCore/Files/flags/st.png b/RebusCore/Files/flags/st.png new file mode 100644 index 0000000..b4edccb Binary files /dev/null and b/RebusCore/Files/flags/st.png differ diff --git a/RebusCore/Files/flags/sv.png b/RebusCore/Files/flags/sv.png new file mode 100644 index 0000000..7fe790c Binary files /dev/null and b/RebusCore/Files/flags/sv.png differ diff --git a/RebusCore/Files/flags/sy.png b/RebusCore/Files/flags/sy.png new file mode 100644 index 0000000..c78f9d5 Binary files /dev/null and b/RebusCore/Files/flags/sy.png differ diff --git a/RebusCore/Files/flags/sz.png b/RebusCore/Files/flags/sz.png new file mode 100644 index 0000000..6550515 Binary files /dev/null and b/RebusCore/Files/flags/sz.png differ diff --git a/RebusCore/Files/flags/td.png b/RebusCore/Files/flags/td.png new file mode 100644 index 0000000..c0c76da Binary files /dev/null and b/RebusCore/Files/flags/td.png differ diff --git a/RebusCore/Files/flags/tg.png b/RebusCore/Files/flags/tg.png new file mode 100644 index 0000000..31f1a00 Binary files /dev/null and b/RebusCore/Files/flags/tg.png differ diff --git a/RebusCore/Files/flags/th.png b/RebusCore/Files/flags/th.png new file mode 100644 index 0000000..5cf74c9 Binary files /dev/null and b/RebusCore/Files/flags/th.png differ diff --git a/RebusCore/Files/flags/tj.png b/RebusCore/Files/flags/tj.png new file mode 100644 index 0000000..1d83e4c Binary files /dev/null and b/RebusCore/Files/flags/tj.png differ diff --git a/RebusCore/Files/flags/tl.png b/RebusCore/Files/flags/tl.png new file mode 100644 index 0000000..c646f33 Binary files /dev/null and b/RebusCore/Files/flags/tl.png differ diff --git a/RebusCore/Files/flags/tm.png b/RebusCore/Files/flags/tm.png new file mode 100644 index 0000000..5dd8fb9 Binary files /dev/null and b/RebusCore/Files/flags/tm.png differ diff --git a/RebusCore/Files/flags/tn.png b/RebusCore/Files/flags/tn.png new file mode 100644 index 0000000..09ce235 Binary files /dev/null and b/RebusCore/Files/flags/tn.png differ diff --git a/RebusCore/Files/flags/to.png b/RebusCore/Files/flags/to.png new file mode 100644 index 0000000..34b5aa1 Binary files /dev/null and b/RebusCore/Files/flags/to.png differ diff --git a/RebusCore/Files/flags/tr.png b/RebusCore/Files/flags/tr.png new file mode 100644 index 0000000..a67740a Binary files /dev/null and b/RebusCore/Files/flags/tr.png differ diff --git a/RebusCore/Files/flags/tt.png b/RebusCore/Files/flags/tt.png new file mode 100644 index 0000000..61ee0b0 Binary files /dev/null and b/RebusCore/Files/flags/tt.png differ diff --git a/RebusCore/Files/flags/tv.png b/RebusCore/Files/flags/tv.png new file mode 100644 index 0000000..16f8616 Binary files /dev/null and b/RebusCore/Files/flags/tv.png differ diff --git a/RebusCore/Files/flags/tw.png b/RebusCore/Files/flags/tw.png new file mode 100644 index 0000000..88cc0df Binary files /dev/null and b/RebusCore/Files/flags/tw.png differ diff --git a/RebusCore/Files/flags/tz.png b/RebusCore/Files/flags/tz.png new file mode 100644 index 0000000..94ca541 Binary files /dev/null and b/RebusCore/Files/flags/tz.png differ diff --git a/RebusCore/Files/flags/ua.png b/RebusCore/Files/flags/ua.png new file mode 100644 index 0000000..80301f4 Binary files /dev/null and b/RebusCore/Files/flags/ua.png differ diff --git a/RebusCore/Files/flags/ug.png b/RebusCore/Files/flags/ug.png new file mode 100644 index 0000000..fec8a45 Binary files /dev/null and b/RebusCore/Files/flags/ug.png differ diff --git a/RebusCore/Files/flags/us.png b/RebusCore/Files/flags/us.png new file mode 100644 index 0000000..07ddf4e Binary files /dev/null and b/RebusCore/Files/flags/us.png differ diff --git a/RebusCore/Files/flags/uy.png b/RebusCore/Files/flags/uy.png new file mode 100644 index 0000000..d49a4f1 Binary files /dev/null and b/RebusCore/Files/flags/uy.png differ diff --git a/RebusCore/Files/flags/uz.png b/RebusCore/Files/flags/uz.png new file mode 100644 index 0000000..37688d8 Binary files /dev/null and b/RebusCore/Files/flags/uz.png differ diff --git a/RebusCore/Files/flags/va.png b/RebusCore/Files/flags/va.png new file mode 100644 index 0000000..9938009 Binary files /dev/null and b/RebusCore/Files/flags/va.png differ diff --git a/RebusCore/Files/flags/vc.png b/RebusCore/Files/flags/vc.png new file mode 100644 index 0000000..55018b1 Binary files /dev/null and b/RebusCore/Files/flags/vc.png differ diff --git a/RebusCore/Files/flags/ve.png b/RebusCore/Files/flags/ve.png new file mode 100644 index 0000000..4c8b135 Binary files /dev/null and b/RebusCore/Files/flags/ve.png differ diff --git a/RebusCore/Files/flags/vn.png b/RebusCore/Files/flags/vn.png new file mode 100644 index 0000000..e9edf37 Binary files /dev/null and b/RebusCore/Files/flags/vn.png differ diff --git a/RebusCore/Files/flags/vu.png b/RebusCore/Files/flags/vu.png new file mode 100644 index 0000000..b221447 Binary files /dev/null and b/RebusCore/Files/flags/vu.png differ diff --git a/RebusCore/Files/flags/ws.png b/RebusCore/Files/flags/ws.png new file mode 100644 index 0000000..b44c452 Binary files /dev/null and b/RebusCore/Files/flags/ws.png differ diff --git a/RebusCore/Files/flags/xk.png b/RebusCore/Files/flags/xk.png new file mode 100644 index 0000000..e1824f1 Binary files /dev/null and b/RebusCore/Files/flags/xk.png differ diff --git a/RebusCore/Files/flags/ye.png b/RebusCore/Files/flags/ye.png new file mode 100644 index 0000000..61a0a56 Binary files /dev/null and b/RebusCore/Files/flags/ye.png differ diff --git a/RebusCore/Files/flags/za.png b/RebusCore/Files/flags/za.png new file mode 100644 index 0000000..b6ac53e Binary files /dev/null and b/RebusCore/Files/flags/za.png differ diff --git a/RebusCore/Files/flags/zm.png b/RebusCore/Files/flags/zm.png new file mode 100644 index 0000000..a8b5749 Binary files /dev/null and b/RebusCore/Files/flags/zm.png differ diff --git a/RebusCore/Files/flags/zw.png b/RebusCore/Files/flags/zw.png new file mode 100644 index 0000000..004cd2a Binary files /dev/null and b/RebusCore/Files/flags/zw.png differ diff --git a/FlightSystem2/Properties/launchSettings.json b/RebusCore/Properties/launchSettings.json similarity index 94% rename from FlightSystem2/Properties/launchSettings.json rename to RebusCore/Properties/launchSettings.json index e2389b2..d2e3098 100644 --- a/FlightSystem2/Properties/launchSettings.json +++ b/RebusCore/Properties/launchSettings.json @@ -17,10 +17,10 @@ "ASPNETCORE_ENVIRONMENT": "Development" } }, - "FlightSystem2": { + "RebusCore": { "commandName": "Project", "launchBrowser": true, - "launchUrl": "weatherforecast", + "launchUrl": "allflights", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, diff --git a/FlightSystem2/FlightSystem.Api.csproj b/RebusCore/RebusCore.csproj similarity index 100% rename from FlightSystem2/FlightSystem.Api.csproj rename to RebusCore/RebusCore.csproj diff --git a/FlightSystem2/Src/Application/Interfaces/AManagers/AFlightManager.cs b/RebusCore/Src/Application/Interfaces/AManagers/AFlightManager.cs similarity index 57% rename from FlightSystem2/Src/Application/Interfaces/AManagers/AFlightManager.cs rename to RebusCore/Src/Application/Interfaces/AManagers/AFlightManager.cs index c0b72ce..3eadcea 100644 --- a/FlightSystem2/Src/Application/Interfaces/AManagers/AFlightManager.cs +++ b/RebusCore/Src/Application/Interfaces/AManagers/AFlightManager.cs @@ -1,7 +1,7 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Application.Interfaces.AManagers +namespace FlightSystem.Api.Application.Interfaces.AManagers { public abstract class AFlightManager : AManager { @@ -9,5 +9,7 @@ public abstract class AFlightManager : AManager public abstract IFlight GetFlightById(ulong Id); public abstract List GetFlightsByTripParams(ITripParams tripParams); + + public abstract string GetStringFlightById(ulong Id); } } diff --git a/FlightSystem2/Src/Application/Interfaces/AManagers/AJourneyManager.cs b/RebusCore/Src/Application/Interfaces/AManagers/AJourneyManager.cs similarity index 72% rename from FlightSystem2/Src/Application/Interfaces/AManagers/AJourneyManager.cs rename to RebusCore/Src/Application/Interfaces/AManagers/AJourneyManager.cs index 75ad721..d623858 100644 --- a/FlightSystem2/Src/Application/Interfaces/AManagers/AJourneyManager.cs +++ b/RebusCore/Src/Application/Interfaces/AManagers/AJourneyManager.cs @@ -1,7 +1,7 @@ -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Services.Converters; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Integration.Common.Services.Converters; -namespace FlightSystem.Api.Src.Application.Interfaces.AManagers +namespace FlightSystem.Api.Application.Interfaces.AManagers { public abstract class AJourneyManager : AManager { diff --git a/FlightSystem2/Src/Application/Interfaces/AManagers/ALocationManager.cs b/RebusCore/Src/Application/Interfaces/AManagers/ALocationManager.cs similarity index 64% rename from FlightSystem2/Src/Application/Interfaces/AManagers/ALocationManager.cs rename to RebusCore/Src/Application/Interfaces/AManagers/ALocationManager.cs index c5e81dc..6366c6e 100644 --- a/FlightSystem2/Src/Application/Interfaces/AManagers/ALocationManager.cs +++ b/RebusCore/Src/Application/Interfaces/AManagers/ALocationManager.cs @@ -1,4 +1,4 @@ -namespace FlightSystem.Api.Src.Application.Interfaces.AManagers +namespace FlightSystem.Api.Application.Interfaces.AManagers { public abstract class ALocationManager : AManager { diff --git a/FlightSystem2/Src/Application/Interfaces/AManagers/AManager.cs b/RebusCore/Src/Application/Interfaces/AManagers/AManager.cs similarity index 54% rename from FlightSystem2/Src/Application/Interfaces/AManagers/AManager.cs rename to RebusCore/Src/Application/Interfaces/AManagers/AManager.cs index 7d167a9..799e639 100644 --- a/FlightSystem2/Src/Application/Interfaces/AManagers/AManager.cs +++ b/RebusCore/Src/Application/Interfaces/AManagers/AManager.cs @@ -1,14 +1,13 @@ -using FlightSystem.Api.Src.Application.Common.Factories; -using FlightSystem.Api.Src.Application.Interfaces.Factories; -using FlightSystem.Api.Src.Domain.Implementations.Factories; -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Services.Converters; -using FlightSystem.Api.Src.Integration.Common.Services.Logger; -using FlightSystem.Api.Src.Integration.Common.Services.Response; -using FlightSystem.Api.Src.Repository.Common; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Factories; +using FlightSystem.Api.Domain.Implementations.Factories; +using FlightSystem.Api.Domain.Interfaces; +using FlightSystem.Api.Integration.Common.Services.Converters; +using FlightSystem.Api.Integration.Common.Services.Logger; +using FlightSystem.Api.Integration.Common.Services.Response; +using FlightSystem.Api.Repository.Common; -namespace FlightSystem.Api.Src.Application.Interfaces.AManagers +namespace FlightSystem.Api.Application.Interfaces.AManagers { public abstract class AManager { diff --git a/RebusCore/Src/Application/Interfaces/AManagers/ARouteManager.cs b/RebusCore/Src/Application/Interfaces/AManagers/ARouteManager.cs new file mode 100644 index 0000000..a7435fb --- /dev/null +++ b/RebusCore/Src/Application/Interfaces/AManagers/ARouteManager.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using FlightSystem.Api.Domain.Interfaces; + +namespace FlightSystem.Api.Application.Interfaces.AManagers +{ + public abstract class ARouteManager : AManager + { + + public abstract List ManageRoutes(ITripParams tripPar); + + } +} diff --git a/FlightSystem2/Src/Application/Interfaces/AServices/IDateConverter.cs b/RebusCore/Src/Application/Interfaces/AServices/IDateConverter.cs similarity index 79% rename from FlightSystem2/Src/Application/Interfaces/AServices/IDateConverter.cs rename to RebusCore/Src/Application/Interfaces/AServices/IDateConverter.cs index 3c4b24d..4c82f80 100644 --- a/FlightSystem2/Src/Application/Interfaces/AServices/IDateConverter.cs +++ b/RebusCore/Src/Application/Interfaces/AServices/IDateConverter.cs @@ -1,6 +1,6 @@ using System; -namespace FlightSystem.Api.Src.Integration.Common.Interfaces +namespace FlightSystem.Api.Application.Interfaces.AServices { public interface IDateConverter { diff --git a/FlightSystem2/Src/Application/Interfaces/AServices/IImageConverter.cs b/RebusCore/Src/Application/Interfaces/AServices/IImageConverter.cs similarity index 75% rename from FlightSystem2/Src/Application/Interfaces/AServices/IImageConverter.cs rename to RebusCore/Src/Application/Interfaces/AServices/IImageConverter.cs index f9846d0..7fd86e3 100644 --- a/FlightSystem2/Src/Application/Interfaces/AServices/IImageConverter.cs +++ b/RebusCore/Src/Application/Interfaces/AServices/IImageConverter.cs @@ -1,6 +1,6 @@ using System.Drawing; -namespace FlightSystem.Api.Src.Integration.Common.Interfaces +namespace FlightSystem.Api.Application.Interfaces.AServices { public interface IImageConverter { diff --git a/FlightSystem2/Src/Application/Interfaces/AServices/ILogger.cs b/RebusCore/Src/Application/Interfaces/AServices/ILogger.cs similarity index 70% rename from FlightSystem2/Src/Application/Interfaces/AServices/ILogger.cs rename to RebusCore/Src/Application/Interfaces/AServices/ILogger.cs index 9b7e3c0..9597ca5 100644 --- a/FlightSystem2/Src/Application/Interfaces/AServices/ILogger.cs +++ b/RebusCore/Src/Application/Interfaces/AServices/ILogger.cs @@ -1,4 +1,4 @@ -namespace FlightSystem.Api.Src.Integration.Common.Interfaces +namespace FlightSystem.Api.Application.Interfaces.AServices { public interface ILogger { diff --git a/FlightSystem2/Src/Application/Interfaces/AServices/IResponse.cs b/RebusCore/Src/Application/Interfaces/AServices/IResponse.cs similarity index 69% rename from FlightSystem2/Src/Application/Interfaces/AServices/IResponse.cs rename to RebusCore/Src/Application/Interfaces/AServices/IResponse.cs index 63624e2..1a05db7 100644 --- a/FlightSystem2/Src/Application/Interfaces/AServices/IResponse.cs +++ b/RebusCore/Src/Application/Interfaces/AServices/IResponse.cs @@ -1,4 +1,4 @@ -namespace FlightSystem.Api.Src.Integration.Common.Interfaces +namespace FlightSystem.Api.Application.Interfaces.AServices { public interface IResponse { diff --git a/RebusCore/Src/Application/Interfaces/AServices/IResponseBody.cs b/RebusCore/Src/Application/Interfaces/AServices/IResponseBody.cs new file mode 100644 index 0000000..b7970b2 --- /dev/null +++ b/RebusCore/Src/Application/Interfaces/AServices/IResponseBody.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using FlightSystem.Api.Domain.Interfaces; + +namespace FlightSystem.Api.Application.Interfaces.AServices +{ + public interface IResponseBody + { + IEnumerable Entities { get; } + } +} diff --git a/FlightSystem2/Src/Application/Interfaces/AServices/IResponseError.cs b/RebusCore/Src/Application/Interfaces/AServices/IResponseError.cs similarity index 61% rename from FlightSystem2/Src/Application/Interfaces/AServices/IResponseError.cs rename to RebusCore/Src/Application/Interfaces/AServices/IResponseError.cs index 2a6d0d8..edf4892 100644 --- a/FlightSystem2/Src/Application/Interfaces/AServices/IResponseError.cs +++ b/RebusCore/Src/Application/Interfaces/AServices/IResponseError.cs @@ -1,4 +1,4 @@ -namespace FlightSystem.Api.Src.Integration.Common.Interfaces +namespace FlightSystem.Api.Application.Interfaces.AServices { public interface IResponseError { diff --git a/FlightSystem2/Src/Application/Interfaces/AServices/IResponseHeader.cs b/RebusCore/Src/Application/Interfaces/AServices/IResponseHeader.cs similarity index 62% rename from FlightSystem2/Src/Application/Interfaces/AServices/IResponseHeader.cs rename to RebusCore/Src/Application/Interfaces/AServices/IResponseHeader.cs index f9a627c..3276490 100644 --- a/FlightSystem2/Src/Application/Interfaces/AServices/IResponseHeader.cs +++ b/RebusCore/Src/Application/Interfaces/AServices/IResponseHeader.cs @@ -1,4 +1,4 @@ -namespace FlightSystem.Api.Src.Integration.Common.Interfaces +namespace FlightSystem.Api.Application.Interfaces.AServices { public interface IResponseHeader { diff --git a/FlightSystem2/Src/Application/Interfaces/AServices/ISerializer.cs b/RebusCore/Src/Application/Interfaces/AServices/ISerializer.cs similarity index 69% rename from FlightSystem2/Src/Application/Interfaces/AServices/ISerializer.cs rename to RebusCore/Src/Application/Interfaces/AServices/ISerializer.cs index adb3fc9..b8b3a33 100644 --- a/FlightSystem2/Src/Application/Interfaces/AServices/ISerializer.cs +++ b/RebusCore/Src/Application/Interfaces/AServices/ISerializer.cs @@ -1,4 +1,4 @@ -namespace FlightSystem.Api.Src.Integration.Common.Interfaces +namespace FlightSystem.Api.Application.Interfaces.AServices { public interface ISerializer { diff --git a/RebusCore/Src/Application/Interfaces/Data/IAirportData.cs b/RebusCore/Src/Application/Interfaces/Data/IAirportData.cs new file mode 100644 index 0000000..8d3fd0a --- /dev/null +++ b/RebusCore/Src/Application/Interfaces/Data/IAirportData.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using FlightSystem.Api.Domain.Implementations.Entities; + +namespace FlightSystem.Api.Application.Interfaces.Data +{ + public interface IAirportData + { + public List GetAll(); + } +} diff --git a/RebusCore/Src/Application/Interfaces/Data/IBackupData.cs b/RebusCore/Src/Application/Interfaces/Data/IBackupData.cs new file mode 100644 index 0000000..5d003f5 --- /dev/null +++ b/RebusCore/Src/Application/Interfaces/Data/IBackupData.cs @@ -0,0 +1,9 @@ +using FlightSystem.Api.Domain.Interfaces; + +namespace FlightSystem.Api.Application.Interfaces.Data +{ + public interface IBackupData + { + void SetBackup(IEntity entity); + } +} diff --git a/RebusCore/Src/Application/Interfaces/Data/ICountryData.cs b/RebusCore/Src/Application/Interfaces/Data/ICountryData.cs new file mode 100644 index 0000000..48f7f4d --- /dev/null +++ b/RebusCore/Src/Application/Interfaces/Data/ICountryData.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using FlightSystem.Api.Domain.Implementations.Entities; + +namespace FlightSystem.Api.Application.Interfaces.Data +{ + public interface ICountryData + { + public List GetAll(); + } +} diff --git a/RebusCore/Src/Application/Interfaces/Data/IFlagImageData.cs b/RebusCore/Src/Application/Interfaces/Data/IFlagImageData.cs new file mode 100644 index 0000000..1013ac1 --- /dev/null +++ b/RebusCore/Src/Application/Interfaces/Data/IFlagImageData.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using FlightSystem.Api.Domain.Interfaces; + +namespace FlightSystem.Api.Application.Interfaces.Data +{ + public interface IFlagImageData + { + public void AddFlagsToCountries(IEnumerable countries); + } +} diff --git a/FlightSystem2/Src/Application/Interfaces/Data/IFlightData.cs b/RebusCore/Src/Application/Interfaces/Data/IFlightData.cs similarity index 69% rename from FlightSystem2/Src/Application/Interfaces/Data/IFlightData.cs rename to RebusCore/Src/Application/Interfaces/Data/IFlightData.cs index bcded74..df58b07 100644 --- a/FlightSystem2/Src/Application/Interfaces/Data/IFlightData.cs +++ b/RebusCore/Src/Application/Interfaces/Data/IFlightData.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Application.Interfaces.Data +namespace FlightSystem.Api.Application.Interfaces.Data { public interface IFlightData { diff --git a/RebusCore/Src/Application/Interfaces/Data/ILocationsData.cs b/RebusCore/Src/Application/Interfaces/Data/ILocationsData.cs new file mode 100644 index 0000000..023ad06 --- /dev/null +++ b/RebusCore/Src/Application/Interfaces/Data/ILocationsData.cs @@ -0,0 +1,9 @@ +using FlightSystem.Api.Domain.Interfaces; + +namespace FlightSystem.Api.Application.Interfaces.Data +{ + public interface ILocationsData + { + ILocations GetLocationsAll(); + } +} diff --git a/FlightSystem2/Src/Application/Interfaces/Data/IRouteData.cs b/RebusCore/Src/Application/Interfaces/Data/IRouteData.cs similarity index 68% rename from FlightSystem2/Src/Application/Interfaces/Data/IRouteData.cs rename to RebusCore/Src/Application/Interfaces/Data/IRouteData.cs index 92898e4..4677c57 100644 --- a/FlightSystem2/Src/Application/Interfaces/Data/IRouteData.cs +++ b/RebusCore/Src/Application/Interfaces/Data/IRouteData.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Application.Interfaces.Data +namespace FlightSystem.Api.Application.Interfaces.Data { public interface IRouteData { diff --git a/FlightSystem2/Src/Application/Interfaces/Factories/IDataFactory.cs b/RebusCore/Src/Application/Interfaces/Factories/IDataFactory.cs similarity index 68% rename from FlightSystem2/Src/Application/Interfaces/Factories/IDataFactory.cs rename to RebusCore/Src/Application/Interfaces/Factories/IDataFactory.cs index cf335dd..702a6da 100644 --- a/FlightSystem2/Src/Application/Interfaces/Factories/IDataFactory.cs +++ b/RebusCore/Src/Application/Interfaces/Factories/IDataFactory.cs @@ -1,6 +1,6 @@ -using FlightSystem.Api.Src.Application.Interfaces.Data; +using FlightSystem.Api.Application.Interfaces.Data; -namespace FlightSystem.Api.Src.Application.Interfaces.Factories +namespace FlightSystem.Api.Application.Interfaces.Factories { public interface IDataFactory { diff --git a/FlightSystem2/Src/Application/Interfaces/Factories/IResponseFactory.cs b/RebusCore/Src/Application/Interfaces/Factories/IResponseFactory.cs similarity index 65% rename from FlightSystem2/Src/Application/Interfaces/Factories/IResponseFactory.cs rename to RebusCore/Src/Application/Interfaces/Factories/IResponseFactory.cs index 8ef6500..2bdcc31 100644 --- a/FlightSystem2/Src/Application/Interfaces/Factories/IResponseFactory.cs +++ b/RebusCore/Src/Application/Interfaces/Factories/IResponseFactory.cs @@ -1,8 +1,8 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Application.Common.Factories +namespace FlightSystem.Api.Application.Interfaces.Factories { public interface IResponseFactory { diff --git a/FlightSystem2/Src/Application/Managers/FlightManager.cs b/RebusCore/Src/Application/Managers/FlightManager.cs similarity index 52% rename from FlightSystem2/Src/Application/Managers/FlightManager.cs rename to RebusCore/Src/Application/Managers/FlightManager.cs index 7bd9828..7056bfa 100644 --- a/FlightSystem2/Src/Application/Managers/FlightManager.cs +++ b/RebusCore/Src/Application/Managers/FlightManager.cs @@ -1,10 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Linq; -using FlightSystem.Api.Src.Application.Interfaces.AManagers; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Application.Interfaces.AManagers; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Application.Managers +namespace FlightSystem.Api.Application.Managers { public class FlightManager : AFlightManager { @@ -27,6 +28,23 @@ public override IFlight GetFlightById(ulong Id) return flightData.GetById(Id); } + public override string GetStringFlightById(ulong Id) + { + try + { + List entities = entityFactory.CreateEntities(); + entities.Add(flightData.GetById(Id)); + return responseFactory.CreateResponse("", entities); + } + catch (Exception ex) + { + string message = String.Format("Exception caught: {0}", ex); + logger.LogMessage(message); + return responseFactory.CreateResponse(message, null); + } + + } + public override List GetFlightsByTripParams(ITripParams tripParams) { List flights = flightData.GetDirectFlightsByTripParams(tripParams); diff --git a/FlightSystem2/Src/Application/Managers/JourneyManager.cs b/RebusCore/Src/Application/Managers/JourneyManager.cs similarity index 94% rename from FlightSystem2/Src/Application/Managers/JourneyManager.cs rename to RebusCore/Src/Application/Managers/JourneyManager.cs index f6e95e1..b168eaf 100644 --- a/FlightSystem2/Src/Application/Managers/JourneyManager.cs +++ b/RebusCore/Src/Application/Managers/JourneyManager.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; using System.Linq; -using FlightSystem.Api.Src.Application.Interfaces.AManagers; -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Application.Interfaces.AManagers; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Application.Managers +namespace FlightSystem.Api.Application.Managers { public class JourneyManager : AJourneyManager { diff --git a/FlightSystem2/Src/Application/Managers/LocationManager.cs b/RebusCore/Src/Application/Managers/LocationManager.cs similarity index 83% rename from FlightSystem2/Src/Application/Managers/LocationManager.cs rename to RebusCore/Src/Application/Managers/LocationManager.cs index 0d43cbe..085b202 100644 --- a/FlightSystem2/Src/Application/Managers/LocationManager.cs +++ b/RebusCore/Src/Application/Managers/LocationManager.cs @@ -1,10 +1,10 @@ using System; using System.Collections.Generic; -using FlightSystem.Api.Src.Application.Interfaces.AManagers; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Application.Interfaces.AManagers; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Application.Managers +namespace FlightSystem.Api.Application.Managers { public class LocationManager : ALocationManager { diff --git a/FlightSystem2/Src/Application/Managers/RouteManager.cs b/RebusCore/Src/Application/Managers/RouteManager.cs similarity index 87% rename from FlightSystem2/Src/Application/Managers/RouteManager.cs rename to RebusCore/Src/Application/Managers/RouteManager.cs index 6b0556a..a70ccb6 100644 --- a/FlightSystem2/Src/Application/Managers/RouteManager.cs +++ b/RebusCore/Src/Application/Managers/RouteManager.cs @@ -1,11 +1,11 @@ -using FlightSystem.Api.Src.Application.Interfaces.AManagers; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Domain.Interfaces; -using System; +using System; using System.Collections.Generic; using System.Linq; +using FlightSystem.Api.Application.Interfaces.AManagers; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Application.Managers +namespace FlightSystem.Api.Application.Managers { public class RouteManager : ARouteManager { @@ -37,7 +37,7 @@ public override List ManageRoutes(ITripParams tripPar) if (numOfRoutes >= maxRoutesFromDb) return SortAndCutRoutes(routes); - routes.ToList().AddRange(Get1StopRoutes()); + routes.AddRange(Get1StopRoutes()); if (numOfRoutes >= maxRoutesFromDb) return SortAndCutRoutes(routes); @@ -90,12 +90,13 @@ private void AddRouteInfo(List routes) route.price += flight.price; } route.timeSpan = route.flights[route.flights.Count() - 1].arrives - route.flights[0].departs; + route.best = (int)route.timeSpan.TotalMinutes + (int)route.price; } } private List SortAndCutRoutes(List routes) { - //routes.Sort(); //update + routes = routes.OrderBy(o=>o.best).ToList(); try { routes.RemoveRange(maxRoutesReturn, routes.Count() - maxRoutesReturn); diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/Airport.cs b/RebusCore/Src/Domain/Implementations/Entities/Airport.cs similarity index 92% rename from FlightSystem2/Src/Domain/Implementations/Entities/Airport.cs rename to RebusCore/Src/Domain/Implementations/Entities/Airport.cs index 4418f6e..62fb64d 100644 --- a/FlightSystem2/Src/Domain/Implementations/Entities/Airport.cs +++ b/RebusCore/Src/Domain/Implementations/Entities/Airport.cs @@ -1,5 +1,5 @@ +using FlightSystem.Api.Domain.Interfaces; using Newtonsoft.Json; -using FlightSystem.Api.Src.Domain.Interfaces; namespace FlightSystem.Api.Domain.Implementations.Entities { diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/City.cs b/RebusCore/Src/Domain/Implementations/Entities/City.cs similarity index 100% rename from FlightSystem2/Src/Domain/Implementations/Entities/City.cs rename to RebusCore/Src/Domain/Implementations/Entities/City.cs diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/Country.cs b/RebusCore/Src/Domain/Implementations/Entities/Country.cs similarity index 90% rename from FlightSystem2/Src/Domain/Implementations/Entities/Country.cs rename to RebusCore/Src/Domain/Implementations/Entities/Country.cs index 89b22d0..e354e2d 100644 --- a/FlightSystem2/Src/Domain/Implementations/Entities/Country.cs +++ b/RebusCore/Src/Domain/Implementations/Entities/Country.cs @@ -1,4 +1,4 @@ -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Domain.Interfaces; using Newtonsoft.Json; namespace FlightSystem.Api.Domain.Implementations.Entities diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/Entity.cs b/RebusCore/Src/Domain/Implementations/Entities/Entity.cs similarity index 70% rename from FlightSystem2/Src/Domain/Implementations/Entities/Entity.cs rename to RebusCore/Src/Domain/Implementations/Entities/Entity.cs index 22550e6..e1087d4 100644 --- a/FlightSystem2/Src/Domain/Implementations/Entities/Entity.cs +++ b/RebusCore/Src/Domain/Implementations/Entities/Entity.cs @@ -1,4 +1,4 @@ -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Domain.Interfaces; namespace FlightSystem.Api.Domain.Implementations.Entities { diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/Flight.cs b/RebusCore/Src/Domain/Implementations/Entities/Flight.cs similarity index 95% rename from FlightSystem2/Src/Domain/Implementations/Entities/Flight.cs rename to RebusCore/Src/Domain/Implementations/Entities/Flight.cs index e8bf6ec..5c48e09 100644 --- a/FlightSystem2/Src/Domain/Implementations/Entities/Flight.cs +++ b/RebusCore/Src/Domain/Implementations/Entities/Flight.cs @@ -1,5 +1,5 @@ using System; -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Domain.Interfaces; using Newtonsoft.Json; namespace FlightSystem.Api.Domain.Implementations.Entities diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/Journey.cs b/RebusCore/Src/Domain/Implementations/Entities/Journey.cs similarity index 90% rename from FlightSystem2/Src/Domain/Implementations/Entities/Journey.cs rename to RebusCore/Src/Domain/Implementations/Entities/Journey.cs index 4df5ec7..0e221df 100644 --- a/FlightSystem2/Src/Domain/Implementations/Entities/Journey.cs +++ b/RebusCore/Src/Domain/Implementations/Entities/Journey.cs @@ -1,6 +1,6 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using Newtonsoft.Json; +using Newtonsoft.Json; using System.Collections.Generic; +using FlightSystem.Api.Domain.Interfaces; namespace FlightSystem.Api.Domain.Implementations.Entities { diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/Locations.cs b/RebusCore/Src/Domain/Implementations/Entities/Locations.cs similarity index 89% rename from FlightSystem2/Src/Domain/Implementations/Entities/Locations.cs rename to RebusCore/Src/Domain/Implementations/Entities/Locations.cs index d4c5559..71e3dd6 100644 --- a/FlightSystem2/Src/Domain/Implementations/Entities/Locations.cs +++ b/RebusCore/Src/Domain/Implementations/Entities/Locations.cs @@ -1,6 +1,6 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using Newtonsoft.Json; +using Newtonsoft.Json; using System.Collections.Generic; +using FlightSystem.Api.Domain.Interfaces; namespace FlightSystem.Api.Domain.Implementations.Entities { diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/Route.cs b/RebusCore/Src/Domain/Implementations/Entities/Route.cs similarity index 85% rename from FlightSystem2/Src/Domain/Implementations/Entities/Route.cs rename to RebusCore/Src/Domain/Implementations/Entities/Route.cs index 8c2e2dc..bb051f9 100644 --- a/FlightSystem2/Src/Domain/Implementations/Entities/Route.cs +++ b/RebusCore/Src/Domain/Implementations/Entities/Route.cs @@ -1,7 +1,7 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using Newtonsoft.Json; +using Newtonsoft.Json; using System; using System.Collections.Generic; +using FlightSystem.Api.Domain.Interfaces; namespace FlightSystem.Api.Domain.Implementations.Entities { @@ -15,6 +15,9 @@ public class Route : Entity, IRoute [JsonProperty] public TimeSpan timeSpan { get; set; } + [JsonProperty] + public int best { get; set; } + [JsonProperty] public List flights { get { return _flights; } diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/Trip.cs b/RebusCore/Src/Domain/Implementations/Entities/Trip.cs similarity index 91% rename from FlightSystem2/Src/Domain/Implementations/Entities/Trip.cs rename to RebusCore/Src/Domain/Implementations/Entities/Trip.cs index 2c82aa2..ce97e0d 100644 --- a/FlightSystem2/Src/Domain/Implementations/Entities/Trip.cs +++ b/RebusCore/Src/Domain/Implementations/Entities/Trip.cs @@ -1,6 +1,6 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using Newtonsoft.Json; +using Newtonsoft.Json; using System.Collections.Generic; +using FlightSystem.Api.Domain.Interfaces; namespace FlightSystem.Api.Domain.Implementations.Entities { diff --git a/FlightSystem2/Src/Domain/Implementations/Entities/TripParams.cs b/RebusCore/Src/Domain/Implementations/Entities/TripParams.cs similarity index 93% rename from FlightSystem2/Src/Domain/Implementations/Entities/TripParams.cs rename to RebusCore/Src/Domain/Implementations/Entities/TripParams.cs index e5d5cd2..ab5c717 100644 --- a/FlightSystem2/Src/Domain/Implementations/Entities/TripParams.cs +++ b/RebusCore/Src/Domain/Implementations/Entities/TripParams.cs @@ -1,4 +1,4 @@ -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Domain.Interfaces; using Newtonsoft.Json; namespace FlightSystem.Api.Domain.Implementations.Entities diff --git a/FlightSystem2/Src/Domain/Implementations/Factories/EntityFactory.cs b/RebusCore/Src/Domain/Implementations/Factories/EntityFactory.cs similarity index 80% rename from FlightSystem2/Src/Domain/Implementations/Factories/EntityFactory.cs rename to RebusCore/Src/Domain/Implementations/Factories/EntityFactory.cs index 41be7c4..690ac91 100644 --- a/FlightSystem2/Src/Domain/Implementations/Factories/EntityFactory.cs +++ b/RebusCore/Src/Domain/Implementations/Factories/EntityFactory.cs @@ -1,8 +1,8 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Domain.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; +using FlightSystem.Api.Domain.Implementations.Entities; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Domain.Implementations.Factories +namespace FlightSystem.Api.Domain.Implementations.Factories { public class EntityFactory : IEntityFactory { diff --git a/RebusCore/Src/Domain/Interfaces/IAirport.cs b/RebusCore/Src/Domain/Interfaces/IAirport.cs new file mode 100644 index 0000000..42ad406 --- /dev/null +++ b/RebusCore/Src/Domain/Interfaces/IAirport.cs @@ -0,0 +1,15 @@ +namespace FlightSystem.Api.Domain.Interfaces +{ + public interface IAirport : IEntity + { + string fullName { get; set; } + + string type { get; } + + string code { get; set; } + + string cityName { get; set; } + + string countryName { get; set; } + } +} diff --git a/FlightSystem2/Src/Domain/Interfaces/ICountry.cs b/RebusCore/Src/Domain/Interfaces/ICountry.cs similarity index 81% rename from FlightSystem2/Src/Domain/Interfaces/ICountry.cs rename to RebusCore/Src/Domain/Interfaces/ICountry.cs index b9b3e82..3d542d2 100644 --- a/FlightSystem2/Src/Domain/Interfaces/ICountry.cs +++ b/RebusCore/Src/Domain/Interfaces/ICountry.cs @@ -1,4 +1,4 @@ -namespace FlightSystem.Api.Src.Domain.Interfaces +namespace FlightSystem.Api.Domain.Interfaces { public interface ICountry : IEntity { diff --git a/RebusCore/Src/Domain/Interfaces/IEntity.cs b/RebusCore/Src/Domain/Interfaces/IEntity.cs new file mode 100644 index 0000000..3fdc7a3 --- /dev/null +++ b/RebusCore/Src/Domain/Interfaces/IEntity.cs @@ -0,0 +1,6 @@ +namespace FlightSystem.Api.Domain.Interfaces +{ + public interface IEntity + { + } +} diff --git a/FlightSystem2/Src/Domain/Interfaces/IEntityFactory.cs b/RebusCore/Src/Domain/Interfaces/IEntityFactory.cs similarity index 90% rename from FlightSystem2/Src/Domain/Interfaces/IEntityFactory.cs rename to RebusCore/Src/Domain/Interfaces/IEntityFactory.cs index f46d13c..41d795a 100644 --- a/FlightSystem2/Src/Domain/Interfaces/IEntityFactory.cs +++ b/RebusCore/Src/Domain/Interfaces/IEntityFactory.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace FlightSystem.Api.Src.Domain.Interfaces +namespace FlightSystem.Api.Domain.Interfaces { public interface IEntityFactory { diff --git a/FlightSystem2/Src/Domain/Interfaces/IFlight.cs b/RebusCore/Src/Domain/Interfaces/IFlight.cs similarity index 89% rename from FlightSystem2/Src/Domain/Interfaces/IFlight.cs rename to RebusCore/Src/Domain/Interfaces/IFlight.cs index 740de6d..e3875bd 100644 --- a/FlightSystem2/Src/Domain/Interfaces/IFlight.cs +++ b/RebusCore/Src/Domain/Interfaces/IFlight.cs @@ -1,6 +1,6 @@ using System; -namespace FlightSystem.Api.Src.Domain.Interfaces +namespace FlightSystem.Api.Domain.Interfaces { public interface IFlight : IEntity { diff --git a/FlightSystem2/Src/Domain/Interfaces/IJourney.cs b/RebusCore/Src/Domain/Interfaces/IJourney.cs similarity index 81% rename from FlightSystem2/Src/Domain/Interfaces/IJourney.cs rename to RebusCore/Src/Domain/Interfaces/IJourney.cs index e75b9f1..09b603d 100644 --- a/FlightSystem2/Src/Domain/Interfaces/IJourney.cs +++ b/RebusCore/Src/Domain/Interfaces/IJourney.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace FlightSystem.Api.Src.Domain.Interfaces +namespace FlightSystem.Api.Domain.Interfaces { public interface IJourney : IEntity { diff --git a/FlightSystem2/Src/Domain/Interfaces/ILocations.cs b/RebusCore/Src/Domain/Interfaces/ILocations.cs similarity index 81% rename from FlightSystem2/Src/Domain/Interfaces/ILocations.cs rename to RebusCore/Src/Domain/Interfaces/ILocations.cs index 5106913..c65054d 100644 --- a/FlightSystem2/Src/Domain/Interfaces/ILocations.cs +++ b/RebusCore/Src/Domain/Interfaces/ILocations.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace FlightSystem.Api.Src.Domain.Interfaces +namespace FlightSystem.Api.Domain.Interfaces { public interface ILocations : IEntity { diff --git a/FlightSystem2/Src/Domain/Interfaces/IRoute.cs b/RebusCore/Src/Domain/Interfaces/IRoute.cs similarity index 74% rename from FlightSystem2/Src/Domain/Interfaces/IRoute.cs rename to RebusCore/Src/Domain/Interfaces/IRoute.cs index 6bea776..5e9c33a 100644 --- a/FlightSystem2/Src/Domain/Interfaces/IRoute.cs +++ b/RebusCore/Src/Domain/Interfaces/IRoute.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; -namespace FlightSystem.Api.Src.Domain.Interfaces +namespace FlightSystem.Api.Domain.Interfaces { public interface IRoute : IEntity { @@ -9,6 +9,8 @@ public interface IRoute : IEntity public TimeSpan timeSpan { get; set; } + public int best { get; set; } + public List flights { get; set; } } } diff --git a/FlightSystem2/Src/Domain/Interfaces/ITrip.cs b/RebusCore/Src/Domain/Interfaces/ITrip.cs similarity index 82% rename from FlightSystem2/Src/Domain/Interfaces/ITrip.cs rename to RebusCore/Src/Domain/Interfaces/ITrip.cs index 5960a5d..5d175a1 100644 --- a/FlightSystem2/Src/Domain/Interfaces/ITrip.cs +++ b/RebusCore/Src/Domain/Interfaces/ITrip.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace FlightSystem.Api.Src.Domain.Interfaces +namespace FlightSystem.Api.Domain.Interfaces { public interface ITrip : IEntity { diff --git a/FlightSystem2/Src/Domain/Interfaces/ITripParams.cs b/RebusCore/Src/Domain/Interfaces/ITripParams.cs similarity index 81% rename from FlightSystem2/Src/Domain/Interfaces/ITripParams.cs rename to RebusCore/Src/Domain/Interfaces/ITripParams.cs index 0f00c9a..4120178 100644 --- a/FlightSystem2/Src/Domain/Interfaces/ITripParams.cs +++ b/RebusCore/Src/Domain/Interfaces/ITripParams.cs @@ -1,4 +1,4 @@ -namespace FlightSystem.Api.Src.Domain.Interfaces +namespace FlightSystem.Api.Domain.Interfaces { public interface ITripParams : IEntity { diff --git a/FlightSystem2/Src/Integration/Common/Services/Converters/DatesConverter.cs b/RebusCore/Src/Integration/Common/Services/Converters/DatesConverter.cs similarity index 83% rename from FlightSystem2/Src/Integration/Common/Services/Converters/DatesConverter.cs rename to RebusCore/Src/Integration/Common/Services/Converters/DatesConverter.cs index b90ea91..5f7feca 100644 --- a/FlightSystem2/Src/Integration/Common/Services/Converters/DatesConverter.cs +++ b/RebusCore/Src/Integration/Common/Services/Converters/DatesConverter.cs @@ -1,7 +1,7 @@ -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using System; +using System; +using FlightSystem.Api.Application.Interfaces.AServices; -namespace FlightSystem.Api.Src.Integration.Common.Services.Converters +namespace FlightSystem.Api.Integration.Common.Services.Converters { public class DatesConverter : IDateConverter { diff --git a/FlightSystem2/Src/Integration/Common/Services/Converters/JSONSerializer.cs b/RebusCore/Src/Integration/Common/Services/Converters/JSONSerializer.cs similarity index 83% rename from FlightSystem2/Src/Integration/Common/Services/Converters/JSONSerializer.cs rename to RebusCore/Src/Integration/Common/Services/Converters/JSONSerializer.cs index c425ca9..3b89b03 100644 --- a/FlightSystem2/Src/Integration/Common/Services/Converters/JSONSerializer.cs +++ b/RebusCore/Src/Integration/Common/Services/Converters/JSONSerializer.cs @@ -1,7 +1,7 @@ -using FlightSystem.Api.Src.Integration.Common.Interfaces; +using FlightSystem.Api.Application.Interfaces.AServices; using Newtonsoft.Json; -namespace FlightSystem.Api.Src.Integration.Common.Services.Converters +namespace FlightSystem.Api.Integration.Common.Services.Converters { public class JSONSerializer : ISerializer { diff --git a/FlightSystem2/Src/Integration/Common/Services/Converters/MyImageConverter.cs b/RebusCore/Src/Integration/Common/Services/Converters/MyImageConverter.cs similarity index 85% rename from FlightSystem2/Src/Integration/Common/Services/Converters/MyImageConverter.cs rename to RebusCore/Src/Integration/Common/Services/Converters/MyImageConverter.cs index 1471fff..4221a01 100644 --- a/FlightSystem2/Src/Integration/Common/Services/Converters/MyImageConverter.cs +++ b/RebusCore/Src/Integration/Common/Services/Converters/MyImageConverter.cs @@ -1,9 +1,9 @@ using System; using System.Drawing; using System.IO; -using FlightSystem.Api.Src.Integration.Common.Interfaces; +using FlightSystem.Api.Application.Interfaces.AServices; -namespace FlightSystem.Api.Src.Integration.Common.Services.Converters +namespace FlightSystem.Api.Integration.Common.Services.Converters { public class MyImageConverter : IImageConverter { diff --git a/FlightSystem2/Src/Integration/Common/Services/Logger/ConsoleLogger.cs b/RebusCore/Src/Integration/Common/Services/Logger/ConsoleLogger.cs similarity index 67% rename from FlightSystem2/Src/Integration/Common/Services/Logger/ConsoleLogger.cs rename to RebusCore/Src/Integration/Common/Services/Logger/ConsoleLogger.cs index 85b2044..3106228 100644 --- a/FlightSystem2/Src/Integration/Common/Services/Logger/ConsoleLogger.cs +++ b/RebusCore/Src/Integration/Common/Services/Logger/ConsoleLogger.cs @@ -1,7 +1,7 @@ -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using System; +using System; +using FlightSystem.Api.Application.Interfaces.AServices; -namespace FlightSystem.Api.Src.Integration.Common.Services.Logger +namespace FlightSystem.Api.Integration.Common.Services.Logger { public class ConsoleLogger : ILogger { diff --git a/FlightSystem2/Src/Integration/Common/Services/Response/JSONResponseFactory.cs b/RebusCore/Src/Integration/Common/Services/Response/JSONResponseFactory.cs similarity index 80% rename from FlightSystem2/Src/Integration/Common/Services/Response/JSONResponseFactory.cs rename to RebusCore/Src/Integration/Common/Services/Response/JSONResponseFactory.cs index 3402d72..1589028 100644 --- a/FlightSystem2/Src/Integration/Common/Services/Response/JSONResponseFactory.cs +++ b/RebusCore/Src/Integration/Common/Services/Response/JSONResponseFactory.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Src.Application.Common.Factories; -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Factories; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Integration.Common.Services.Response +namespace FlightSystem.Api.Integration.Common.Services.Response { public class JSONResponseFactory : IResponseFactory { diff --git a/FlightSystem2/Src/Integration/Common/Services/Response/Response.cs b/RebusCore/Src/Integration/Common/Services/Response/Response.cs similarity index 79% rename from FlightSystem2/Src/Integration/Common/Services/Response/Response.cs rename to RebusCore/Src/Integration/Common/Services/Response/Response.cs index 7637f4f..497f2db 100644 --- a/FlightSystem2/Src/Integration/Common/Services/Response/Response.cs +++ b/RebusCore/Src/Integration/Common/Services/Response/Response.cs @@ -1,7 +1,7 @@ -using FlightSystem.Api.Src.Integration.Common.Interfaces; +using FlightSystem.Api.Application.Interfaces.AServices; using Newtonsoft.Json; -namespace FlightSystem.Api.Src.Integration.Common.Services.Response +namespace FlightSystem.Api.Integration.Common.Services.Response { [JsonObject(MemberSerialization.OptIn)] public class Response : IResponse diff --git a/FlightSystem2/Src/Integration/Common/Services/Response/ResponseBody.cs b/RebusCore/Src/Integration/Common/Services/Response/ResponseBody.cs similarity index 64% rename from FlightSystem2/Src/Integration/Common/Services/Response/ResponseBody.cs rename to RebusCore/Src/Integration/Common/Services/Response/ResponseBody.cs index bbbfefa..b2e6896 100644 --- a/FlightSystem2/Src/Integration/Common/Services/Response/ResponseBody.cs +++ b/RebusCore/Src/Integration/Common/Services/Response/ResponseBody.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Interfaces; +using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Domain.Interfaces; using Newtonsoft.Json; -using System.Collections.Generic; -namespace FlightSystem.Api.Src.Integration.Common.Services.Response +namespace FlightSystem.Api.Integration.Common.Services.Response { [JsonObject(MemberSerialization.OptIn)] public class ResponseBody : IResponseBody diff --git a/FlightSystem2/Src/Integration/Common/Services/Response/ResponseError.cs b/RebusCore/Src/Integration/Common/Services/Response/ResponseError.cs similarity index 75% rename from FlightSystem2/Src/Integration/Common/Services/Response/ResponseError.cs rename to RebusCore/Src/Integration/Common/Services/Response/ResponseError.cs index c344818..3b8b7f2 100644 --- a/FlightSystem2/Src/Integration/Common/Services/Response/ResponseError.cs +++ b/RebusCore/Src/Integration/Common/Services/Response/ResponseError.cs @@ -1,7 +1,7 @@ -using FlightSystem.Api.Src.Integration.Common.Interfaces; +using FlightSystem.Api.Application.Interfaces.AServices; using Newtonsoft.Json; -namespace FlightSystem.Api.Src.Integration.Common.Services.Response +namespace FlightSystem.Api.Integration.Common.Services.Response { [JsonObject(MemberSerialization.OptIn)] public class ResponseError : IResponseError diff --git a/FlightSystem2/Src/Integration/Common/Services/Response/ResponseHeader.cs b/RebusCore/Src/Integration/Common/Services/Response/ResponseHeader.cs similarity index 75% rename from FlightSystem2/Src/Integration/Common/Services/Response/ResponseHeader.cs rename to RebusCore/Src/Integration/Common/Services/Response/ResponseHeader.cs index 00729b0..ecabd3a 100644 --- a/FlightSystem2/Src/Integration/Common/Services/Response/ResponseHeader.cs +++ b/RebusCore/Src/Integration/Common/Services/Response/ResponseHeader.cs @@ -1,7 +1,7 @@ -using FlightSystem.Api.Src.Integration.Common.Interfaces; +using FlightSystem.Api.Application.Interfaces.AServices; using Newtonsoft.Json; -namespace FlightSystem.Api.Src.Integration.Common.Services.Response +namespace FlightSystem.Api.Integration.Common.Services.Response { [JsonObject(MemberSerialization.OptIn)] public class ResponseHeader : IResponseHeader diff --git a/RebusCore/Src/Integration/Config/IntegrationConfig.cs b/RebusCore/Src/Integration/Config/IntegrationConfig.cs new file mode 100644 index 0000000..83853b1 --- /dev/null +++ b/RebusCore/Src/Integration/Config/IntegrationConfig.cs @@ -0,0 +1,21 @@ +namespace FlightSystem.Api.Integration.Config +{ + public static class IntegrationConfig + { + + public static string GetDataBaseUri() { return "bolt://neo4j:7687"; } + + public static string GetDataBaseUsername() { return "neo4j"; } + public static string GetDataBasePassword() { return "123"; } + + public static string GetBackupFilePath() { return @"C:\Users\Paulius\Desktop\Dummies\"; } + + public static string GetBackupFileType() { return ".json"; } + + public static string GetFlagsFilePath() { return "/app/files/flags/"; } + + public static string GetFlagsFileType() { return ".png"; } + + + } +} diff --git a/FlightSystem2/Src/Integration/Config/Program.cs b/RebusCore/Src/Integration/Config/Program.cs similarity index 90% rename from FlightSystem2/Src/Integration/Config/Program.cs rename to RebusCore/Src/Integration/Config/Program.cs index 8225554..fa1273f 100644 --- a/FlightSystem2/Src/Integration/Config/Program.cs +++ b/RebusCore/Src/Integration/Config/Program.cs @@ -1,10 +1,10 @@ using System; -using FlightSystem.Api.Src.Repository.Neo4J.Common; +using FlightSystem.Api.Repository.Neo4J.Common; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace FlightSystem +namespace FlightSystem.Api.Integration.Config { public class Program { diff --git a/FlightSystem2/Src/Integration/Config/Startup.cs b/RebusCore/Src/Integration/Config/Startup.cs similarity index 96% rename from FlightSystem2/Src/Integration/Config/Startup.cs rename to RebusCore/Src/Integration/Config/Startup.cs index db37755..ed967df 100644 --- a/FlightSystem2/Src/Integration/Config/Startup.cs +++ b/RebusCore/Src/Integration/Config/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; -namespace FlightSystem +namespace FlightSystem.Api.Integration.Config { public class Startup { diff --git a/FlightSystem2/Src/Integration/Controllers/AllFlightsController.cs b/RebusCore/Src/Integration/Controllers/AllFlightsController.cs similarity index 77% rename from FlightSystem2/Src/Integration/Controllers/AllFlightsController.cs rename to RebusCore/Src/Integration/Controllers/AllFlightsController.cs index 14c85b3..8196d2c 100644 --- a/FlightSystem2/Src/Integration/Controllers/AllFlightsController.cs +++ b/RebusCore/Src/Integration/Controllers/AllFlightsController.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Src.Application.Interfaces.AManagers; -using FlightSystem.Api.Src.Application.Managers; +using FlightSystem.Api.Application.Interfaces.AManagers; +using FlightSystem.Api.Application.Managers; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -namespace FlightSystem.Api.Src.Integration.Controllers +namespace RebusCore.Src.Integration.Controllers { [ApiController] [Route("[controller]")] diff --git a/FlightSystem2/Src/Integration/Controllers/AllLocationsController.cs b/RebusCore/Src/Integration/Controllers/AllLocationsController.cs similarity index 78% rename from FlightSystem2/Src/Integration/Controllers/AllLocationsController.cs rename to RebusCore/Src/Integration/Controllers/AllLocationsController.cs index 0afbfbd..a7cf6ca 100644 --- a/FlightSystem2/Src/Integration/Controllers/AllLocationsController.cs +++ b/RebusCore/Src/Integration/Controllers/AllLocationsController.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Src.Application.Interfaces.AManagers; -using FlightSystem.Api.Src.Application.Managers; +using FlightSystem.Api.Application.Interfaces.AManagers; +using FlightSystem.Api.Application.Managers; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -namespace FlightSystem.Api.Src.Integration.Controllers +namespace RebusCore.Src.Integration.Controllers { [ApiController] [Route("[controller]")] diff --git a/FlightSystem2/Src/Integration/Controllers/FlightController.cs b/RebusCore/Src/Integration/Controllers/FlightController.cs similarity index 68% rename from FlightSystem2/Src/Integration/Controllers/FlightController.cs rename to RebusCore/Src/Integration/Controllers/FlightController.cs index b22d778..c5fba43 100644 --- a/FlightSystem2/Src/Integration/Controllers/FlightController.cs +++ b/RebusCore/Src/Integration/Controllers/FlightController.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Src.Application.Interfaces.AManagers; -using FlightSystem.Api.Src.Application.Managers; +using FlightSystem.Api.Application.Interfaces.AManagers; +using FlightSystem.Api.Application.Managers; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -namespace FlightSystem.Api.Src.Integration.Controllers +namespace RebusCore.Src.Integration.Controllers { [ApiController] [Route("[controller]")] @@ -21,7 +21,7 @@ public FlightController(ILogger logger) [HttpGet] public string Get(ulong flightId) { - return _flightManager.GetFlightById(flightId).ToString(); + return _flightManager.GetStringFlightById(flightId); } } } \ No newline at end of file diff --git a/FlightSystem2/Src/Integration/Controllers/JourneyController.cs b/RebusCore/Src/Integration/Controllers/JourneyController.cs similarity index 85% rename from FlightSystem2/Src/Integration/Controllers/JourneyController.cs rename to RebusCore/Src/Integration/Controllers/JourneyController.cs index e93b8b8..24a2eca 100644 --- a/FlightSystem2/Src/Integration/Controllers/JourneyController.cs +++ b/RebusCore/Src/Integration/Controllers/JourneyController.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Src.Application.Interfaces.AManagers; -using FlightSystem.Api.Src.Application.Managers; +using FlightSystem.Api.Application.Interfaces.AManagers; +using FlightSystem.Api.Application.Managers; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -namespace FlightSystem.Api.Src.Integration.Controllers +namespace RebusCore.Src.Integration.Controllers { [ApiController] [Route("[controller]")] diff --git a/RebusCore/Src/Integration/Controllers/TestConnController.cs b/RebusCore/Src/Integration/Controllers/TestConnController.cs new file mode 100644 index 0000000..5519d69 --- /dev/null +++ b/RebusCore/Src/Integration/Controllers/TestConnController.cs @@ -0,0 +1,32 @@ +using FlightSystem.Api.Application.Interfaces.AManagers; +using FlightSystem.Api.Application.Managers; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using FlightSystem.Api.Repository.Neo4J.Common; + +namespace RebusCore.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class TestConnController : ControllerBase + { + private AFlightManager _flightManager = new FlightManager(); + + private readonly ILogger _logger; + + public TestConnController(ILogger logger) + { + _logger = logger; + } + + /* FIXME remove dirrect realations. Maybe use application logic instead */ + [HttpGet] + public string Get() + { + if (Neo4JContext.RunQuery("MATCH (a { name: 'TEST' }) RETURN a").ToString() == "Neo4j.Driver.Internal.Result.StatementResult"){ + return "TEST_OK"; + } + return "TEST_ERR"; + } + } +} diff --git a/FlightSystem2/Src/Repository/Common/DataFactoryDelegation.cs b/RebusCore/Src/Repository/Common/DataFactoryDelegation.cs similarity index 80% rename from FlightSystem2/Src/Repository/Common/DataFactoryDelegation.cs rename to RebusCore/Src/Repository/Common/DataFactoryDelegation.cs index b1703f0..f47dc40 100644 --- a/FlightSystem2/Src/Repository/Common/DataFactoryDelegation.cs +++ b/RebusCore/Src/Repository/Common/DataFactoryDelegation.cs @@ -1,10 +1,10 @@ -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Application.Interfaces.Factories; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Repository.FileSystem.Common; -using FlightSystem.Api.Src.Repository.Neo4J.Common; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Application.Interfaces.Factories; +using FlightSystem.Api.Repository.FileSystem.Common; +using FlightSystem.Api.Repository.Neo4J.Common; -namespace FlightSystem.Api.Src.Repository.Common +namespace FlightSystem.Api.Repository.Common { public class DataFactoryDelegation : IDataFactory { diff --git a/FlightSystem2/Src/Repository/Common/Neo4JDataFactory.cs b/RebusCore/Src/Repository/Common/Neo4JDataFactory.cs similarity index 78% rename from FlightSystem2/Src/Repository/Common/Neo4JDataFactory.cs rename to RebusCore/Src/Repository/Common/Neo4JDataFactory.cs index 6838e8f..943e5de 100644 --- a/FlightSystem2/Src/Repository/Common/Neo4JDataFactory.cs +++ b/RebusCore/Src/Repository/Common/Neo4JDataFactory.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Application.Interfaces.Factories; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Repository.FileSystem.Data; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Application.Interfaces.Factories; +using FlightSystem.Api.Repository.FileSystem.Data; -namespace FlightSystem.Api.Src.Repository.Common +namespace FlightSystem.Api.Repository.Common { public class Neo4JDataFactory : IDataFactory { diff --git a/FlightSystem2/Src/Repository/FileSystem/Common/FileSystemDataFactory.cs b/RebusCore/Src/Repository/FileSystem/Common/FileSystemDataFactory.cs similarity index 78% rename from FlightSystem2/Src/Repository/FileSystem/Common/FileSystemDataFactory.cs rename to RebusCore/Src/Repository/FileSystem/Common/FileSystemDataFactory.cs index a749deb..e8f8d43 100644 --- a/FlightSystem2/Src/Repository/FileSystem/Common/FileSystemDataFactory.cs +++ b/RebusCore/Src/Repository/FileSystem/Common/FileSystemDataFactory.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Application.Interfaces.Factories; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Repository.FileSystem.Data; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Application.Interfaces.Factories; +using FlightSystem.Api.Repository.FileSystem.Data; -namespace FlightSystem.Api.Src.Repository.FileSystem.Common +namespace FlightSystem.Api.Repository.FileSystem.Common { public class FileSystemDataFactory : IDataFactory { diff --git a/FlightSystem2/Src/Repository/FileSystem/Data/AirportData.cs b/RebusCore/Src/Repository/FileSystem/Data/AirportData.cs similarity index 51% rename from FlightSystem2/Src/Repository/FileSystem/Data/AirportData.cs rename to RebusCore/Src/Repository/FileSystem/Data/AirportData.cs index e903df4..2daea78 100644 --- a/FlightSystem2/Src/Repository/FileSystem/Data/AirportData.cs +++ b/RebusCore/Src/Repository/FileSystem/Data/AirportData.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using System; +using System; using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Implementations.Entities; -namespace FlightSystem.Api.Src.Repository.FileSystem.Data +namespace FlightSystem.Api.Repository.FileSystem.Data { public class AirportData : IAirportData { diff --git a/FlightSystem2/Src/Repository/FileSystem/Data/BackupData.cs b/RebusCore/Src/Repository/FileSystem/Data/BackupData.cs similarity index 67% rename from FlightSystem2/Src/Repository/FileSystem/Data/BackupData.cs rename to RebusCore/Src/Repository/FileSystem/Data/BackupData.cs index ef2316c..8ee1bf7 100644 --- a/FlightSystem2/Src/Repository/FileSystem/Data/BackupData.cs +++ b/RebusCore/Src/Repository/FileSystem/Data/BackupData.cs @@ -1,11 +1,11 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Integration.Config; -using System.Collections.Generic; +using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Implementations.Entities; +using FlightSystem.Api.Domain.Interfaces; +using FlightSystem.Api.Integration.Config; -namespace FlightSystem.Api.Src.Repository.FileSystem.Data +namespace FlightSystem.Api.Repository.FileSystem.Data { public class BackupData : IBackupData { diff --git a/FlightSystem2/Src/Repository/FileSystem/Data/CountryData.cs b/RebusCore/Src/Repository/FileSystem/Data/CountryData.cs similarity index 51% rename from FlightSystem2/Src/Repository/FileSystem/Data/CountryData.cs rename to RebusCore/Src/Repository/FileSystem/Data/CountryData.cs index 31d7619..efbaf66 100644 --- a/FlightSystem2/Src/Repository/FileSystem/Data/CountryData.cs +++ b/RebusCore/Src/Repository/FileSystem/Data/CountryData.cs @@ -1,9 +1,9 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using System; +using System; using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Implementations.Entities; -namespace FlightSystem.Api.Src.Repository.FileSystem.Data +namespace FlightSystem.Api.Repository.FileSystem.Data { public class CountryData : ICountryData { diff --git a/FlightSystem2/Src/Repository/FileSystem/Data/FlagImageData.cs b/RebusCore/Src/Repository/FileSystem/Data/FlagImageData.cs similarity index 68% rename from FlightSystem2/Src/Repository/FileSystem/Data/FlagImageData.cs rename to RebusCore/Src/Repository/FileSystem/Data/FlagImageData.cs index 103125a..86471fa 100644 --- a/FlightSystem2/Src/Repository/FileSystem/Data/FlagImageData.cs +++ b/RebusCore/Src/Repository/FileSystem/Data/FlagImageData.cs @@ -1,11 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Drawing; -using FlightSystem.Api.Src.Integration.Config; -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Application.Interfaces.Data; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Interfaces; +using FlightSystem.Api.Integration.Config; -namespace FlightSystem.Api.Src.Repository.FileSystem.Data +namespace FlightSystem.Api.Repository.FileSystem.Data { public class FlagImageData : IFlagImageData { @@ -32,12 +33,13 @@ private string GetFlagImageString(string countryCode) try { - image = Image.FromFile(IntegrationConfig.GetFlagsFilePath() + countryCode + IntegrationConfig.GetFlagsFileType()); + image = Image.FromFile(IntegrationConfig.GetFlagsFilePath() + countryCode.ToLower() + IntegrationConfig.GetFlagsFileType()); } - catch (System.IO.IOException e) + catch (Exception e) { _logger.LogObject("Exception caught: {0}", e); + return ""; } return _imageConverter.ImageToString(image); diff --git a/FlightSystem2/Src/Repository/FileSystem/Data/FlightData.cs b/RebusCore/Src/Repository/FileSystem/Data/FlightData.cs similarity index 71% rename from FlightSystem2/Src/Repository/FileSystem/Data/FlightData.cs rename to RebusCore/Src/Repository/FileSystem/Data/FlightData.cs index e410157..dd1d7e3 100644 --- a/FlightSystem2/Src/Repository/FileSystem/Data/FlightData.cs +++ b/RebusCore/Src/Repository/FileSystem/Data/FlightData.cs @@ -1,12 +1,12 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Integration.Config; -using System; +using System; using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Implementations.Entities; +using FlightSystem.Api.Domain.Interfaces; +using FlightSystem.Api.Integration.Config; -namespace FlightSystem.Api.Src.Repository.FileSystem.Data +namespace FlightSystem.Api.Repository.FileSystem.Data { public class FlightData : IFlightData { diff --git a/FlightSystem2/Src/Repository/FileSystem/Data/LocationsData.cs b/RebusCore/Src/Repository/FileSystem/Data/LocationsData.cs similarity index 63% rename from FlightSystem2/Src/Repository/FileSystem/Data/LocationsData.cs rename to RebusCore/Src/Repository/FileSystem/Data/LocationsData.cs index 650e17e..baf7009 100644 --- a/FlightSystem2/Src/Repository/FileSystem/Data/LocationsData.cs +++ b/RebusCore/Src/Repository/FileSystem/Data/LocationsData.cs @@ -1,10 +1,10 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Integration.Config; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Implementations.Entities; +using FlightSystem.Api.Domain.Interfaces; +using FlightSystem.Api.Integration.Config; -namespace FlightSystem.Api.Src.Repository.FileSystem.Data +namespace FlightSystem.Api.Repository.FileSystem.Data { public class LocationsData : ILocationsData { diff --git a/FlightSystem2/Src/Repository/FileSystem/Data/RouteData.cs b/RebusCore/Src/Repository/FileSystem/Data/RouteData.cs similarity index 66% rename from FlightSystem2/Src/Repository/FileSystem/Data/RouteData.cs rename to RebusCore/Src/Repository/FileSystem/Data/RouteData.cs index a7bc160..93b7bd2 100644 --- a/FlightSystem2/Src/Repository/FileSystem/Data/RouteData.cs +++ b/RebusCore/Src/Repository/FileSystem/Data/RouteData.cs @@ -1,11 +1,11 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Integration.Common.Interfaces; -using FlightSystem.Api.Src.Integration.Config; -using System.Collections.Generic; +using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.AServices; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Implementations.Entities; +using FlightSystem.Api.Domain.Interfaces; +using FlightSystem.Api.Integration.Config; -namespace FlightSystem.Api.Src.Repository.FileSystem.Data +namespace FlightSystem.Api.Repository.FileSystem.Data { public class RouteData : IRouteData { diff --git a/FlightSystem2/Src/Repository/Neo4J/Common/FlightParser.cs b/RebusCore/Src/Repository/Neo4J/Common/FlightParser.cs similarity index 96% rename from FlightSystem2/Src/Repository/Neo4J/Common/FlightParser.cs rename to RebusCore/Src/Repository/Neo4J/Common/FlightParser.cs index 10d2cd9..67db7bf 100644 --- a/FlightSystem2/Src/Repository/Neo4J/Common/FlightParser.cs +++ b/RebusCore/Src/Repository/Neo4J/Common/FlightParser.cs @@ -1,8 +1,8 @@ using System; -using Neo4j.Driver.V1; using FlightSystem.Api.Domain.Implementations.Entities; +using Neo4j.Driver.V1; -namespace FlightSystem.Api.Src.Repository.Neo4J.Common +namespace FlightSystem.Api.Repository.Neo4J.Common { public class FlightParser { diff --git a/FlightSystem2/Src/Repository/Neo4J/Common/Neo4JContext.cs b/RebusCore/Src/Repository/Neo4J/Common/Neo4JContext.cs similarity index 86% rename from FlightSystem2/Src/Repository/Neo4J/Common/Neo4JContext.cs rename to RebusCore/Src/Repository/Neo4J/Common/Neo4JContext.cs index bd79732..b98555d 100644 --- a/FlightSystem2/Src/Repository/Neo4J/Common/Neo4JContext.cs +++ b/RebusCore/Src/Repository/Neo4J/Common/Neo4JContext.cs @@ -1,10 +1,8 @@ using System; -using System.Collections.Generic; +using FlightSystem.Api.Integration.Config; using Neo4j.Driver.V1; -using System.Threading.Tasks; -using FlightSystem.Api.Src.Integration.Config; -namespace FlightSystem.Api.Src.Repository.Neo4J.Common +namespace FlightSystem.Api.Repository.Neo4J.Common { public static class Neo4JContext { diff --git a/FlightSystem2/Src/Repository/Neo4J/Data/AirportData.cs b/RebusCore/Src/Repository/Neo4J/Data/AirportData.cs similarity index 85% rename from FlightSystem2/Src/Repository/Neo4J/Data/AirportData.cs rename to RebusCore/Src/Repository/Neo4J/Data/AirportData.cs index b067e1d..4b0a5fd 100644 --- a/FlightSystem2/Src/Repository/Neo4J/Data/AirportData.cs +++ b/RebusCore/Src/Repository/Neo4J/Data/AirportData.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.Data; using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Repository.Neo4J.Common; +using FlightSystem.Api.Repository.Neo4J.Common; using Neo4j.Driver.V1; -namespace FlightSystem.Api.Src.Repository.Neo4J.Data +namespace FlightSystem.Api.Repository.Neo4J.Data { public class AirportData : IAirportData { diff --git a/FlightSystem2/Src/Repository/Neo4J/Data/CountryData.cs b/RebusCore/Src/Repository/Neo4J/Data/CountryData.cs similarity index 84% rename from FlightSystem2/Src/Repository/Neo4J/Data/CountryData.cs rename to RebusCore/Src/Repository/Neo4J/Data/CountryData.cs index 87fa564..6bf1312 100644 --- a/FlightSystem2/Src/Repository/Neo4J/Data/CountryData.cs +++ b/RebusCore/Src/Repository/Neo4J/Data/CountryData.cs @@ -1,10 +1,10 @@ using System.Collections.Generic; +using FlightSystem.Api.Application.Interfaces.Data; using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Repository.Neo4J.Common; +using FlightSystem.Api.Repository.Neo4J.Common; using Neo4j.Driver.V1; -namespace FlightSystem.Api.Src.Repository.Neo4J.Data +namespace FlightSystem.Api.Repository.Neo4J.Data { public class CountryData : ICountryData { diff --git a/FlightSystem2/Src/Repository/Neo4J/Data/FlightData.cs b/RebusCore/Src/Repository/Neo4J/Data/FlightData.cs similarity index 93% rename from FlightSystem2/Src/Repository/Neo4J/Data/FlightData.cs rename to RebusCore/Src/Repository/Neo4J/Data/FlightData.cs index a043968..a00032d 100644 --- a/FlightSystem2/Src/Repository/Neo4J/Data/FlightData.cs +++ b/RebusCore/Src/Repository/Neo4J/Data/FlightData.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; -using Neo4j.Driver.V1; -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Repository.Neo4J.Common; +using FlightSystem.Api.Application.Interfaces.Data; using FlightSystem.Api.Domain.Implementations.Entities; +using FlightSystem.Api.Domain.Interfaces; +using FlightSystem.Api.Repository.Neo4J.Common; +using Neo4j.Driver.V1; -namespace FlightSystem.Api.Src.Repository.Neo4J.Data +namespace FlightSystem.Api.Repository.Neo4J.Data { public class FlightData : IFlightData { diff --git a/FlightSystem2/Src/Repository/Neo4J/Data/LocationsData.cs b/RebusCore/Src/Repository/Neo4J/Data/LocationsData.cs similarity index 61% rename from FlightSystem2/Src/Repository/Neo4J/Data/LocationsData.cs rename to RebusCore/Src/Repository/Neo4J/Data/LocationsData.cs index 0167677..adb31bf 100644 --- a/FlightSystem2/Src/Repository/Neo4J/Data/LocationsData.cs +++ b/RebusCore/Src/Repository/Neo4J/Data/LocationsData.cs @@ -1,8 +1,8 @@ -using FlightSystem.Api.Domain.Implementations.Entities; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Domain.Interfaces; +using FlightSystem.Api.Application.Interfaces.Data; +using FlightSystem.Api.Domain.Implementations.Entities; +using FlightSystem.Api.Domain.Interfaces; -namespace FlightSystem.Api.Src.Repository.Neo4J.Data +namespace FlightSystem.Api.Repository.Neo4J.Data { public class LocationsData : ILocationsData { diff --git a/FlightSystem2/Src/Repository/Neo4J/Data/RouteData.cs b/RebusCore/Src/Repository/Neo4J/Data/RouteData.cs similarity index 97% rename from FlightSystem2/Src/Repository/Neo4J/Data/RouteData.cs rename to RebusCore/Src/Repository/Neo4J/Data/RouteData.cs index e8bb12f..5cef488 100644 --- a/FlightSystem2/Src/Repository/Neo4J/Data/RouteData.cs +++ b/RebusCore/Src/Repository/Neo4J/Data/RouteData.cs @@ -1,11 +1,11 @@ using System.Collections.Generic; -using Neo4j.Driver.V1; -using FlightSystem.Api.Src.Domain.Interfaces; -using FlightSystem.Api.Src.Application.Interfaces.Data; -using FlightSystem.Api.Src.Repository.Neo4J.Common; +using FlightSystem.Api.Application.Interfaces.Data; using FlightSystem.Api.Domain.Implementations.Entities; +using FlightSystem.Api.Domain.Interfaces; +using FlightSystem.Api.Repository.Neo4J.Common; +using Neo4j.Driver.V1; -namespace FlightSystem.Api.Src.Repository.Neo4J.Data +namespace FlightSystem.Api.Repository.Neo4J.Data { public class RouteData : IRouteData { diff --git a/FlightSystem2/appsettings.Development.json b/RebusCore/appsettings.Development.json similarity index 100% rename from FlightSystem2/appsettings.Development.json rename to RebusCore/appsettings.Development.json diff --git a/RebusNeo/Dockerfile b/RebusNeo/Dockerfile new file mode 100644 index 0000000..586d386 --- /dev/null +++ b/RebusNeo/Dockerfile @@ -0,0 +1,24 @@ +#See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. + +FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-buster-slim AS base +WORKDIR /app +ENV ASPNETCORE_URLS=http://+:5002 +EXPOSE 5002 + +FROM mcr.microsoft.com/dotnet/core/sdk:3.1-buster AS build +WORKDIR /src +COPY ["RebusNeo/RebusNeo.csproj", "RebusNeo/"] +RUN dotnet restore "RebusNeo/RebusNeo.csproj" +COPY . . +WORKDIR "/src/RebusNeo" +RUN dotnet build "RebusNeo.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "RebusNeo.csproj" -c Release -o /app/publish + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "RebusNeo.dll"] + +COPY RebusNeo/Files /app/files \ No newline at end of file diff --git a/RebusNeo/Files/test.txt b/RebusNeo/Files/test.txt new file mode 100644 index 0000000..a0aba93 --- /dev/null +++ b/RebusNeo/Files/test.txt @@ -0,0 +1 @@ +OK \ No newline at end of file diff --git a/RebusNeo/Files/test_flight.json b/RebusNeo/Files/test_flight.json new file mode 100644 index 0000000..8114af8 --- /dev/null +++ b/RebusNeo/Files/test_flight.json @@ -0,0 +1,2296 @@ +{ + "Header": { + "ResponseError": { + "ErrorCode": 0, + "ErrorMessage": "" + } + }, + "ResponseBody": { + "Entities": [ + { + "trips": [ + { + "tripParams": { + "origin": "AMS", + "destination": "BRU", + "depDate": "2019-10-1", + "onlyDirect": false + }, + "numOfRoutes": 20, + "routes": [ + { + "price": 25.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123495", + "flightCode": "SN-1461", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T23:30:00", + "price": 25.0 + } + ] + }, + { + "price": 20.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123435", + "flightCode": "SN-1451", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T19:00:00", + "arrives": "2019-10-01T19:30:00", + "price": 20.0 + } + ] + }, + { + "price": 35.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123375", + "flightCode": "SN-1441", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T17:00:00", + "arrives": "2019-10-01T17:30:00", + "price": 35.0 + } + ] + }, + { + "price": 20.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123315", + "flightCode": "SN-1421", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T14:00:00", + "arrives": "2019-10-01T14:30:00", + "price": 20.0 + } + ] + }, + { + "price": 30.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123255", + "flightCode": "SN-1411", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T08:00:00", + "arrives": "2019-10-01T08:30:00", + "price": 30.0 + } + ] + }, + { + "price": 25.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123195", + "flightCode": "SN-1401", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T06:00:00", + "arrives": "2019-10-01T06:30:00", + "price": 25.0 + } + ] + }, + { + "price": 90.0, + "timeSpan": "-11:30:00", + "flights": [ + { + "flightId": "120665", + "flightCode": "AF-1221", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T11:30:00", + "arrives": "2019-10-01T13:30:00", + "price": 40.0 + }, + { + "flightId": "120424", + "flightCode": "AF-1140", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T18:00:00", + "arrives": "2019-10-01T18:50:00", + "price": 30.0 + }, + { + "flightId": "122765", + "flightCode": "SN-1131", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + } + ] + }, + { + "price": 100.0, + "timeSpan": "-11:30:00", + "flights": [ + { + "flightId": "120665", + "flightCode": "AF-1221", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T11:30:00", + "arrives": "2019-10-01T13:30:00", + "price": 40.0 + }, + { + "flightId": "83447", + "flightCode": "BA-1111", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T20:30:00", + "arrives": "2019-10-01T21:20:00", + "price": 40.0 + }, + { + "flightId": "122765", + "flightCode": "SN-1131", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + } + ] + }, + { + "price": 100.0, + "timeSpan": "12:20:00", + "flights": [ + { + "flightId": "120665", + "flightCode": "AF-1221", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T11:30:00", + "arrives": "2019-10-01T13:30:00", + "price": 40.0 + }, + { + "flightId": "116638", + "flightCode": "BA-1831", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "London-Gatwick Airport", + "type": "Airport", + "code": "LGW", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T21:00:00", + "arrives": "2019-10-01T22:00:00", + "price": 40.0 + }, + { + "flightId": "124213", + "flightCode": "SN-11101", + "fromAirport": { + "fullName": "London-Gatwick Airport", + "type": "Airport", + "code": "LGW", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T23:50:00", + "price": 20.0 + } + ] + }, + { + "price": 100.0, + "timeSpan": "-09:30:00", + "flights": [ + { + "flightId": "120605", + "flightCode": "AF-1211", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T09:30:00", + "arrives": "2019-10-01T10:30:00", + "price": 50.0 + }, + { + "flightId": "120424", + "flightCode": "AF-1140", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T18:00:00", + "arrives": "2019-10-01T18:50:00", + "price": 30.0 + }, + { + "flightId": "122765", + "flightCode": "SN-1131", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + } + ] + }, + { + "price": 100.0, + "timeSpan": "-09:30:00", + "flights": [ + { + "flightId": "120605", + "flightCode": "AF-1211", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T09:30:00", + "arrives": "2019-10-01T10:30:00", + "price": 50.0 + }, + { + "flightId": "120304", + "flightCode": "AF-1120", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T12:00:00", + "arrives": "2019-10-01T12:50:00", + "price": 30.0 + }, + { + "flightId": "122765", + "flightCode": "SN-1131", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + } + ] + }, + { + "price": 100.0, + "timeSpan": "-06:30:00", + "flights": [ + { + "flightId": "120545", + "flightCode": "AF-1201", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T06:30:00", + "arrives": "2019-10-01T07:30:00", + "price": 50.0 + }, + { + "flightId": "120424", + "flightCode": "AF-1140", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T18:00:00", + "arrives": "2019-10-01T18:50:00", + "price": 30.0 + }, + { + "flightId": "122765", + "flightCode": "SN-1131", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + } + ] + }, + { + "price": 100.0, + "timeSpan": "-06:30:00", + "flights": [ + { + "flightId": "120545", + "flightCode": "AF-1201", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T06:30:00", + "arrives": "2019-10-01T07:30:00", + "price": 50.0 + }, + { + "flightId": "120304", + "flightCode": "AF-1120", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T12:00:00", + "arrives": "2019-10-01T12:50:00", + "price": 30.0 + }, + { + "flightId": "122765", + "flightCode": "SN-1131", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + } + ] + }, + { + "price": 110.0, + "timeSpan": "-11:30:00", + "flights": [ + { + "flightId": "120665", + "flightCode": "AF-1221", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T11:30:00", + "arrives": "2019-10-01T13:30:00", + "price": 40.0 + }, + { + "flightId": "83387", + "flightCode": "BA-1109", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T17:30:00", + "arrives": "2019-10-01T18:20:00", + "price": 50.0 + }, + { + "flightId": "122765", + "flightCode": "SN-1131", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + } + ] + }, + { + "price": 110.0, + "timeSpan": "12:20:00", + "flights": [ + { + "flightId": "120665", + "flightCode": "AF-1221", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T11:30:00", + "arrives": "2019-10-01T13:30:00", + "price": 40.0 + }, + { + "flightId": "116578", + "flightCode": "BA-1821", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "London-Gatwick Airport", + "type": "Airport", + "code": "LGW", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T15:00:00", + "arrives": "2019-10-01T16:00:00", + "price": 50.0 + }, + { + "flightId": "124213", + "flightCode": "SN-11101", + "fromAirport": { + "fullName": "London-Gatwick Airport", + "type": "Airport", + "code": "LGW", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T23:50:00", + "price": 20.0 + } + ] + }, + { + "price": 110.0, + "timeSpan": "-11:30:00", + "flights": [ + { + "flightId": "120665", + "flightCode": "AF-1221", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T11:30:00", + "arrives": "2019-10-01T13:30:00", + "price": 40.0 + }, + { + "flightId": "120364", + "flightCode": "AF-1130", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T15:00:00", + "arrives": "2019-10-01T15:50:00", + "price": 50.0 + }, + { + "flightId": "122765", + "flightCode": "SN-1131", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + } + ] + }, + { + "price": 110.0, + "timeSpan": "-09:30:00", + "flights": [ + { + "flightId": "120605", + "flightCode": "AF-1211", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T09:30:00", + "arrives": "2019-10-01T10:30:00", + "price": 50.0 + }, + { + "flightId": "83447", + "flightCode": "BA-1111", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T20:30:00", + "arrives": "2019-10-01T21:20:00", + "price": 40.0 + }, + { + "flightId": "122765", + "flightCode": "SN-1131", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + } + ] + }, + { + "price": 110.0, + "timeSpan": "14:20:00", + "flights": [ + { + "flightId": "120605", + "flightCode": "AF-1211", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T09:30:00", + "arrives": "2019-10-01T10:30:00", + "price": 50.0 + }, + { + "flightId": "116638", + "flightCode": "BA-1831", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "London-Gatwick Airport", + "type": "Airport", + "code": "LGW", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T21:00:00", + "arrives": "2019-10-01T22:00:00", + "price": 40.0 + }, + { + "flightId": "124213", + "flightCode": "SN-11101", + "fromAirport": { + "fullName": "London-Gatwick Airport", + "type": "Airport", + "code": "LGW", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T23:50:00", + "price": 20.0 + } + ] + }, + { + "price": 110.0, + "timeSpan": "06:30:00", + "flights": [ + { + "flightId": "120605", + "flightCode": "AF-1211", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T09:30:00", + "arrives": "2019-10-01T10:30:00", + "price": 50.0 + }, + { + "flightId": "120304", + "flightCode": "AF-1120", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T12:00:00", + "arrives": "2019-10-01T12:50:00", + "price": 30.0 + }, + { + "flightId": "122705", + "flightCode": "SN-1121", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T15:00:00", + "arrives": "2019-10-01T16:00:00", + "price": 30.0 + } + ] + }, + { + "price": 110.0, + "timeSpan": "09:30:00", + "flights": [ + { + "flightId": "120545", + "flightCode": "AF-1201", + "fromAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T06:30:00", + "arrives": "2019-10-01T07:30:00", + "price": 50.0 + }, + { + "flightId": "120304", + "flightCode": "AF-1120", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T12:00:00", + "arrives": "2019-10-01T12:50:00", + "price": 30.0 + }, + { + "flightId": "122705", + "flightCode": "SN-1121", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-01T15:00:00", + "arrives": "2019-10-01T16:00:00", + "price": 30.0 + } + ] + } + ] + }, + { + "tripParams": { + "origin": "BRU", + "destination": "AMS", + "depDate": "2019-10-1", + "onlyDirect": false + }, + "numOfRoutes": 20, + "routes": [ + { + "price": 25.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123194", + "flightCode": "SN-1400", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T06:00:00", + "arrives": "2019-10-01T06:30:00", + "price": 25.0 + } + ] + }, + { + "price": 30.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123254", + "flightCode": "SN-1410", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T08:00:00", + "arrives": "2019-10-01T08:30:00", + "price": 30.0 + } + ] + }, + { + "price": 20.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123314", + "flightCode": "SN-1420", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T14:00:00", + "arrives": "2019-10-01T14:30:00", + "price": 20.0 + } + ] + }, + { + "price": 35.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123374", + "flightCode": "SN-1440", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T17:00:00", + "arrives": "2019-10-01T17:30:00", + "price": 35.0 + } + ] + }, + { + "price": 20.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123434", + "flightCode": "SN-1450", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T19:00:00", + "arrives": "2019-10-01T19:30:00", + "price": 20.0 + } + ] + }, + { + "price": 25.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "123494", + "flightCode": "SN-1460", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T23:30:00", + "price": 25.0 + } + ] + }, + { + "price": 70.0, + "timeSpan": "10.13:30:00", + "flights": [ + { + "flightId": "122604", + "flightCode": "SN-11100", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T06:00:00", + "arrives": "2019-10-11T07:00:00", + "price": 30.0 + }, + { + "flightId": "122665", + "flightCode": "SN-11111", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-11T09:00:00", + "arrives": "2019-10-11T10:00:00", + "price": 20.0 + }, + { + "flightId": "123454", + "flightCode": "SN-11450", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-11T19:00:00", + "arrives": "2019-10-11T19:30:00", + "price": 20.0 + } + ] + }, + { + "price": 70.0, + "timeSpan": "10.08:30:00", + "flights": [ + { + "flightId": "122604", + "flightCode": "SN-11100", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T06:00:00", + "arrives": "2019-10-11T07:00:00", + "price": 30.0 + }, + { + "flightId": "122665", + "flightCode": "SN-11111", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-11T09:00:00", + "arrives": "2019-10-11T10:00:00", + "price": 20.0 + }, + { + "flightId": "123334", + "flightCode": "SN-11420", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-11T14:00:00", + "arrives": "2019-10-11T14:30:00", + "price": 20.0 + } + ] + }, + { + "price": 80.0, + "timeSpan": "16:30:00", + "flights": [ + { + "flightId": "123775", + "flightCode": "SN-1700", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Frankfurt Airport", + "type": "Airport", + "code": "FRA", + "cityName": "Frankfurt", + "countryName": "DE" + }, + "departs": "2019-10-01T07:00:00", + "arrives": "2019-10-01T07:50:00", + "price": 20.0 + }, + { + "flightId": "122224", + "flightCode": "LH-1330", + "fromAirport": { + "fullName": "Frankfurt Airport", + "type": "Airport", + "code": "FRA", + "cityName": "Frankfurt", + "countryName": "DE" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T19:30:00", + "arrives": "2019-10-01T20:50:00", + "price": 30.0 + }, + { + "flightId": "120784", + "flightCode": "AF-1240", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T22:30:00", + "arrives": "2019-10-01T23:30:00", + "price": 30.0 + } + ] + }, + { + "price": 80.0, + "timeSpan": "14:30:00", + "flights": [ + { + "flightId": "122644", + "flightCode": "SN-1110", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T09:00:00", + "arrives": "2019-10-01T10:00:00", + "price": 20.0 + }, + { + "flightId": "120425", + "flightCode": "AF-1141", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T18:00:00", + "arrives": "2019-10-01T18:50:00", + "price": 30.0 + }, + { + "flightId": "120784", + "flightCode": "AF-1240", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T22:30:00", + "arrives": "2019-10-01T23:30:00", + "price": 30.0 + } + ] + }, + { + "price": 80.0, + "timeSpan": "14:30:00", + "flights": [ + { + "flightId": "122644", + "flightCode": "SN-1110", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T09:00:00", + "arrives": "2019-10-01T10:00:00", + "price": 20.0 + }, + { + "flightId": "120305", + "flightCode": "AF-1121", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T12:00:00", + "arrives": "2019-10-01T12:50:00", + "price": 30.0 + }, + { + "flightId": "120784", + "flightCode": "AF-1240", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T22:30:00", + "arrives": "2019-10-01T23:30:00", + "price": 30.0 + } + ] + }, + { + "price": 80.0, + "timeSpan": "00:30:00", + "flights": [ + { + "flightId": "122764", + "flightCode": "SN-1130", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T23:00:00", + "arrives": "2019-10-01T00:00:00", + "price": 20.0 + }, + { + "flightId": "120305", + "flightCode": "AF-1121", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T12:00:00", + "arrives": "2019-10-01T12:50:00", + "price": 30.0 + }, + { + "flightId": "120784", + "flightCode": "AF-1240", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T22:30:00", + "arrives": "2019-10-01T23:30:00", + "price": 30.0 + } + ] + }, + { + "price": 80.0, + "timeSpan": "10.13:30:00", + "flights": [ + { + "flightId": "122604", + "flightCode": "SN-11100", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T06:00:00", + "arrives": "2019-10-11T07:00:00", + "price": 30.0 + }, + { + "flightId": "122725", + "flightCode": "SN-11121", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-11T15:00:00", + "arrives": "2019-10-11T16:00:00", + "price": 30.0 + }, + { + "flightId": "123454", + "flightCode": "SN-11450", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-11T19:00:00", + "arrives": "2019-10-11T19:30:00", + "price": 20.0 + } + ] + }, + { + "price": 85.0, + "timeSpan": "10.17:30:00", + "flights": [ + { + "flightId": "122604", + "flightCode": "SN-11100", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T06:00:00", + "arrives": "2019-10-11T07:00:00", + "price": 30.0 + }, + { + "flightId": "122725", + "flightCode": "SN-11121", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-11T15:00:00", + "arrives": "2019-10-11T16:00:00", + "price": 30.0 + }, + { + "flightId": "123514", + "flightCode": "SN-11460", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-11T23:00:00", + "arrives": "2019-10-11T23:30:00", + "price": 25.0 + } + ] + }, + { + "price": 85.0, + "timeSpan": "10.11:30:00", + "flights": [ + { + "flightId": "122604", + "flightCode": "SN-11100", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T06:00:00", + "arrives": "2019-10-11T07:00:00", + "price": 30.0 + }, + { + "flightId": "122665", + "flightCode": "SN-11111", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "departs": "2019-10-11T09:00:00", + "arrives": "2019-10-11T10:00:00", + "price": 20.0 + }, + { + "flightId": "123394", + "flightCode": "SN-11440", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-11T17:00:00", + "arrives": "2019-10-11T17:30:00", + "price": 35.0 + } + ] + }, + { + "price": 90.0, + "timeSpan": "16:30:00", + "flights": [ + { + "flightId": "123775", + "flightCode": "SN-1700", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Frankfurt Airport", + "type": "Airport", + "code": "FRA", + "cityName": "Frankfurt", + "countryName": "DE" + }, + "departs": "2019-10-01T07:00:00", + "arrives": "2019-10-01T07:50:00", + "price": 20.0 + }, + { + "flightId": "122164", + "flightCode": "LH-1320", + "fromAirport": { + "fullName": "Frankfurt Airport", + "type": "Airport", + "code": "FRA", + "cityName": "Frankfurt", + "countryName": "DE" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T15:30:00", + "arrives": "2019-10-01T16:50:00", + "price": 40.0 + }, + { + "flightId": "120784", + "flightCode": "AF-1240", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T22:30:00", + "arrives": "2019-10-01T23:30:00", + "price": 30.0 + } + ] + }, + { + "price": 90.0, + "timeSpan": "17:30:00", + "flights": [ + { + "flightId": "122584", + "flightCode": "SN-1100", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T06:00:00", + "arrives": "2019-10-01T07:00:00", + "price": 30.0 + }, + { + "flightId": "120425", + "flightCode": "AF-1141", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T18:00:00", + "arrives": "2019-10-01T18:50:00", + "price": 30.0 + }, + { + "flightId": "120784", + "flightCode": "AF-1240", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T22:30:00", + "arrives": "2019-10-01T23:30:00", + "price": 30.0 + } + ] + }, + { + "price": 90.0, + "timeSpan": "17:30:00", + "flights": [ + { + "flightId": "122584", + "flightCode": "SN-1100", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T06:00:00", + "arrives": "2019-10-01T07:00:00", + "price": 30.0 + }, + { + "flightId": "120305", + "flightCode": "AF-1121", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T12:00:00", + "arrives": "2019-10-01T12:50:00", + "price": 30.0 + }, + { + "flightId": "120784", + "flightCode": "AF-1240", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T22:30:00", + "arrives": "2019-10-01T23:30:00", + "price": 30.0 + } + ] + }, + { + "price": 90.0, + "timeSpan": "14:30:00", + "flights": [ + { + "flightId": "122644", + "flightCode": "SN-1110", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T09:00:00", + "arrives": "2019-10-01T10:00:00", + "price": 20.0 + }, + { + "flightId": "83446", + "flightCode": "BA-1110", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T20:30:00", + "arrives": "2019-10-01T21:20:00", + "price": 40.0 + }, + { + "flightId": "120784", + "flightCode": "AF-1240", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T22:30:00", + "arrives": "2019-10-01T23:30:00", + "price": 30.0 + } + ] + }, + { + "price": 90.0, + "timeSpan": "11:30:00", + "flights": [ + { + "flightId": "122644", + "flightCode": "SN-1110", + "fromAirport": { + "fullName": "Brussels Airport", + "type": "Airport", + "code": "BRU", + "cityName": "Brussels", + "countryName": "BE" + }, + "toAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "departs": "2019-10-01T09:00:00", + "arrives": "2019-10-01T10:00:00", + "price": 20.0 + }, + { + "flightId": "120305", + "flightCode": "AF-1121", + "fromAirport": { + "fullName": "Heathrow Airport", + "type": "Airport", + "code": "LHR", + "cityName": "London", + "countryName": "GB" + }, + "toAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "departs": "2019-10-01T12:00:00", + "arrives": "2019-10-01T12:50:00", + "price": 30.0 + }, + { + "flightId": "120724", + "flightCode": "AF-1230", + "fromAirport": { + "fullName": "Charles de Gaulle Airport", + "type": "Airport", + "code": "CDG", + "cityName": "Paris", + "countryName": "FR" + }, + "toAirport": { + "fullName": "Amsterdam Airport Schiphol", + "type": "Airport", + "code": "AMS", + "cityName": "Amsterdam", + "countryName": "NL" + }, + "departs": "2019-10-01T19:30:00", + "arrives": "2019-10-01T20:30:00", + "price": 40.0 + } + ] + } + ] + } + ], + "numOfPass": 1, + "passClass": "economy" + } + ] + } +} \ No newline at end of file diff --git a/RebusNeo/Migrations/20200429062057_UserTest.Designer.cs b/RebusNeo/Migrations/20200429062057_UserTest.Designer.cs new file mode 100644 index 0000000..4a7d0cf --- /dev/null +++ b/RebusNeo/Migrations/20200429062057_UserTest.Designer.cs @@ -0,0 +1,43 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200429062057_UserTest")] + partial class UserTest + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("userEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("userLoginName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("UserInfoItems"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200429062057_UserTest.cs b/RebusNeo/Migrations/20200429062057_UserTest.cs new file mode 100644 index 0000000..d50ac3c --- /dev/null +++ b/RebusNeo/Migrations/20200429062057_UserTest.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "UserInfoItems", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + userLoginName = table.Column(nullable: true), + userEmail = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_UserInfoItems", x => x.id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "UserInfoItems"); + } + } +} diff --git a/RebusNeo/Migrations/20200429071302_UserTest1.Designer.cs b/RebusNeo/Migrations/20200429071302_UserTest1.Designer.cs new file mode 100644 index 0000000..68bc462 --- /dev/null +++ b/RebusNeo/Migrations/20200429071302_UserTest1.Designer.cs @@ -0,0 +1,43 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200429071302_UserTest1")] + partial class UserTest1 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("userEmail") + .HasColumnType("nvarchar(max)"); + + b.Property("userLoginName") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200429071302_UserTest1.cs b/RebusNeo/Migrations/20200429071302_UserTest1.cs new file mode 100644 index 0000000..1ef1131 --- /dev/null +++ b/RebusNeo/Migrations/20200429071302_UserTest1.cs @@ -0,0 +1,39 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest1 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_UserInfoItems", + table: "UserInfoItems"); + + migrationBuilder.RenameTable( + name: "UserInfoItems", + newName: "userInfo"); + + migrationBuilder.AddPrimaryKey( + name: "PK_userInfo", + table: "userInfo", + column: "id"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropPrimaryKey( + name: "PK_userInfo", + table: "userInfo"); + + migrationBuilder.RenameTable( + name: "userInfo", + newName: "UserInfoItems"); + + migrationBuilder.AddPrimaryKey( + name: "PK_UserInfoItems", + table: "UserInfoItems", + column: "id"); + } + } +} diff --git a/RebusNeo/Migrations/20200429072852_UserTest2.Designer.cs b/RebusNeo/Migrations/20200429072852_UserTest2.Designer.cs new file mode 100644 index 0000000..ddc0e47 --- /dev/null +++ b/RebusNeo/Migrations/20200429072852_UserTest2.Designer.cs @@ -0,0 +1,49 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200429072852_UserTest2")] + partial class UserTest2 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200429072852_UserTest2.cs b/RebusNeo/Migrations/20200429072852_UserTest2.cs new file mode 100644 index 0000000..3fe1948 --- /dev/null +++ b/RebusNeo/Migrations/20200429072852_UserTest2.cs @@ -0,0 +1,69 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest2 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "userEmail", + table: "userInfo"); + + migrationBuilder.DropColumn( + name: "userLoginName", + table: "userInfo"); + + migrationBuilder.AddColumn( + name: "email", + table: "userInfo", + nullable: true); + + migrationBuilder.AddColumn( + name: "loginName", + table: "userInfo", + nullable: true); + + migrationBuilder.AddColumn( + name: "password", + table: "userInfo", + nullable: true); + + migrationBuilder.AddColumn( + name: "salt", + table: "userInfo", + nullable: true); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "email", + table: "userInfo"); + + migrationBuilder.DropColumn( + name: "loginName", + table: "userInfo"); + + migrationBuilder.DropColumn( + name: "password", + table: "userInfo"); + + migrationBuilder.DropColumn( + name: "salt", + table: "userInfo"); + + migrationBuilder.AddColumn( + name: "userEmail", + table: "userInfo", + type: "nvarchar(max)", + nullable: true); + + migrationBuilder.AddColumn( + name: "userLoginName", + table: "userInfo", + type: "nvarchar(max)", + nullable: true); + } + } +} diff --git a/RebusNeo/Migrations/20200429081544_UserTest3.Designer.cs b/RebusNeo/Migrations/20200429081544_UserTest3.Designer.cs new file mode 100644 index 0000000..7d75632 --- /dev/null +++ b/RebusNeo/Migrations/20200429081544_UserTest3.Designer.cs @@ -0,0 +1,49 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200429081544_UserTest3")] + partial class UserTest3 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200429081544_UserTest3.cs b/RebusNeo/Migrations/20200429081544_UserTest3.cs new file mode 100644 index 0000000..529aea7 --- /dev/null +++ b/RebusNeo/Migrations/20200429081544_UserTest3.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest3 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/RebusNeo/Migrations/20200429085133_UserTest4.Designer.cs b/RebusNeo/Migrations/20200429085133_UserTest4.Designer.cs new file mode 100644 index 0000000..13cf5c2 --- /dev/null +++ b/RebusNeo/Migrations/20200429085133_UserTest4.Designer.cs @@ -0,0 +1,49 @@ +// +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200429085133_UserTest4")] + partial class UserTest4 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200429085133_UserTest4.cs b/RebusNeo/Migrations/20200429085133_UserTest4.cs new file mode 100644 index 0000000..0c513bf --- /dev/null +++ b/RebusNeo/Migrations/20200429085133_UserTest4.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest4 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/RebusNeo/Migrations/20200512193156_UserTest5.Designer.cs b/RebusNeo/Migrations/20200512193156_UserTest5.Designer.cs new file mode 100644 index 0000000..b71aa6e --- /dev/null +++ b/RebusNeo/Migrations/20200512193156_UserTest5.Designer.cs @@ -0,0 +1,71 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200512193156_UserTest5")] + partial class UserTest5 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Token", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("expireDate") + .HasColumnType("datetime2"); + + b.Property("token") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("token"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200512193156_UserTest5.cs b/RebusNeo/Migrations/20200512193156_UserTest5.cs new file mode 100644 index 0000000..40612a3 --- /dev/null +++ b/RebusNeo/Migrations/20200512193156_UserTest5.cs @@ -0,0 +1,32 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest5 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "token", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + token = table.Column(nullable: true), + userid = table.Column(nullable: false), + expireDate = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_token", x => x.id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "token"); + } + } +} diff --git a/RebusNeo/Migrations/20200512193428_UserTest6.Designer.cs b/RebusNeo/Migrations/20200512193428_UserTest6.Designer.cs new file mode 100644 index 0000000..c08149d --- /dev/null +++ b/RebusNeo/Migrations/20200512193428_UserTest6.Designer.cs @@ -0,0 +1,71 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200512193428_UserTest6")] + partial class UserTest6 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Token", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("expireDate") + .HasColumnType("datetime2"); + + b.Property("token") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("token"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200512193428_UserTest6.cs b/RebusNeo/Migrations/20200512193428_UserTest6.cs new file mode 100644 index 0000000..51e0418 --- /dev/null +++ b/RebusNeo/Migrations/20200512193428_UserTest6.cs @@ -0,0 +1,17 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest6 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/RebusNeo/Migrations/20200512211018_UserTest7.Designer.cs b/RebusNeo/Migrations/20200512211018_UserTest7.Designer.cs new file mode 100644 index 0000000..7ac9015 --- /dev/null +++ b/RebusNeo/Migrations/20200512211018_UserTest7.Designer.cs @@ -0,0 +1,107 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200512211018_UserTest7")] + partial class UserTest7 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.PersonalInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("city") + .HasColumnType("nvarchar(max)"); + + b.Property("country") + .HasColumnType("nvarchar(max)"); + + b.Property("firstname") + .HasColumnType("nvarchar(max)"); + + b.Property("house") + .HasColumnType("nvarchar(max)"); + + b.Property("lastname") + .HasColumnType("nvarchar(max)"); + + b.Property("phonenumber") + .HasColumnType("nvarchar(max)"); + + b.Property("street") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("personalInfo"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Token", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("expireDate") + .HasColumnType("datetime2"); + + b.Property("token") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("token"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200512211018_UserTest7.cs b/RebusNeo/Migrations/20200512211018_UserTest7.cs new file mode 100644 index 0000000..6dca83d --- /dev/null +++ b/RebusNeo/Migrations/20200512211018_UserTest7.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest7 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "personalInfo", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + userid = table.Column(nullable: false), + firstname = table.Column(nullable: true), + lastname = table.Column(nullable: true), + phonenumber = table.Column(nullable: true), + country = table.Column(nullable: true), + city = table.Column(nullable: true), + street = table.Column(nullable: true), + house = table.Column(nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_personalInfo", x => x.id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "personalInfo"); + } + } +} diff --git a/RebusNeo/Migrations/20200513094703_UserTesta.Designer.cs b/RebusNeo/Migrations/20200513094703_UserTesta.Designer.cs new file mode 100644 index 0000000..dcd9f44 --- /dev/null +++ b/RebusNeo/Migrations/20200513094703_UserTesta.Designer.cs @@ -0,0 +1,125 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200513094703_UserTesta")] + partial class UserTesta + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Balance", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("balance") + .HasColumnType("decimal(18,2)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("balance"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.PersonalInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("city") + .HasColumnType("nvarchar(max)"); + + b.Property("country") + .HasColumnType("nvarchar(max)"); + + b.Property("firstname") + .HasColumnType("nvarchar(max)"); + + b.Property("house") + .HasColumnType("nvarchar(max)"); + + b.Property("lastname") + .HasColumnType("nvarchar(max)"); + + b.Property("phonenumber") + .HasColumnType("nvarchar(max)"); + + b.Property("street") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("personalInfo"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Token", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("expireDate") + .HasColumnType("datetime2"); + + b.Property("token") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("token"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200513094703_UserTesta.cs b/RebusNeo/Migrations/20200513094703_UserTesta.cs new file mode 100644 index 0000000..61e95ab --- /dev/null +++ b/RebusNeo/Migrations/20200513094703_UserTesta.cs @@ -0,0 +1,30 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTesta : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "balance", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + userid = table.Column(nullable: false), + balance = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_balance", x => x.id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "balance"); + } + } +} diff --git a/RebusNeo/Migrations/20200513114101_UserTest10.Designer.cs b/RebusNeo/Migrations/20200513114101_UserTest10.Designer.cs new file mode 100644 index 0000000..1e9d265 --- /dev/null +++ b/RebusNeo/Migrations/20200513114101_UserTest10.Designer.cs @@ -0,0 +1,167 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200513114101_UserTest10")] + partial class UserTest10 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Balance", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("balance") + .HasColumnType("decimal(18,2)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("balance"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Flight", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("cost") + .HasColumnType("decimal(18,2)"); + + b.Property("flightId") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("flight"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Order", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("cost") + .HasColumnType("int"); + + b.Property("datetime") + .HasColumnType("datetime2"); + + b.Property("details") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("order"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.PersonalInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("city") + .HasColumnType("nvarchar(max)"); + + b.Property("country") + .HasColumnType("nvarchar(max)"); + + b.Property("firstname") + .HasColumnType("nvarchar(max)"); + + b.Property("house") + .HasColumnType("nvarchar(max)"); + + b.Property("lastname") + .HasColumnType("nvarchar(max)"); + + b.Property("phonenumber") + .HasColumnType("nvarchar(max)"); + + b.Property("street") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("personalInfo"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Token", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("expireDate") + .HasColumnType("datetime2"); + + b.Property("token") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("token"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200513114101_UserTest10.cs b/RebusNeo/Migrations/20200513114101_UserTest10.cs new file mode 100644 index 0000000..9c35f10 --- /dev/null +++ b/RebusNeo/Migrations/20200513114101_UserTest10.cs @@ -0,0 +1,50 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest10 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "flight", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + flightId = table.Column(nullable: false), + cost = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_flight", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "order", + columns: table => new + { + id = table.Column(nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + userid = table.Column(nullable: false), + cost = table.Column(nullable: false), + details = table.Column(nullable: true), + datetime = table.Column(nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_order", x => x.id); + }); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "flight"); + + migrationBuilder.DropTable( + name: "order"); + } + } +} diff --git a/RebusNeo/Migrations/20200520081836_UserTest11.Designer.cs b/RebusNeo/Migrations/20200520081836_UserTest11.Designer.cs new file mode 100644 index 0000000..57fe526 --- /dev/null +++ b/RebusNeo/Migrations/20200520081836_UserTest11.Designer.cs @@ -0,0 +1,170 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + [Migration("20200520081836_UserTest11")] + partial class UserTest11 + { + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Balance", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("balance") + .HasColumnType("decimal(18,2)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("balance"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Flight", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("cost") + .HasColumnType("decimal(18,2)"); + + b.Property("flightId") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("flight"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Order", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("cost") + .HasColumnType("decimal(18,2)"); + + b.Property("datetime") + .HasColumnType("datetime2"); + + b.Property("details") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("order"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.PersonalInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("city") + .HasColumnType("nvarchar(max)"); + + b.Property("country") + .HasColumnType("nvarchar(max)"); + + b.Property("firstname") + .HasColumnType("nvarchar(max)"); + + b.Property("house") + .HasColumnType("nvarchar(max)"); + + b.Property("lastname") + .HasColumnType("nvarchar(max)"); + + b.Property("phonenumber") + .HasColumnType("nvarchar(max)"); + + b.Property("street") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("personalInfo"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Token", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("expireDate") + .HasColumnType("datetime2"); + + b.Property("token") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("token"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.Property("status") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Migrations/20200520081836_UserTest11.cs b/RebusNeo/Migrations/20200520081836_UserTest11.cs new file mode 100644 index 0000000..ba57dfd --- /dev/null +++ b/RebusNeo/Migrations/20200520081836_UserTest11.cs @@ -0,0 +1,36 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +namespace RebusNeo.Migrations +{ + public partial class UserTest11 : Migration + { + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "status", + table: "userInfo", + nullable: true); + + migrationBuilder.AlterColumn( + name: "cost", + table: "order", + nullable: false, + oldClrType: typeof(int), + oldType: "int"); + } + + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "status", + table: "userInfo"); + + migrationBuilder.AlterColumn( + name: "cost", + table: "order", + type: "int", + nullable: false, + oldClrType: typeof(decimal)); + } + } +} diff --git a/RebusNeo/Migrations/MSSQLContextModelSnapshot.cs b/RebusNeo/Migrations/MSSQLContextModelSnapshot.cs new file mode 100644 index 0000000..87fa6b4 --- /dev/null +++ b/RebusNeo/Migrations/MSSQLContextModelSnapshot.cs @@ -0,0 +1,168 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Migrations +{ + [DbContext(typeof(MSSQLContext))] + partial class MSSQLContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "3.1.3") + .HasAnnotation("Relational:MaxIdentifierLength", 128) + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Balance", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("balance") + .HasColumnType("decimal(18,2)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("balance"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Flight", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("cost") + .HasColumnType("decimal(18,2)"); + + b.Property("flightId") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("flight"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Order", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("cost") + .HasColumnType("decimal(18,2)"); + + b.Property("datetime") + .HasColumnType("datetime2"); + + b.Property("details") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("order"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.PersonalInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("city") + .HasColumnType("nvarchar(max)"); + + b.Property("country") + .HasColumnType("nvarchar(max)"); + + b.Property("firstname") + .HasColumnType("nvarchar(max)"); + + b.Property("house") + .HasColumnType("nvarchar(max)"); + + b.Property("lastname") + .HasColumnType("nvarchar(max)"); + + b.Property("phonenumber") + .HasColumnType("nvarchar(max)"); + + b.Property("street") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("personalInfo"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.Token", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("expireDate") + .HasColumnType("datetime2"); + + b.Property("token") + .HasColumnType("nvarchar(max)"); + + b.Property("userid") + .HasColumnType("int"); + + b.HasKey("id"); + + b.ToTable("token"); + }); + + modelBuilder.Entity("RebusNeo.Src.Domain.Implementations.UserInfo", b => + { + b.Property("id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); + + b.Property("email") + .HasColumnType("nvarchar(max)"); + + b.Property("loginName") + .HasColumnType("nvarchar(max)"); + + b.Property("password") + .HasColumnType("nvarchar(max)"); + + b.Property("salt") + .HasColumnType("nvarchar(max)"); + + b.Property("status") + .HasColumnType("nvarchar(max)"); + + b.HasKey("id"); + + b.ToTable("userInfo"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/RebusNeo/Properties/launchSettings.json b/RebusNeo/Properties/launchSettings.json new file mode 100644 index 0000000..4525827 --- /dev/null +++ b/RebusNeo/Properties/launchSettings.json @@ -0,0 +1,36 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51253", + "sslPort": 0 + } + }, + "$schema": "http://json.schemastore.org/launchsettings.json", + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "test", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "RebusNeo": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "testConn", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:5000" + }, + "Docker": { + "commandName": "Docker", + "launchBrowser": true, + "launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/testConn", + "publishAllPorts": true + } + } +} \ No newline at end of file diff --git a/RebusNeo/RebusNeo.csproj b/RebusNeo/RebusNeo.csproj new file mode 100644 index 0000000..572f9d2 --- /dev/null +++ b/RebusNeo/RebusNeo.csproj @@ -0,0 +1,45 @@ + + + + netcoreapp3.1 + Linux + + + + + + + + + + + + + + + + + + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + + + + + + + diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/AFlightManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/AFlightManager.cs new file mode 100644 index 0000000..cecd60d --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/AFlightManager.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class AFlightManager : AManager + { + public abstract string GetFlight(ulong flightId); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/AJourneyManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/AJourneyManager.cs new file mode 100644 index 0000000..599f781 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/AJourneyManager.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Domain.Implementations; + + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class AJourneyManager : AManager + { + public abstract string GetJourney(bool isOneWay, + string origin, + string destination, + string depDate, + string retDate, + bool onlyDirect, + int numOfPass, + string passClass); + + public abstract string OrderJourney(string pToken, int pUserId, string pListOfFlights); + + public abstract string GetOrderedFlights(string pToken, int pUserId); + + public abstract List GetOrderedFlights(int pUserId); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/ALocationManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/ALocationManager.cs new file mode 100644 index 0000000..cceb04c --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/ALocationManager.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class ALocationtManager : AManager + { + public abstract string GetAllLocations(); + + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/ALoginManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/ALoginManager.cs new file mode 100644 index 0000000..e16ba18 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/ALoginManager.cs @@ -0,0 +1,17 @@ +using System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class ALoginManager : AManager + { + public abstract string Login(string username, string password); + + public abstract string LoginChangePass(string username, string password, string newPassword); + + public abstract string LogOut(string pToken, int pUserId); + + public abstract string BanUser(string username, string action); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/AManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/AManager.cs new file mode 100644 index 0000000..e679b0e --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/AManager.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.IServices; +using RebusNeo.Src.Application.Interfaces.Factories; +using RebusNeo.Src.Domain.Interfaces; +using RebusNeo.Src.Domain.Implementations; +using RebusNeo.Src.Integration.Common.Services.Converters; +using RebusNeo.Src.Integration.Common.Services.Response; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class AManager + { + private ISerializer _serializer; + public IResponseFactory responseFactory; + public IEntityFactory entityFactory; + public MSSQLContext context; + + public AManager() + { + _serializer = new JSONSerializer(); + responseFactory = new JSONResponseFactory(_serializer); + entityFactory = new EntityFactory(); + } + + public void SetDbContext(MSSQLContext pContext) + { + context = pContext; + } + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/APersonalBalanceManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/APersonalBalanceManager.cs new file mode 100644 index 0000000..b4f3a8f --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/APersonalBalanceManager.cs @@ -0,0 +1,17 @@ +using System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class APersonalBalanceManager : AManager + { + public abstract string AddPersonalBalance(string pToken, int pUserId, string pAddBal); + + public abstract string GetPersonalBalance(string pToken, int pUserId); + + public abstract decimal GetPersonalBalance(int pUserId); + + public abstract void UpdatePersonalBalance(int pUserId, decimal pChange); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/APersonalInfoManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/APersonalInfoManager.cs new file mode 100644 index 0000000..65e08ef --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/APersonalInfoManager.cs @@ -0,0 +1,13 @@ +using System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class APersonalInfoManager : AManager + { + public abstract string SetPersonalInfo(string pToken, int pUserId, string pFirstName, string pLastName, string pPhonenumber, string pCountry, string pCity, string pStreet, string pHouse); + + public abstract string GetPersonalInfo(string pToken, int pUserId); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/ARegistrationManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/ARegistrationManager.cs new file mode 100644 index 0000000..8889cfe --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/ARegistrationManager.cs @@ -0,0 +1,21 @@ +using System; +using RebusNeo.Src.Repository.MSSQL.Common; +using System.Collections.Generic; +using RebusNeo.Src.Domain.Implementations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class ARegistrationManager : AManager + { + public abstract string Register(string username, string email, string password); + + public abstract void passDb(MSSQLContext context); + public abstract ActionResult> get(); + + public abstract string Error(string pMsg); + public abstract string Ok(string username, string email); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/AReportManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/AReportManager.cs new file mode 100644 index 0000000..28655f5 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/AReportManager.cs @@ -0,0 +1,11 @@ +using System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class AReportManager : AManager + { + public abstract string GetReport(); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/ATestConnManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/ATestConnManager.cs new file mode 100644 index 0000000..442fac9 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/ATestConnManager.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class ATestConnManager : AManager + { + public abstract void TestConn(); + public abstract string GetResults(); + + } +} diff --git a/RebusNeo/Src/Application/Interfaces/AManagers/ATestFlightManager.cs b/RebusNeo/Src/Application/Interfaces/AManagers/ATestFlightManager.cs new file mode 100644 index 0000000..c677e18 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/AManagers/ATestFlightManager.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Application.Interfaces.AManagers +{ + public abstract class ATestFlightManager : AManager + { + public abstract string GetTestFligth(); + + } +} diff --git a/RebusNeo/Src/Application/Interfaces/Factories/IResponseFactory.cs b/RebusNeo/Src/Application/Interfaces/Factories/IResponseFactory.cs new file mode 100644 index 0000000..ffc6b71 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/Factories/IResponseFactory.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using RebusNeo.Src.Application.Interfaces.IServices; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Application.Interfaces.Factories +{ + public interface IResponseFactory + { + public string CreateResponse(int errorCode, string errorMessage, IEnumerable entities, string token); + + public IResponseHeader CreateResponseHeader(IResponseError error, IResponseToken token); + public IResponseBody CreateResponseBody(IEnumerable entities); + + public IResponseError CreaterResponseError(int errorCode, string errorMessage); + + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IData/IDirectCallJourneyData.cs b/RebusNeo/Src/Application/Interfaces/IData/IDirectCallJourneyData.cs new file mode 100644 index 0000000..d028c45 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IData/IDirectCallJourneyData.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Application.Interfaces.IData +{ + public interface IDirectCallJourneyData + { + string GetJourney(string parameters); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IData/IFlightData.cs b/RebusNeo/Src/Application/Interfaces/IData/IFlightData.cs new file mode 100644 index 0000000..1a42be3 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IData/IFlightData.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Application.Interfaces.IData +{ + public interface IFlightData + { + string GetFlight(ulong flightId); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IData/ILocationData.cs b/RebusNeo/Src/Application/Interfaces/IData/ILocationData.cs new file mode 100644 index 0000000..b1c5c6d --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IData/ILocationData.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Application.Interfaces.IData +{ + public interface ILocationData + { + string GetAllLocationResults(); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IData/ITestConnData.cs b/RebusNeo/Src/Application/Interfaces/IData/ITestConnData.cs new file mode 100644 index 0000000..4b37db6 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IData/ITestConnData.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Application.Interfaces.IData +{ + public interface ITestConnData + { + string GetTestConnResults(); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IData/ITestFlightData.cs b/RebusNeo/Src/Application/Interfaces/IData/ITestFlightData.cs new file mode 100644 index 0000000..ad97b0a --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IData/ITestFlightData.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Application.Interfaces.IData +{ + public interface ITestFlightData + { + string GetTestFlightResults(); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IServices/IDateConverter.cs b/RebusNeo/Src/Application/Interfaces/IServices/IDateConverter.cs new file mode 100644 index 0000000..0076633 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IServices/IDateConverter.cs @@ -0,0 +1,12 @@ +using System; + +namespace RebusNeo.Src.Application.Interfaces.IServices +{ + public interface IDateConverter + { + public DateTime ConvertDateFromString(string date); + public string ConvertDateToString(DateTime date); + + public string TryToConvert(string date); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IServices/ILogger.cs b/RebusNeo/Src/Application/Interfaces/IServices/ILogger.cs new file mode 100644 index 0000000..d4ee6fd --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IServices/ILogger.cs @@ -0,0 +1,8 @@ +namespace RebusNeo.Src.Application.Interfaces.IServices +{ + public interface ILogger + { + public void LogObject(string message, object obj); + public void LogMessage(string message); + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IServices/IResponse.cs b/RebusNeo/Src/Application/Interfaces/IServices/IResponse.cs new file mode 100644 index 0000000..85f8d56 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IServices/IResponse.cs @@ -0,0 +1,8 @@ +namespace RebusNeo.Src.Application.Interfaces.IServices +{ + public interface IResponse + { + public IResponseHeader Header { get;} + public IResponseBody ResponseBody { get;} + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IServices/IResponseBody.cs b/RebusNeo/Src/Application/Interfaces/IServices/IResponseBody.cs new file mode 100644 index 0000000..53bedaf --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IServices/IResponseBody.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Application.Interfaces.IServices +{ + public interface IResponseBody + { + IEnumerable Entities { get; } + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IServices/IResponseError.cs b/RebusNeo/Src/Application/Interfaces/IServices/IResponseError.cs new file mode 100644 index 0000000..d32ed79 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IServices/IResponseError.cs @@ -0,0 +1,7 @@ +namespace RebusNeo.Src.Application.Interfaces.IServices +{ + public interface IResponseError + { + public string ErrorMessage { get; set; } + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IServices/IResponseHeader.cs b/RebusNeo/Src/Application/Interfaces/IServices/IResponseHeader.cs new file mode 100644 index 0000000..69b7391 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IServices/IResponseHeader.cs @@ -0,0 +1,7 @@ +namespace RebusNeo.Src.Application.Interfaces.IServices +{ + public interface IResponseHeader + { + public IResponseError ResponseError { get;} + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IServices/IResponseToken.cs b/RebusNeo/Src/Application/Interfaces/IServices/IResponseToken.cs new file mode 100644 index 0000000..f6997a9 --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IServices/IResponseToken.cs @@ -0,0 +1,7 @@ +namespace RebusNeo.Src.Application.Interfaces.IServices +{ + public interface IResponseToken + { + public string Token { get; set; } + } +} diff --git a/RebusNeo/Src/Application/Interfaces/IServices/ISerializer.cs b/RebusNeo/Src/Application/Interfaces/IServices/ISerializer.cs new file mode 100644 index 0000000..cdfa7fc --- /dev/null +++ b/RebusNeo/Src/Application/Interfaces/IServices/ISerializer.cs @@ -0,0 +1,8 @@ +namespace RebusNeo.Src.Application.Interfaces.IServices +{ + public interface ISerializer + { + public string Serialize(object response); + public T Deserialize(string _object); + } +} diff --git a/RebusNeo/Src/Application/Logic/Journey/FlightManager.cs b/RebusNeo/Src/Application/Logic/Journey/FlightManager.cs new file mode 100644 index 0000000..793070c --- /dev/null +++ b/RebusNeo/Src/Application/Logic/Journey/FlightManager.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Data; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.RebusCore.Data; +using RebusNeo.Src.Domain.Interfaces; +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Application.Logic.Journey +{ + public class FlightManager : AFlightManager + { + private IFlightData _journeyData = new FlightData(); + + public override string GetFlight(ulong flightId) + { + return _journeyData.GetFlight(flightId); + } + } +} diff --git a/RebusNeo/Src/Application/Logic/Journey/JourneyManager.cs b/RebusNeo/Src/Application/Logic/Journey/JourneyManager.cs new file mode 100644 index 0000000..6f4939d --- /dev/null +++ b/RebusNeo/Src/Application/Logic/Journey/JourneyManager.cs @@ -0,0 +1,173 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Data; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.RebusCore.Data; +using RebusNeo.Src.Domain.Interfaces; +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Application.Logic.Journey +{ + public class JourneyManager : AJourneyManager + { + private IDirectCallJourneyData _journeyData = new DirectCallJourneyData(); + private TokenManager _tokenManager = new TokenManager(); + + private Order _order; + + PersonalBalanceManager personalBalanceManager = new PersonalBalanceManager(); + + decimal totalCost = 0; + string priceStringInJSON = "\"price\":"; + + public override string GetJourney(bool isOneWay, + string origin, + string destination, + string depDate, + string retDate, + bool onlyDirect, + int numOfPass, + string passClass) + { + return _journeyData.GetJourney("?isOneWay=" + isOneWay + "&origin=" + origin + + "&destination=" + destination + "&depDate=" + depDate + + "&retDate=" + retDate + "&onlyDirect=" + onlyDirect + + "&numOfPass=" + numOfPass + "&passClass=" + passClass); + } + + public override string OrderJourney(string pToken, int pUserId, string pListOfFlights) + { + personalBalanceManager.SetDbContext(context); + + _tokenManager.SetDbContext(context); + + if (!_tokenManager.IsTokenValid(pToken, pUserId)) + return CreateErrorResp(999, String.Format("{0}", "Session ended!")); + + if (!IsValidAmount(totalCost, pToken, pUserId)) + return CreateErrorResp(990, String.Format("{0}", "Not enough money in account!")); + + if (!IsFlightsValid(pListOfFlights)) + return CreateErrorResp(991, String.Format("{0}", "Wrong filghts!")); + + _order = new Order(); + _order.userid = pUserId; + _order.details = pListOfFlights; + _order.datetime = DateTime.UtcNow; + _order.cost = totalCost; + + using (var tran = context.Database.BeginTransaction()) { + try { + context.Add(_order); + personalBalanceManager.UpdatePersonalBalance(pUserId, -_order.cost); + + tran.Commit(); + } catch { + tran.Rollback(); + return CreateErrorResp(1000, String.Format("{0}", "System error!")); + } + } + + return CreateOkResp(); + } + + public override string GetOrderedFlights(string pToken, int pUserId) + { + _tokenManager.SetDbContext(context); + + if (!_tokenManager.IsTokenValid(pToken, pUserId)) + return CreateErrorResp(999, String.Format("{0}", "Session ended!")); + + var orders = context.order.Where(o => o.userid == pUserId).Select(o => o.details); + + List entities = entityFactory.CreateEntities(); + + foreach (var order in orders) + { + OrderResp orderResp = new OrderResp(); + string [] rawFlights = order.Split(","); + + orderResp.flights = new List(rawFlights); + + entities.Add(orderResp); + } + + _tokenManager.SetDbContext(context); + _tokenManager.GenerateTokenFor(pUserId); + + return responseFactory.CreateResponse(0, "", entities, _tokenManager.GetToken()); + } + + public override List GetOrderedFlights(int pUserId) + { + var orders = context.order.Where(o => o.userid == pUserId).Select(o => o.details); + List ordersList = new List(); + + List entities = entityFactory.CreateEntities(); + + foreach (var order in orders) + { + OrderResp orderResp = new OrderResp(); + string [] rawFlights = order.Split(","); + + orderResp.flights = new List(rawFlights); + + ordersList.Add(orderResp); + } + + return ordersList; + } + + private string CreateErrorResp(int code, string pMsg) + { + List entities = entityFactory.CreateEntities(); + return responseFactory.CreateResponse(code, pMsg, entities, ""); + } + + private string CreateOkResp() + { + _tokenManager.SetDbContext(context); + _tokenManager.GenerateTokenFor(_order.userid); + + List entities = entityFactory.CreateEntities(); + entities.Add(_order); + return responseFactory.CreateResponse(0, "", entities, _tokenManager.GetToken()); + } + + private bool IsValidAmount(decimal pCost, string pToken, int pUserId) + { + if (personalBalanceManager.GetPersonalBalance(pUserId) >= pCost) + return true; + return false; + } + + private bool IsFlightsValid(string pListOfFlights) + { + try{ + FlightManager flightManager = new FlightManager(); + + List flightIds = new List(pListOfFlights.Split(",")); + foreach (var flightid in flightIds) + { + var flight = flightManager.GetFlight(Convert.ToUInt64(flightid)); + if(!flight.Contains("\"ErrorCode\":0,")) + { + return false; + } + + var str = flight.Substring(flight.IndexOf(priceStringInJSON) + priceStringInJSON.Length); + totalCost += Convert.ToDecimal(str.Substring(0, str.IndexOf("}"))); + } + return true; + } + catch(Exception){ + return false; + } + + } + } +} diff --git a/RebusNeo/Src/Application/Logic/Location/LocationManager.cs b/RebusNeo/Src/Application/Logic/Location/LocationManager.cs new file mode 100644 index 0000000..4544724 --- /dev/null +++ b/RebusNeo/Src/Application/Logic/Location/LocationManager.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Repository.RebusCore.Data; + +namespace RebusNeo.Src.Application.Logic.Location +{ + public class LocationManager : ALocationtManager + { + private ILocationData _locationData = new LocationData(); + + public override string GetAllLocations() + { + return _locationData.GetAllLocationResults(); + } + } +} diff --git a/RebusNeo/Src/Application/Logic/System/LoginManager.cs b/RebusNeo/Src/Application/Logic/System/LoginManager.cs new file mode 100644 index 0000000..27756b6 --- /dev/null +++ b/RebusNeo/Src/Application/Logic/System/LoginManager.cs @@ -0,0 +1,212 @@ +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Domain.Interfaces; +using System.Collections.Generic; +using System.Security.Cryptography; +using System; +using System.Linq; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace RebusNeo.Src.Application.Logic.System +{ + public class LoginManager : ALoginManager + { + private UserInfo _userInfo; + private TokenManager _tokenManager = new TokenManager(); + + private int _userId; + + public override string Login(string username, string password) + { + try + { + string userAuthRes = UserAuthentification(username, password); + + if (userAuthRes == "") + return CreateOkResp(); + else return CreateErrorResp(String.Format("{0}", userAuthRes)); + } + + catch (Exception ex) + { + return CreateErrorResp(String.Format("Exception caught: {0}", ex)); + } + } + + public override string LoginChangePass(string username, string password, string newPassword){ + try + { + string userAuthRes = UserAuthentification(username, password); + + if (userAuthRes != "") + return CreateErrorResp(String.Format("{0}", userAuthRes)); + + if (!IsPasswordValid(newPassword)) + return CreateErrorResp(String.Format("{0}", "New password is not vaild!")); + + return ChangePassword(newPassword); + } + + catch (Exception ex) + { + return CreateErrorResp(String.Format("Exception caught: {0}", ex)); + } + } + + public override string LogOut(string pToken, int pUserId) + { + try + { + _tokenManager.SetDbContext(context); + + if (!_tokenManager.IsTokenValid(pToken, pUserId)) + return CreateErrorResp(String.Format(String.Format("{0}", "Session ended!"))); + + _tokenManager.DeleteToken(pToken, pUserId); + + List entities = entityFactory.CreateEntities(); + return responseFactory.CreateResponse(0, "", entities, ""); + } + + catch (Exception ex) + { + return CreateErrorResp(String.Format("Exception caught: {0}", ex)); + } + } + + public override string BanUser(string username, string action) + { + try{ + _userInfo = context.userInfo.First(o => o.loginName == username); + + switch (action) + { + case "ban": + _tokenManager.SetDbContext(context); + _tokenManager.DeleteToken(_userInfo.id); + Ban(); + break; + case "activate": + Activate(); + break; + case "killsession": + _tokenManager.SetDbContext(context); + _tokenManager.DeleteToken(_userInfo.id); + break; + } + + context.userInfo.Attach(_userInfo); + context.SaveChanges(); + + } + catch(Exception ex) + { + return String.Format("Exception caught: {0}", ex); + } + return "Action completed successfully"; + } + + private void Ban() + { + _userInfo.status = "BANED"; + } + + private void Activate() + { + _userInfo.status = "ACTIVE"; + } + + private string UserAuthentification(string username, string password){ + string savedPasswordHash; + + try{ + _userInfo = context.userInfo.First(o => o.loginName == username); + savedPasswordHash = _userInfo.password; + + if (_userInfo.status != "ACTIVE") + return "Access denied!"; + + /* Extract the bytes */ + byte[] hashBytes = Convert.FromBase64String(savedPasswordHash); + /* Get the salt */ + byte[] salt = new byte[16]; + Array.Copy(hashBytes, 0, salt, 0, 16); + /* Compute the hash on the password the user entered */ + var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000); + byte[] hash = pbkdf2.GetBytes(20); + /* Compare the results */ + for (int i=0; i < 20; i++){ + if (hashBytes[i+16] != hash[i]){ + return "Incorrect Username or Password!"; + } + } + } + + catch (Exception) + { + return "Incorrect Username or Password."; + } + + return ""; + } + + private string CreateOkResp() + { + _tokenManager.SetDbContext(context); + _tokenManager.GenerateTokenFor(_userInfo.id); + + _userInfo.password = ""; + _userInfo.salt = ""; + + List entities = entityFactory.CreateEntities(); + entities.Add(_userInfo); + return responseFactory.CreateResponse(0, "", entities, _tokenManager.GetToken()); + } + + private string CreateErrorResp(string pMsg) + { + List entities = entityFactory.CreateEntities(); + return responseFactory.CreateResponse(1, pMsg, entities, ""); + } + + private string ChangePassword(string password) + { + string hashedPass = HashPass(password); + + if (hashedPass == _userInfo.password) + return CreateErrorResp(String.Format("{0}", "New password is same as old!")); + + _userInfo.password = hashedPass; + + context.userInfo.Attach(_userInfo); + context.Entry(_userInfo).Property(x => x.password).IsModified = true; + context.SaveChanges(); + + return CreateOkResp(); + } + + private bool IsPasswordValid(string password) + { + if (password.Length < 8) + return false; + return true; + } + + private string HashPass(string password) + { + byte[] salt; + new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]); + + var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000); + byte[] hash = pbkdf2.GetBytes(20); + + byte[] hashBytes = new byte[36]; + Array.Copy(salt, 0, hashBytes, 0, 16); + Array.Copy(hash, 0, hashBytes, 16, 20); + + return Convert.ToBase64String(hashBytes); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Application/Logic/System/PersonalBalanceManager.cs b/RebusNeo/Src/Application/Logic/System/PersonalBalanceManager.cs new file mode 100644 index 0000000..cab6da6 --- /dev/null +++ b/RebusNeo/Src/Application/Logic/System/PersonalBalanceManager.cs @@ -0,0 +1,107 @@ +using System; +using RebusNeo.Src.Domain.Interfaces; +using RebusNeo.Src.Domain.Implementations; +using RebusNeo.Src.Application.Interfaces.AManagers; +using System.Collections.Generic; +using System.Linq; + +namespace RebusNeo.Src.Application.Logic.System +{ + public class PersonalBalanceManager : APersonalBalanceManager + { + private TokenManager _tokenManager = new TokenManager(); + private Balance balance; + + public override string AddPersonalBalance(string pToken, int pUserId, string pAddBal){ + _tokenManager.SetDbContext(context); + + if (!_tokenManager.IsTokenValid(pToken, pUserId)) + return CreateErrorResp(String.Format(String.Format("{0}", "Session ended!"))); + + balance = context.balance.FirstOrDefault(o => o.userid == pUserId); + + if (balance == null) + { + balance = new Balance(); + balance.userid = pUserId; + + context.Add(balance); + } + + try{ + balance.balance += Convert.ToDecimal(pAddBal); + if (balance.balance < 0) + return CreateErrorResp(String.Format(String.Format("{0}", "Negative amount!"))); + } + catch{} + + context.SaveChanges(); + + return CreateOkResp(); + } + + public override string GetPersonalBalance(string pToken, int pUserId){ + _tokenManager.SetDbContext(context); + + if (!_tokenManager.IsTokenValid(pToken, pUserId)) + return CreateErrorResp(String.Format(String.Format("{0}", "Session ended!"))); + + balance = context.balance.FirstOrDefault(o => o.userid == pUserId); + + if (balance == null) + { + balance = new Balance(); + balance.userid = pUserId; + balance.balance = 0; + } + + return CreateOkResp(); + } + + public override decimal GetPersonalBalance(int pUserId) + { + balance = context.balance.FirstOrDefault(o => o.userid == pUserId); + + if (balance == null) + return 0; + return balance.balance; + } + + public override void UpdatePersonalBalance(int pUserId, decimal pChange) + { + balance = context.balance.FirstOrDefault(o => o.userid == pUserId); + + if (balance == null) + { + balance = new Balance(); + balance.userid = pUserId; + + context.Add(balance); + } + try{ + balance.balance += Convert.ToDecimal(pChange); + if (balance.balance < 0) + return; + } + catch{} + + context.SaveChanges(); + } + + private string CreateOkResp() + { + _tokenManager.SetDbContext(context); + _tokenManager.GenerateTokenFor(balance.userid); + + List entities = entityFactory.CreateEntities(); + entities.Add(balance); + return responseFactory.CreateResponse(0, "", entities, _tokenManager.GetToken()); + } + + private string CreateErrorResp(string pMsg) + { + List entities = entityFactory.CreateEntities(); + return responseFactory.CreateResponse(999, pMsg, entities, ""); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Application/Logic/System/PersonalInfoManager.cs b/RebusNeo/Src/Application/Logic/System/PersonalInfoManager.cs new file mode 100644 index 0000000..3bd3f49 --- /dev/null +++ b/RebusNeo/Src/Application/Logic/System/PersonalInfoManager.cs @@ -0,0 +1,94 @@ +using System; +using RebusNeo.Src.Domain.Interfaces; +using RebusNeo.Src.Domain.Implementations; +using RebusNeo.Src.Application.Interfaces.AManagers; +using System.Collections.Generic; +using System.Linq; + +namespace RebusNeo.Src.Application.Logic.System +{ + public class PersonalInfoManager : APersonalInfoManager + { + private TokenManager _tokenManager = new TokenManager(); + private PersonalInfo _personalInfo; + + public override string SetPersonalInfo(string pToken, int pUserId, string pFirstName, string pLastName, string pPhonenumber, string pCountry, string pCity, string pStreet, string pHouse) + { + pToken.Trim(); + + _tokenManager.SetDbContext(context); + + if (!_tokenManager.IsTokenValid(pToken, pUserId)) + return CreateErrorResp(String.Format(String.Format("{0}", "Session ended!"))); + + _personalInfo = context.personalInfo.FirstOrDefault(o => o.userid == pUserId); + + if (_personalInfo == null) + { + _personalInfo = new PersonalInfo(); + _personalInfo.userid = pUserId; + + context.Add(_personalInfo); + } + + if (!(pFirstName == null)) + _personalInfo.firstname = pFirstName; + + if (!(pLastName == null)) + _personalInfo.lastname = pLastName; + + if (!(pPhonenumber == null)) + _personalInfo.phonenumber = pPhonenumber; + + if (!(pCountry == null)) + _personalInfo.country = pCountry; + + if (!(pCity == null)) + _personalInfo.city = pCity; + + if (!(pStreet == null)) + _personalInfo.street = pStreet; + + if (!(pHouse == null)) + _personalInfo.house = pHouse; + + context.SaveChanges(); + + return CreateOkResp(); + } + + public override string GetPersonalInfo(string pToken, int pUserId) + { + _tokenManager.SetDbContext(context); + + if (!_tokenManager.IsTokenValid(pToken, pUserId)) + return CreateErrorResp(String.Format(String.Format("{0}", "Session ended!"))); + + _personalInfo = context.personalInfo.FirstOrDefault(o => o.userid == pUserId); + + if (_personalInfo is null) + { + _personalInfo = new PersonalInfo(); + _personalInfo.userid = pUserId; + } + + return CreateOkResp(); + } + + private string CreateOkResp() + { + _tokenManager.SetDbContext(context); + _tokenManager.GenerateTokenFor(_personalInfo.userid); + + List entities = entityFactory.CreateEntities(); + entities.Add(_personalInfo); + return responseFactory.CreateResponse(0, "", entities, _tokenManager.GetToken()); + } + + private string CreateErrorResp(string pMsg) + { + List entities = entityFactory.CreateEntities(); + return responseFactory.CreateResponse(999, pMsg, entities, ""); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Application/Logic/System/RegistrationManager.cs b/RebusNeo/Src/Application/Logic/System/RegistrationManager.cs new file mode 100644 index 0000000..d5f5952 --- /dev/null +++ b/RebusNeo/Src/Application/Logic/System/RegistrationManager.cs @@ -0,0 +1,52 @@ +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Domain.Interfaces; +using System.Collections.Generic; +using System.Security.Cryptography; +using System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; + +namespace RebusNeo.Src.Application.Logic.System +{ + public class RegistrationManager : ARegistrationManager + { + private IUserRegistrationInfo _userLoginInfo; + private IUserInfo _userInfo; + + private MSSQLContext _context; + public override string Register(string username, string email,string password) + { + _userInfo = entityFactory.CreateUserInfo("aaa", "ccc@ccc.ccc"); + List entities = entityFactory.CreateEntities(); + entities.Add(_userInfo); + return responseFactory.CreateResponse(1, "", entities, ""); + } + + public override string Error(string pMsg) + { + List entities = entityFactory.CreateEntities(); + return responseFactory.CreateResponse(1, pMsg, entities, ""); + } + + public override string Ok(string username, string email) + { + _userInfo = entityFactory.CreateUserInfo(username, email); + List entities = entityFactory.CreateEntities(); + entities.Add(_userInfo); + return responseFactory.CreateResponse(0, "", entities, ""); + } + + public override void passDb(MSSQLContext context) + { + _context = context; + } + + public override ActionResult> get() + { + return _context.userInfo; + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Application/Logic/System/ReportManager.cs b/RebusNeo/Src/Application/Logic/System/ReportManager.cs new file mode 100644 index 0000000..2dad3e6 --- /dev/null +++ b/RebusNeo/Src/Application/Logic/System/ReportManager.cs @@ -0,0 +1,52 @@ +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Domain.Interfaces; +using System.Collections.Generic; +using System.Security.Cryptography; +using System; +using System.Linq; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using RebusNeo.Src.Application.Logic.Journey; + +namespace RebusNeo.Src.Application.Logic.System +{ + public class ReportManager : AReportManager + { + Report report = new Report(); + ReportUsers reportUsers = new ReportUsers(); + + List usersInfo = new List(); + + JourneyManager journeyManager = new JourneyManager(); + + public override string GetReport() + { + reportUsers.reportUsers = new List(); + + foreach(var user in context.userInfo.ToList()) { + TokenManager tokenManager = new TokenManager(); + tokenManager.SetDbContext(context); + journeyManager.SetDbContext(context); + + ReportUser repUser = new ReportUser(); + repUser.id = user.id; + repUser.username = user.loginName; + repUser.status = user.status; + repUser.session = tokenManager.HasValidToken(user.id); + repUser.lastlogin = tokenManager.GetTokenExpDate(user.id); + repUser.orders = journeyManager.GetOrderedFlights(user.id); + + + reportUsers.reportUsers.Add(repUser); + } + + report.reportUsers = reportUsers; + + List entities = entityFactory.CreateEntities(); + entities.Add(report); + return responseFactory.CreateResponse(0, "", entities, ""); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Application/Logic/System/TokenGenerator.cs b/RebusNeo/Src/Application/Logic/System/TokenGenerator.cs new file mode 100644 index 0000000..d5c104a --- /dev/null +++ b/RebusNeo/Src/Application/Logic/System/TokenGenerator.cs @@ -0,0 +1,25 @@ +using System.Security.Cryptography; +using System.Text; + +namespace RebusNeo.Src.Application.Logic.System +{ + public class TokenGenerator + { + public string Generate(int size) + { + var charSet = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789"; + var chars = charSet.ToCharArray(); + var data = new byte[1]; + var crypto = new RNGCryptoServiceProvider(); + crypto.GetNonZeroBytes(data); + data = new byte[size]; + crypto.GetNonZeroBytes(data); + var result = new StringBuilder(size); + foreach (var b in data) + { + result.Append(chars[b % (chars.Length)]); + } + return result.ToString(); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Application/Logic/System/TokenManager.cs b/RebusNeo/Src/Application/Logic/System/TokenManager.cs new file mode 100644 index 0000000..f26644a --- /dev/null +++ b/RebusNeo/Src/Application/Logic/System/TokenManager.cs @@ -0,0 +1,124 @@ +using System; +using RebusNeo.Src.Domain.Interfaces; +using RebusNeo.Src.Domain.Implementations; +using RebusNeo.Src.Application.Interfaces.AManagers; +using System.Linq; + +namespace RebusNeo.Src.Application.Logic.System +{ + public class TokenManager : AManager + { + private Token _token; + private int _expTime = 10; + + public void GenerateTokenFor(int pUserId) + { + TokenGenerator tokenGenerator = new TokenGenerator(); + string tokenValue; + + tokenValue = tokenGenerator.Generate(32); + + _token = context.token.FirstOrDefault(o => o.userid == pUserId); + + if (_token == null) + { + _token = entityFactory.CreateToken(tokenValue, pUserId, DateTime.UtcNow.AddMinutes(_expTime)); + context.token.Add(_token); + } + else { + _token.token = tokenValue; + _token.expireDate = DateTime.UtcNow.AddMinutes(_expTime); + } + + + context.SaveChanges(); + } + + public string GetToken() + { + return _token.token; + } + + public void DeleteToken(string pToken, int pUserId) + { + try { + _token = context.token.FirstOrDefault(o => o.token == pToken); + + if (_token == null) + return; + + if (pUserId == _token.userid && DateTime.UtcNow < _token.expireDate) + { + _token.expireDate = DateTime.UtcNow.AddMinutes(0); + context.SaveChanges(); + } + } + catch(Exception){ + return; + } + } + + public void DeleteToken(int pUserId) + { + try { + _token = context.token.FirstOrDefault(o => o.userid == pUserId); + + if (_token == null) + return; + + _token.expireDate = DateTime.UtcNow.AddMinutes(0); + context.SaveChanges(); + } + catch(Exception){ + return; + } + } + + public bool IsTokenValid(string pToken, int pUserId) + { + try { + _token = context.token.FirstOrDefault(o => o.token == pToken); + + if (_token == null) + return false; + + if (pUserId == _token.userid && DateTime.UtcNow < _token.expireDate) + return true; + } + catch(Exception){ + return false; + } + + return false; + } + + public bool HasValidToken(int pUserId) + { + try { + foreach(var token in context.token) { + if (pUserId == token.userid && DateTime.UtcNow < token.expireDate) + return true; + } + } + catch(Exception){ + return false; + } + return false; + } + + public DateTime GetTokenExpDate(int pUserId) + { + try { + _token = context.token.FirstOrDefault(o => o.userid == pUserId); + + if (_token == null) + return new DateTime(); + + return _token.expireDate; + } + catch(Exception){ + return new DateTime(); + } + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Application/Logic/TestConn/ATestConn.cs b/RebusNeo/Src/Application/Logic/TestConn/ATestConn.cs new file mode 100644 index 0000000..2c4a91f --- /dev/null +++ b/RebusNeo/Src/Application/Logic/TestConn/ATestConn.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.IData; + +namespace RebusNeo.Src.Application.Logic.TestConn +{ + public abstract class ATestConn + { + private string _results; + + protected string _startMsg; + protected string _endMsg; + protected string _errMsg; + protected ITestConnData _testConnData; + + public string GetTestResults() + { + SetSpecificData(); + + // try + // { + _results = _testConnData.GetTestConnResults(); + // } + // catch (Exception ex) + // { + // throw ex; + // } + + if (_results == "") + { + _results = _errMsg; + } + + return _startMsg + _results + _endMsg; + } + + protected abstract void SetSpecificData(); + } +} diff --git a/RebusNeo/Src/Application/Logic/TestConn/TestConnFileSystem.cs b/RebusNeo/Src/Application/Logic/TestConn/TestConnFileSystem.cs new file mode 100644 index 0000000..33bf289 --- /dev/null +++ b/RebusNeo/Src/Application/Logic/TestConn/TestConnFileSystem.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Repository.FileSystem.Data; + +namespace RebusNeo.Src.Application.Logic.TestConn +{ + public class TestConnFileSystem : ATestConn + { + protected override void SetSpecificData() + { + _testConnData = new TestConnData(); + + _startMsg = ""; + _endMsg = ""; + _errMsg = "FileSystemErr"; + } + } +} diff --git a/RebusNeo/Src/Application/Logic/TestConn/TestConnManager.cs b/RebusNeo/Src/Application/Logic/TestConn/TestConnManager.cs new file mode 100644 index 0000000..191edd7 --- /dev/null +++ b/RebusNeo/Src/Application/Logic/TestConn/TestConnManager.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Interfaces.IData; + +namespace RebusNeo.Src.Application.Logic.TestConn +{ + public class TestConnManager : ATestConnManager + { + private List _testConnList = new List(); + + private string _results = ""; + private string _separator = ""; + private string _startOfTest = ""; + private string _endOfTest = ""; + + public TestConnManager() + { + _testConnList.Add(new TestConnFileSystem()); + _testConnList.Add(new TestConnRebusCore()); + } + + public override void TestConn() + { + _results = AddString(_results, _startOfTest); + + foreach (var testConn in _testConnList) + { + _results = AddString(_results, testConn.GetTestResults()); + } + + _results = AddString(_results, _endOfTest); + } + + public override string GetResults() { return _results; } + + private string AddString(string a, string b) + { + if (a != "") + { + return a + _separator + b; + } + return b; + } + } +} diff --git a/RebusNeo/Src/Application/Logic/TestConn/TestConnRebusCore.cs b/RebusNeo/Src/Application/Logic/TestConn/TestConnRebusCore.cs new file mode 100644 index 0000000..a930ff3 --- /dev/null +++ b/RebusNeo/Src/Application/Logic/TestConn/TestConnRebusCore.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Repository.RebusCore.Data; + +namespace RebusNeo.Src.Application.Logic.TestConn +{ + public class TestConnRebusCore : ATestConn + { + protected override void SetSpecificData() + { + _testConnData = new TestConnData(); + + _startMsg = ""; + _endMsg = ""; + _errMsg = "RebusCoreErr"; + } + } +} diff --git a/RebusNeo/Src/Application/Logic/TestConn/TestFligthManager.cs b/RebusNeo/Src/Application/Logic/TestConn/TestFligthManager.cs new file mode 100644 index 0000000..5b0a3ed --- /dev/null +++ b/RebusNeo/Src/Application/Logic/TestConn/TestFligthManager.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Repository.FileSystem.Data; + +namespace RebusNeo.Src.Application.Logic.TestConn +{ + public class TestFligthManager : ATestFlightManager + { + private ITestFlightData _testFlightData = new TestFlightData(); + + public override string GetTestFligth() + { + return _testFlightData.GetTestFlightResults(); + } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/Balance.cs b/RebusNeo/Src/Domain/Implementations/Balance.cs new file mode 100644 index 0000000..49ce526 --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/Balance.cs @@ -0,0 +1,13 @@ +using System; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class Balance : IEntity, IBalance + { + public int id { get; set; } + public int userid { get; set; } + + public decimal balance { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/Entity.cs b/RebusNeo/Src/Domain/Implementations/Entity.cs new file mode 100644 index 0000000..0011e9a --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/Entity.cs @@ -0,0 +1,8 @@ +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public abstract class Entity : IEntity + { + } +} diff --git a/RebusNeo/Src/Domain/Implementations/EntityFactory.cs b/RebusNeo/Src/Domain/Implementations/EntityFactory.cs new file mode 100644 index 0000000..c0211c0 --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/EntityFactory.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using RebusNeo.Src.Domain.Implementations; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class EntityFactory : IEntityFactory + { + public List CreateEntities() + { + return new List(); + } + + public IUserLoginInfo CreateUserLoginInfo(string loginName, string password) + { + return new UserLoginInfo(password, loginName); + } + + public UserInfo CreateUserInfo(string loginName, string email) + { + return new UserInfo(loginName, email); + } + + public IUserRegistrationInfo CreateUserRegistrationInfo(IUserInfo info, IUserLoginInfo loginInfo) + { + return new UserRegistrationInfo(info, loginInfo); + } + + public IUserRegistrationInfo CreateUserRegistrationInfo(string loginName, string email, string password) + { + return CreateUserRegistrationInfo(CreateUserInfo(loginName, email), CreateUserLoginInfo(loginName, password)); + } + + public Token CreateToken(string pToken, int pUserid, DateTime pExpireDate) + { + return new Token(pToken, pUserid, pExpireDate); + } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/Flight.cs b/RebusNeo/Src/Domain/Implementations/Flight.cs new file mode 100644 index 0000000..eac05c0 --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/Flight.cs @@ -0,0 +1,12 @@ +using System; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class Flight : IEntity, IFlight + { + public int id { get; set; } + public int flightId { get; set; } + public decimal cost { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/Order.cs b/RebusNeo/Src/Domain/Implementations/Order.cs new file mode 100644 index 0000000..0c70877 --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/Order.cs @@ -0,0 +1,14 @@ +using System; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class Order : IEntity, IOrder + { + public int id { get; set; } + public int userid { get; set; } + public decimal cost { get; set; } + public string details { get; set; } + public DateTime datetime { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/OrderResp.cs b/RebusNeo/Src/Domain/Implementations/OrderResp.cs new file mode 100644 index 0000000..92f3dac --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/OrderResp.cs @@ -0,0 +1,11 @@ +using System; +using RebusNeo.Src.Domain.Interfaces; +using System.Collections.Generic; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class OrderResp : IEntity, IOrderResp + { + public List flights { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/PersonalInfo.cs b/RebusNeo/Src/Domain/Implementations/PersonalInfo.cs new file mode 100644 index 0000000..73c17f1 --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/PersonalInfo.cs @@ -0,0 +1,26 @@ +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class PersonalInfo : IEntity, IPersonalInfo + { + public PersonalInfo(){} + + public int id { get; set; } + public int userid { get; set; } + + public string firstname { get; set; } + + public string lastname { get; set; } + + public string phonenumber { get; set; } + + public string country { get; set; } + + public string city { get; set; } + + public string street { get; set; } + + public string house { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/Report.cs b/RebusNeo/Src/Domain/Implementations/Report.cs new file mode 100644 index 0000000..6eb2f41 --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/Report.cs @@ -0,0 +1,9 @@ +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class Report : IEntity, IReport + { + public ReportUsers reportUsers { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/ReportUser.cs b/RebusNeo/Src/Domain/Implementations/ReportUser.cs new file mode 100644 index 0000000..d48da1b --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/ReportUser.cs @@ -0,0 +1,21 @@ +using System; +using RebusNeo.Src.Domain.Interfaces; +using System.Collections.Generic; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class ReportUser : IEntity, IReportUser + { + public int id { get; set; } + + public string username { get; set; } + + public DateTime lastlogin { get; set; } + + public string status { get; set; } + + public bool session { get; set; } + + public List orders { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/ReportUsers.cs b/RebusNeo/Src/Domain/Implementations/ReportUsers.cs new file mode 100644 index 0000000..560fe3b --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/ReportUsers.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class ReportUsers : IEntity, IReportUsers + { + public List reportUsers { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/Token.cs b/RebusNeo/Src/Domain/Implementations/Token.cs new file mode 100644 index 0000000..ba9446e --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/Token.cs @@ -0,0 +1,24 @@ +using System; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class Token : IEntity, IToken + { + public Token(){} + + public Token(string pToken, int pUserid, DateTime pExpireDate) + { + token = pToken; + userid = pUserid; + expireDate = pExpireDate; + } + + public int id { get; set; } + public string token { get; set; } + + public int userid { get; set; } + + public DateTime expireDate { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/UserInfo.cs b/RebusNeo/Src/Domain/Implementations/UserInfo.cs new file mode 100644 index 0000000..3373d12 --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/UserInfo.cs @@ -0,0 +1,33 @@ +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class UserInfo : IEntity, IUserInfo + { + public UserInfo(){} + public UserInfo(string pLoginName, string pEmail) + { + loginName = pLoginName; + email = pEmail; + } + + public UserInfo(string pLoginName, string pEmail, string pPassword) + { + loginName = pLoginName; + email = pEmail; + password = pPassword; + status = "ACTIVE"; + } + + public int id { get; set; } + public string loginName { get; set; } + + public string email { get; set; } + + public string password { get; set; } + + public string salt { get; set; } + + public string status { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/UserLoginInfo.cs b/RebusNeo/Src/Domain/Implementations/UserLoginInfo.cs new file mode 100644 index 0000000..c8e5863 --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/UserLoginInfo.cs @@ -0,0 +1,16 @@ +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class UserLoginInfo : IEntity, IUserLoginInfo + { + public UserLoginInfo(string password, string loginName) + { + userPassword = password; + userLoginName = loginName; + } + public string userPassword { get; set; } + + public string userLoginName { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Implementations/UserRegistrationInfo.cs b/RebusNeo/Src/Domain/Implementations/UserRegistrationInfo.cs new file mode 100644 index 0000000..ed54acc --- /dev/null +++ b/RebusNeo/Src/Domain/Implementations/UserRegistrationInfo.cs @@ -0,0 +1,16 @@ +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Domain.Implementations +{ + public class UserRegistrationInfo : IEntity, IUserRegistrationInfo + { + public UserRegistrationInfo(IUserInfo info, IUserLoginInfo loginInfo) + { + userInfo = info; + userLoginInfo = loginInfo; + } + public IUserInfo userInfo{ get; set; } + + public IUserLoginInfo userLoginInfo { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IBalance.cs b/RebusNeo/Src/Domain/Interfaces/IBalance.cs new file mode 100644 index 0000000..7e654a6 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IBalance.cs @@ -0,0 +1,14 @@ +using System; + +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IBalance : IEntity + { + + public int id { get; set; } + public int userid { get; set; } + + public decimal balance { get; set; } + + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IEntity.cs b/RebusNeo/Src/Domain/Interfaces/IEntity.cs new file mode 100644 index 0000000..5554d6a --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IEntity.cs @@ -0,0 +1,6 @@ +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IEntity + { + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IEntityFactory.cs b/RebusNeo/Src/Domain/Interfaces/IEntityFactory.cs new file mode 100644 index 0000000..18dc0b2 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IEntityFactory.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IEntityFactory + { + public List CreateEntities(); + public IUserLoginInfo CreateUserLoginInfo(string loginName, string password); + public UserInfo CreateUserInfo(string loginName, string email); + public IUserRegistrationInfo CreateUserRegistrationInfo(IUserInfo info, IUserLoginInfo loginInfo); + public IUserRegistrationInfo CreateUserRegistrationInfo(string loginName, string email, string password); + + public Token CreateToken(string pToken, int pUserid, DateTime pExpireDate); + + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IFlight.cs b/RebusNeo/Src/Domain/Interfaces/IFlight.cs new file mode 100644 index 0000000..361aa69 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IFlight.cs @@ -0,0 +1,11 @@ +using System; + +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IFlight : IEntity + { + public int id { get; set; } + public int flightId { get; set; } + public decimal cost { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IOrder.cs b/RebusNeo/Src/Domain/Interfaces/IOrder.cs new file mode 100644 index 0000000..63f8c22 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IOrder.cs @@ -0,0 +1,13 @@ +using System; + +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IOrder : IEntity + { + public int id { get; set; } + public int userid { get; set; } + public decimal cost { get; set; } + public string details { get; set; } + public DateTime datetime { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IOrderResp.cs b/RebusNeo/Src/Domain/Interfaces/IOrderResp.cs new file mode 100644 index 0000000..e060b2f --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IOrderResp.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IOrderResp : IEntity + { + List flights { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IPersonalInfo.cs b/RebusNeo/Src/Domain/Interfaces/IPersonalInfo.cs new file mode 100644 index 0000000..7764322 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IPersonalInfo.cs @@ -0,0 +1,23 @@ +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IPersonalInfo : IEntity + { + + public int id { get; set; } + public int userid { get; set; } + + public string firstname { get; set; } + + public string lastname { get; set; } + + public string phonenumber { get; set; } + + public string country { get; set; } + + public string city { get; set; } + + public string street { get; set; } + + public string house { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IReport.cs b/RebusNeo/Src/Domain/Interfaces/IReport.cs new file mode 100644 index 0000000..323d4c5 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IReport.cs @@ -0,0 +1,9 @@ +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IReport : IEntity + { + public ReportUsers reportUsers { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IReportUser.cs b/RebusNeo/Src/Domain/Interfaces/IReportUser.cs new file mode 100644 index 0000000..0ac7d57 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IReportUser.cs @@ -0,0 +1,21 @@ +using System; +using RebusNeo.Src.Domain.Implementations; +using System.Collections.Generic; + +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IReportUser : IEntity + { + public int id { get; set; } + + public string username { get; set; } + + public DateTime lastlogin { get; set; } + + public string status { get; set; } + + public bool session { get; set; } + + public List orders { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IReportUsers.cs b/RebusNeo/Src/Domain/Interfaces/IReportUsers.cs new file mode 100644 index 0000000..d9b8f59 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IReportUsers.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IReportUsers : IEntity + { + public List reportUsers { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IToken.cs b/RebusNeo/Src/Domain/Interfaces/IToken.cs new file mode 100644 index 0000000..b31d366 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IToken.cs @@ -0,0 +1,15 @@ +using System; + +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IToken : IEntity + { + + public int id { get; set; } + public string token { get; set; } + + public int userid { get; set; } + + public DateTime expireDate { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IUserInfo.cs b/RebusNeo/Src/Domain/Interfaces/IUserInfo.cs new file mode 100644 index 0000000..30dc21a --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IUserInfo.cs @@ -0,0 +1,17 @@ +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IUserInfo : IEntity + { + + public int id { get; set; } + public string loginName { get; set; } + + public string email { get; set; } + + public string password { get; set; } + + public string salt { get; set; } + + public string status { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IUserLoginInfo.cs b/RebusNeo/Src/Domain/Interfaces/IUserLoginInfo.cs new file mode 100644 index 0000000..433bf19 --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IUserLoginInfo.cs @@ -0,0 +1,9 @@ +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IUserLoginInfo : IEntity + { + public string userPassword { get; set; } + + public string userLoginName { get; set; } + } +} diff --git a/RebusNeo/Src/Domain/Interfaces/IUserRegistrationInfo.cs b/RebusNeo/Src/Domain/Interfaces/IUserRegistrationInfo.cs new file mode 100644 index 0000000..7c4ef2d --- /dev/null +++ b/RebusNeo/Src/Domain/Interfaces/IUserRegistrationInfo.cs @@ -0,0 +1,9 @@ +namespace RebusNeo.Src.Domain.Interfaces +{ + public interface IUserRegistrationInfo : IEntity + { + public IUserInfo userInfo{ get; set; } + + public IUserLoginInfo userLoginInfo { get; set; } + } +} diff --git a/RebusNeo/Src/Integration/Common/Services/Converters/DatesConverter.cs b/RebusNeo/Src/Integration/Common/Services/Converters/DatesConverter.cs new file mode 100644 index 0000000..d99ed75 --- /dev/null +++ b/RebusNeo/Src/Integration/Common/Services/Converters/DatesConverter.cs @@ -0,0 +1,31 @@ +using System; +using RebusNeo.Src.Application.Interfaces.IServices; + +namespace RebusNeo.Src.Integration.Common.Services.Converters +{ + public class DatesConverter : IDateConverter + { + public DateTime ConvertDateFromString(string date) + { + try + { + return DateTime.ParseExact(date, "yyyy-M-d", System.Globalization.CultureInfo.InvariantCulture); + } + catch (Exception) + { + return DateTime.MinValue; + } + } + + public string ConvertDateToString(DateTime date) + { + return date.ToString("yyyy-M-d", System.Globalization.CultureInfo.InvariantCulture); + } + + public string TryToConvert(string date) + { + DateTime dateTime = ConvertDateFromString(date); + return ConvertDateToString(dateTime); + } + } +} diff --git a/RebusNeo/Src/Integration/Common/Services/Converters/JSONSerializer.cs b/RebusNeo/Src/Integration/Common/Services/Converters/JSONSerializer.cs new file mode 100644 index 0000000..f241925 --- /dev/null +++ b/RebusNeo/Src/Integration/Common/Services/Converters/JSONSerializer.cs @@ -0,0 +1,29 @@ +using RebusNeo.Src.Application.Interfaces.IServices; +using Newtonsoft.Json; + +namespace RebusNeo.Src.Integration.Common.Services.Converters +{ + public class JSONSerializer : ISerializer + { + public T Deserialize(string _object) + { + var setting = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.None + }; + + return JsonConvert.DeserializeObject(_object, setting); + } + + public string Serialize(object response) + { + var setting = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.None + }; + + return JsonConvert.SerializeObject(response, setting); + } + + } +} diff --git a/RebusNeo/Src/Integration/Common/Services/Response/JSONResponseFactory.cs b/RebusNeo/Src/Integration/Common/Services/Response/JSONResponseFactory.cs new file mode 100644 index 0000000..97d45ad --- /dev/null +++ b/RebusNeo/Src/Integration/Common/Services/Response/JSONResponseFactory.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using RebusNeo.Src.Application.Interfaces.IServices; +using RebusNeo.Src.Application.Interfaces.Factories; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Integration.Common.Services.Response +{ + public class JSONResponseFactory : IResponseFactory + { + private ISerializer _serializer; + + public JSONResponseFactory(ISerializer serializer) + { + _serializer = serializer; + } + + public string CreateResponse(int errorCode, string errorMessage, IEnumerable entities, string token) + { + IResponseError error = CreaterResponseError(errorCode, errorMessage); + IResponseToken respToken = CreaterResponseToken(token); + IResponseHeader header = CreateResponseHeader(error, respToken); + IResponseBody body = CreateResponseBody(entities); + IResponse response = new Response(body, header); + return _serializer.Serialize(response); + } + + public IResponseBody CreateResponseBody(IEnumerable entities) + { + return new ResponseBody(entities); + } + + public IResponseHeader CreateResponseHeader(IResponseError error, IResponseToken token) + { + return new ResponseHeader(error, token); + } + + public IResponseError CreaterResponseError(int errorCode, string errorMessage) + { + return new ResponseError(errorCode, errorMessage); + } + + private IResponseToken CreaterResponseToken(string token) + { + return new ResponseToken(token); + } + } +} diff --git a/RebusNeo/Src/Integration/Common/Services/Response/Response.cs b/RebusNeo/Src/Integration/Common/Services/Response/Response.cs new file mode 100644 index 0000000..0e08ab2 --- /dev/null +++ b/RebusNeo/Src/Integration/Common/Services/Response/Response.cs @@ -0,0 +1,24 @@ +using RebusNeo.Src.Application.Interfaces.IServices; +using Newtonsoft.Json; + +namespace RebusNeo.Src.Integration.Common.Services.Response +{ + [JsonObject(MemberSerialization.OptIn)] + public class Response : IResponse + { + + private IResponseBody _body; + private IResponseHeader _header; + + public Response(IResponseBody body, IResponseHeader header) + { + _body = body; + _header = header; + } + + [JsonProperty] + public IResponseHeader Header { get => _header; } + [JsonProperty] + public IResponseBody ResponseBody { get => _body; } + } +} diff --git a/RebusNeo/Src/Integration/Common/Services/Response/ResponseBody.cs b/RebusNeo/Src/Integration/Common/Services/Response/ResponseBody.cs new file mode 100644 index 0000000..b4c3983 --- /dev/null +++ b/RebusNeo/Src/Integration/Common/Services/Response/ResponseBody.cs @@ -0,0 +1,22 @@ +using System.Collections.Generic; +using RebusNeo.Src.Application.Interfaces.IServices; +using RebusNeo.Src.Domain.Interfaces; +using Newtonsoft.Json; + +namespace RebusNeo.Src.Integration.Common.Services.Response +{ + [JsonObject(MemberSerialization.OptIn)] + public class ResponseBody : IResponseBody + { + + private IEnumerable _entities; + + public ResponseBody(IEnumerable entities) + { + _entities = entities; + } + + [JsonProperty] + public IEnumerable Entities { get => _entities; } + } +} diff --git a/RebusNeo/Src/Integration/Common/Services/Response/ResponseError.cs b/RebusNeo/Src/Integration/Common/Services/Response/ResponseError.cs new file mode 100644 index 0000000..7785f31 --- /dev/null +++ b/RebusNeo/Src/Integration/Common/Services/Response/ResponseError.cs @@ -0,0 +1,26 @@ +using RebusNeo.Src.Application.Interfaces.IServices; +using Newtonsoft.Json; + +namespace RebusNeo.Src.Integration.Common.Services.Response +{ + [JsonObject(MemberSerialization.OptIn)] + public class ResponseError : IResponseError + { + public ResponseError(string errorMessage) + { + ErrorMessage = errorMessage; + } + + public ResponseError(int errorCode, string errorMessage) + { + ErrorCode = errorCode; + ErrorMessage = errorMessage; + } + + [JsonProperty] + public int ErrorCode { set; get; } = 0; + + [JsonProperty] + public string ErrorMessage { set; get; } = ""; + } +} diff --git a/RebusNeo/Src/Integration/Common/Services/Response/ResponseHeader.cs b/RebusNeo/Src/Integration/Common/Services/Response/ResponseHeader.cs new file mode 100644 index 0000000..90450d1 --- /dev/null +++ b/RebusNeo/Src/Integration/Common/Services/Response/ResponseHeader.cs @@ -0,0 +1,25 @@ +using RebusNeo.Src.Application.Interfaces.IServices; +using Newtonsoft.Json; + +namespace RebusNeo.Src.Integration.Common.Services.Response +{ + [JsonObject(MemberSerialization.OptIn)] + public class ResponseHeader : IResponseHeader + { + + private IResponseError _responseError; + private IResponseToken _responseToken; + + public ResponseHeader(IResponseError responseError, IResponseToken responseToken) + { + _responseError = responseError; + _responseToken = responseToken; + } + + [JsonProperty] + public IResponseError ResponseError { get => _responseError;} + + [JsonProperty] + public IResponseToken ResponseToken { get => _responseToken;} + } +} diff --git a/RebusNeo/Src/Integration/Common/Services/Response/ResponseToken.cs b/RebusNeo/Src/Integration/Common/Services/Response/ResponseToken.cs new file mode 100644 index 0000000..be968e7 --- /dev/null +++ b/RebusNeo/Src/Integration/Common/Services/Response/ResponseToken.cs @@ -0,0 +1,18 @@ +using RebusNeo.Src.Application.Interfaces.IServices; +using Newtonsoft.Json; + +namespace RebusNeo.Src.Integration.Common.Services.Response +{ + [JsonObject(MemberSerialization.OptIn)] + public class ResponseToken : IResponseToken + { + public ResponseToken(string token) + { + Token = token; + } + + + [JsonProperty] + public string Token { set; get; } = "123456789"; + } +} diff --git a/RebusNeo/Src/Integration/Config/IntegrationConfig.cs b/RebusNeo/Src/Integration/Config/IntegrationConfig.cs new file mode 100644 index 0000000..ffb4b67 --- /dev/null +++ b/RebusNeo/Src/Integration/Config/IntegrationConfig.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Integration.Config +{ + public static class IntegrationConfig + { + public static string GetFileSystemDir() + { + return OperatingSys.IsLinux() ? "/app/files/" : @"C:\Users\Paulius\Desktop\"; + } + + public static string GetRebusCoreUri() + { + return OperatingSys.IsLinux() ? "http://rebuscore:5001/" : ""; + } + + public static string GetTestFileType() + { + return ".json"; + } + } +} diff --git a/RebusNeo/Src/Integration/Config/OperatingSys.cs b/RebusNeo/Src/Integration/Config/OperatingSys.cs new file mode 100644 index 0000000..7c1d1ab --- /dev/null +++ b/RebusNeo/Src/Integration/Config/OperatingSys.cs @@ -0,0 +1,10 @@ +using System.Runtime.InteropServices; + +public static class OperatingSys +{ + public static bool IsWindows() => + RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + public static bool IsLinux() => + RuntimeInformation.IsOSPlatform(OSPlatform.Linux); +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Config/Program.cs b/RebusNeo/Src/Integration/Config/Program.cs new file mode 100644 index 0000000..0a99728 --- /dev/null +++ b/RebusNeo/Src/Integration/Config/Program.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; + +namespace RebusNeo.Src.Integration.Config +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseStartup(); + }); + } +} diff --git a/RebusNeo/Src/Integration/Config/Startup.cs b/RebusNeo/Src/Integration/Config/Startup.cs new file mode 100644 index 0000000..8b6c66a --- /dev/null +++ b/RebusNeo/Src/Integration/Config/Startup.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.EntityFrameworkCore; +using RebusNeo.Src.Repository.MSSQL.Common; + +namespace RebusNeo.Src.Integration.Config +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllers(); + + var server = Configuration["DBServer"] ?? "ms-sql-server"; + var port = Configuration["DBPort"] ?? "1433"; + var database = Configuration["Database"] ?? "FlightSys"; + var user = Configuration["DBUser"] ?? "SA"; + var password = Configuration["DBPassword"] ?? "ABC123abc"; + + services.AddDbContext(options => + options.UseSqlServer($"Server={server},{port};Initial Catalog={database}; User ID={user};Password={password}")); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseRouting(); + + app.UseAuthorization(); + + app.UseEndpoints(endpoints => + { + endpoints.MapControllers(); + }); + + PrepDB.PrepPopulation(app); + } + } +} diff --git a/RebusNeo/Src/Integration/Controllers/BalanceAdminController.cs b/RebusNeo/Src/Integration/Controllers/BalanceAdminController.cs new file mode 100644 index 0000000..7a6b071 --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/BalanceAdminController.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.ComponentModel.DataAnnotations; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class BalanceAdminController : ControllerBase + { + + private readonly ILogger _logger; + private APersonalBalanceManager personalBalanceManager; + + private readonly MSSQLContext _context; + + public BalanceAdminController(MSSQLContext context) + { + _context = context; + personalBalanceManager = new PersonalBalanceManager(); + personalBalanceManager.SetDbContext(_context); + } + + [HttpGet] + public string Get([Required] int userid, [Required] string amount) + { + try{ + personalBalanceManager.UpdatePersonalBalance(userid, Convert.ToDecimal(amount)); + } + catch(Exception) + { + return "Error"; + } + + return "OK"; + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/BanUserController.cs b/RebusNeo/Src/Integration/Controllers/BanUserController.cs new file mode 100644 index 0000000..e98156c --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/BanUserController.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.ComponentModel.DataAnnotations; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class BanUserController : ControllerBase + { + + private readonly ILogger _logger; + private ALoginManager loginManager; + + private readonly MSSQLContext _context; + + public BanUserController(MSSQLContext context) + { + _context = context; + loginManager = new LoginManager(); + loginManager.SetDbContext(_context); + } + + [HttpGet] + public string Get(string username, string action) + { + return loginManager.BanUser(username, action); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/ChangePassController.cs b/RebusNeo/Src/Integration/Controllers/ChangePassController.cs new file mode 100644 index 0000000..26694cf --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/ChangePassController.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.Security.Cryptography; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ChangePassController : ControllerBase + { + + private readonly ILogger _logger; + private ALoginManager _loginManager; + + private readonly MSSQLContext _context; + + public ChangePassController(MSSQLContext context) + { + _context = context; + _loginManager = new LoginManager(); + _loginManager.SetDbContext(_context); + } + + [HttpPost] + public string Post(string username, string password, string newpassword) + { + return _loginManager.LoginChangePass(username, password, newpassword); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/FlightController.cs b/RebusNeo/Src/Integration/Controllers/FlightController.cs new file mode 100644 index 0000000..080cad3 --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/FlightController.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.Journey; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class FlightController : ControllerBase + { + + private readonly ILogger _logger; + private readonly AFlightManager _flightManager = new FlightManager(); + + public FlightController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string Get(ulong flightId) + { + return _flightManager.GetFlight(flightId); + } + } +} diff --git a/RebusNeo/Src/Integration/Controllers/JourneyController.cs b/RebusNeo/Src/Integration/Controllers/JourneyController.cs new file mode 100644 index 0000000..2c21d10 --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/JourneyController.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.Journey; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class JourneyController : ControllerBase + { + + private readonly ILogger _logger; + private readonly AJourneyManager _journeyManager = new JourneyManager(); + + public JourneyController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string Get(bool isOneWay, + string origin, + string destination, + string depDate, + string retDate = "", + bool onlyDirect = false, + int numOfPass = 1, + string passClass = "economy") + { + return _journeyManager.GetJourney(isOneWay, origin, destination, depDate, + retDate, onlyDirect, numOfPass, passClass); + } + } +} diff --git a/RebusNeo/Src/Integration/Controllers/LocationsController.cs b/RebusNeo/Src/Integration/Controllers/LocationsController.cs new file mode 100644 index 0000000..4d3847d --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/LocationsController.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.Location; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class LocationsController : ControllerBase + { + + private readonly ILogger _logger; + private readonly ALocationtManager _locationManager = new LocationManager(); + + public LocationsController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string Get() + { + return _locationManager.GetAllLocations(); + } + } +} diff --git a/RebusNeo/Src/Integration/Controllers/LogOutController.cs b/RebusNeo/Src/Integration/Controllers/LogOutController.cs new file mode 100644 index 0000000..107b583 --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/LogOutController.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.ComponentModel.DataAnnotations; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class LogOutController : ControllerBase + { + + private readonly ILogger _logger; + private ALoginManager loginManager; + + private readonly MSSQLContext _context; + + public LogOutController(MSSQLContext context) + { + _context = context; + loginManager = new LoginManager(); + loginManager.SetDbContext(_context); + } + + [HttpPost] + public string Post([Required] string token, [Required] int userid) + { + return loginManager.LogOut(token, userid); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/LoginController.cs b/RebusNeo/Src/Integration/Controllers/LoginController.cs new file mode 100644 index 0000000..2810cd1 --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/LoginController.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.Security.Cryptography; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class LoginController : ControllerBase + { + + private readonly ILogger _logger; + private ALoginManager _loginManager; + + private readonly MSSQLContext _context; + + public LoginController(MSSQLContext context) + { + _context = context; + _loginManager = new LoginManager(); + _loginManager.SetDbContext(_context); + } + + [HttpPost] + public string Post(string username, string password) + { + return _loginManager.Login(username, password); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/OrderJourneyController.cs b/RebusNeo/Src/Integration/Controllers/OrderJourneyController.cs new file mode 100644 index 0000000..1c2383a --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/OrderJourneyController.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.Journey; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.ComponentModel.DataAnnotations; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class OrderJourneyController : ControllerBase + { + + private readonly ILogger _logger; + private AJourneyManager journeyManager; + + private readonly MSSQLContext _context; + + public OrderJourneyController(MSSQLContext context) + { + _context = context; + journeyManager = new JourneyManager(); + journeyManager.SetDbContext(_context); + } + + [HttpPost] + public string Post([Required] string token, [Required] int userid, [Required] string flightlist) + { + return journeyManager.OrderJourney(token, userid, flightlist); + } + + [HttpGet] + public string Get([Required] string token, [Required] int userid) + { + return journeyManager.GetOrderedFlights(token, userid); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/PersonalBalanceController.cs b/RebusNeo/Src/Integration/Controllers/PersonalBalanceController.cs new file mode 100644 index 0000000..3ba8120 --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/PersonalBalanceController.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.ComponentModel.DataAnnotations; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class PersonalBalanceController : ControllerBase + { + + private readonly ILogger _logger; + private APersonalBalanceManager personalBalanceManager; + + private readonly MSSQLContext _context; + + public PersonalBalanceController(MSSQLContext context) + { + _context = context; + personalBalanceManager = new PersonalBalanceManager(); + personalBalanceManager.SetDbContext(_context); + } + + [HttpPost] + public string Post([Required] string token, [Required] int userid, [Required] string addbal) + { + return personalBalanceManager.AddPersonalBalance(token, userid, addbal); + } + + [HttpGet] + public string Get([Required] string token, [Required] int userid) + { + return personalBalanceManager.GetPersonalBalance(token, userid); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/PersonalInfoController.cs b/RebusNeo/Src/Integration/Controllers/PersonalInfoController.cs new file mode 100644 index 0000000..35bf679 --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/PersonalInfoController.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.Security.Cryptography; +using System.ComponentModel.DataAnnotations; + +namespace RebusNeo.Src.Integration.Controllers +{ + + [ApiController] + [Route("[controller]")] + public class PersonalInfoController : ControllerBase + { + + private readonly ILogger _logger; + private APersonalInfoManager _personalInfoManager; + + private readonly MSSQLContext _context; + + public PersonalInfoController(MSSQLContext context) + { + _context = context; + _personalInfoManager = new PersonalInfoManager(); + _personalInfoManager.SetDbContext(_context); + } + + [HttpPost] + public string Post([Required] string token, [Required] int userid, string firstname, string lastname, string phonenumber, string country, string city, string street, string house) + { + return _personalInfoManager.SetPersonalInfo(token, userid, firstname, lastname, phonenumber, country, city, street, house); + } + + [HttpGet] + public string Get([Required] string token, [Required] int userid) + { + return _personalInfoManager.GetPersonalInfo(token, userid); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/RegisterController.cs b/RebusNeo/Src/Integration/Controllers/RegisterController.cs new file mode 100644 index 0000000..bc29bda --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/RegisterController.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.Security.Cryptography; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class RegisterController : ControllerBase + { + + private readonly ILogger _logger; + private readonly ARegistrationManager _registrationManager = new RegistrationManager(); + + private readonly MSSQLContext _context; + + public RegisterController(ILogger logger, MSSQLContext context) + { + _logger = logger; + _context = context; + } + + [HttpPost] + public string Post(string username, string email, string password) + { + username.Trim(); + email.Trim(); + password.Trim(); + + try { + var a = _context.userInfo.First(o => o.loginName == username).loginName; + return _registrationManager.Error("This Username is already used!"); + } + catch (Exception){ + } + + if (username.Length < 8 || password.Length < 8){ + return _registrationManager.Error("Username and Password cannot contain less than 8 letters!"); + } + + try { + _ = new System.Net.Mail.MailAddress(email); + } + catch { + return _registrationManager.Error("Email Address is incorrect!"); + } + + byte[] salt; + new RNGCryptoServiceProvider().GetBytes(salt = new byte[16]); + + var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000); + byte[] hash = pbkdf2.GetBytes(20); + + byte[] hashBytes = new byte[36]; + Array.Copy(salt, 0, hashBytes, 0, 16); + Array.Copy(hash, 0, hashBytes, 16, 20); + + string savedPasswordHash = Convert.ToBase64String(hashBytes); + _context.userInfo.Add(new UserInfo(username, email, savedPasswordHash)); + _context.SaveChanges(); + + return _registrationManager.Ok(username, email); + } + + [HttpGet] + public ActionResult> Get() + { + _registrationManager.passDb(_context); + return _registrationManager.get(); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/ReportController.cs b/RebusNeo/Src/Integration/Controllers/ReportController.cs new file mode 100644 index 0000000..3b8f76c --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/ReportController.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.System; +using RebusNeo.Src.Repository.MSSQL.Common; +using RebusNeo.Src.Domain.Implementations; +using System.ComponentModel.DataAnnotations; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class ReportController : ControllerBase + { + + private readonly ILogger _logger; + private AReportManager reportManager; + + private readonly MSSQLContext _context; + + public ReportController(MSSQLContext context) + { + _context = context; + reportManager = new ReportManager(); + reportManager.SetDbContext(_context); + } + + [HttpGet] + public string Get() + { + return reportManager.GetReport(); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Integration/Controllers/TestConnController.cs b/RebusNeo/Src/Integration/Controllers/TestConnController.cs new file mode 100644 index 0000000..56bf71d --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/TestConnController.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.TestConn; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class TestConnController : ControllerBase + { + + private readonly ILogger _logger; + private ATestConnManager _testConnManager = new TestConnManager(); + + public TestConnController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string Get() + { + _testConnManager.TestConn(); + return _testConnManager.GetResults(); + } + } +} diff --git a/RebusNeo/Src/Integration/Controllers/TestFlightController.cs b/RebusNeo/Src/Integration/Controllers/TestFlightController.cs new file mode 100644 index 0000000..6c7950a --- /dev/null +++ b/RebusNeo/Src/Integration/Controllers/TestFlightController.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using RebusNeo.Src.Application.Interfaces.AManagers; +using RebusNeo.Src.Application.Logic.TestConn; + +namespace RebusNeo.Src.Integration.Controllers +{ + [ApiController] + [Route("[controller]")] + public class TestFlightController : ControllerBase + { + + private readonly ILogger _logger; + private readonly ATestFlightManager _testFlightManager = new TestFligthManager(); + + public TestFlightController(ILogger logger) + { + _logger = logger; + } + + [HttpGet] + public string Get() + { + return _testFlightManager.GetTestFligth(); + } + } +} diff --git a/RebusNeo/Src/Repository/Common/AsyncHelper.cs b/RebusNeo/Src/Repository/Common/AsyncHelper.cs new file mode 100644 index 0000000..2aae032 --- /dev/null +++ b/RebusNeo/Src/Repository/Common/AsyncHelper.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using System.Threading; + +namespace RebusNeo.Src.Repository.Common +{ + public static class AsyncHelper + { + private static readonly TaskFactory _taskFactory = new + TaskFactory(CancellationToken.None, + TaskCreationOptions.None, + TaskContinuationOptions.None, + TaskScheduler.Default); + + public static TResult RunSync(Func> func) + => _taskFactory + .StartNew(func) + .Unwrap() + .GetAwaiter() + .GetResult(); + + public static void RunSync(Func func) + => _taskFactory + .StartNew(func) + .Unwrap() + .GetAwaiter() + .GetResult(); + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Repository/Common/WebApiClient.cs b/RebusNeo/Src/Repository/Common/WebApiClient.cs new file mode 100644 index 0000000..92ee5b2 --- /dev/null +++ b/RebusNeo/Src/Repository/Common/WebApiClient.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Text.Json; +using System.Threading.Tasks; + +namespace RebusNeo.Src.Repository.Common +{ + class WebApiClient + { + private static readonly HttpClient client = new HttpClient(); + private static string response = "httpError"; + + public static async Task Call(string uri) + { + // Call asynchronous network methods in a try/catch block to handle exceptions. + try + { + response = await client.GetStringAsync(uri); + } + catch (HttpRequestException) + { + //FIXME + return response; + } + System.Console.WriteLine(response); + return response; + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Repository/FileSystem/Data/TestConnData.cs b/RebusNeo/Src/Repository/FileSystem/Data/TestConnData.cs new file mode 100644 index 0000000..b83d5a1 --- /dev/null +++ b/RebusNeo/Src/Repository/FileSystem/Data/TestConnData.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Integration.Config; + +namespace RebusNeo.Src.Repository.FileSystem.Data +{ + public class TestConnData : ITestConnData + { + public string GetTestConnResults() + { + return System.IO.File.ReadAllText(IntegrationConfig.GetFileSystemDir() + "test.txt"); + } + } +} diff --git a/RebusNeo/Src/Repository/FileSystem/Data/TestFlightData.cs b/RebusNeo/Src/Repository/FileSystem/Data/TestFlightData.cs new file mode 100644 index 0000000..5cb21a1 --- /dev/null +++ b/RebusNeo/Src/Repository/FileSystem/Data/TestFlightData.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Integration.Config; + +namespace RebusNeo.Src.Repository.FileSystem.Data +{ + public class TestFlightData : ITestFlightData + { + private string _result; + + public string GetTestFlightResults() + { + try + { + _result = System.IO.File.ReadAllText(IntegrationConfig.GetFileSystemDir() + "test_flight" + + IntegrationConfig.GetTestFileType()); + } + catch (Exception ex) + { + return ex.Message; + } + + return _result; + } + } +} diff --git a/RebusNeo/Src/Repository/MSSQL/Common/MSSQLContext.cs b/RebusNeo/Src/Repository/MSSQL/Common/MSSQLContext.cs new file mode 100644 index 0000000..f59e2aa --- /dev/null +++ b/RebusNeo/Src/Repository/MSSQL/Common/MSSQLContext.cs @@ -0,0 +1,26 @@ +using Microsoft.EntityFrameworkCore; +using RebusNeo.Src.Domain.Implementations; +using RebusNeo.Src.Domain.Interfaces; + +namespace RebusNeo.Src.Repository.MSSQL.Common +{ + public class MSSQLContext : DbContext + { + public MSSQLContext (DbContextOptions options) : base(options) + { + + } + + public DbSet userInfo {get; set;} + + public DbSet token {get; set;} + + public DbSet personalInfo {get; set;} + + public DbSet balance {get; set;} + + public DbSet flight {get; set;} + + public DbSet order {get; set;} + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Repository/MSSQL/PrepDB.cs b/RebusNeo/Src/Repository/MSSQL/PrepDB.cs new file mode 100644 index 0000000..6374ac6 --- /dev/null +++ b/RebusNeo/Src/Repository/MSSQL/PrepDB.cs @@ -0,0 +1,27 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using System.Linq; +using System; +using RebusNeo.Src.Domain.Implementations; + +namespace RebusNeo.Src.Repository.MSSQL.Common +{ + public static class PrepDB + { + public static void PrepPopulation(IApplicationBuilder app) + { + using (var serviceScope = app.ApplicationServices.CreateScope()) + { + SeedData(serviceScope.ServiceProvider.GetService()); + } + } + + public static void SeedData(MSSQLContext context) + { + context.Database.Migrate(); + + context.SaveChanges(); + } + } +} \ No newline at end of file diff --git a/RebusNeo/Src/Repository/RebusCore/Data/DirectCallJourneyData.cs b/RebusNeo/Src/Repository/RebusCore/Data/DirectCallJourneyData.cs new file mode 100644 index 0000000..b76c75b --- /dev/null +++ b/RebusNeo/Src/Repository/RebusCore/Data/DirectCallJourneyData.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Repository.Common; +using RebusNeo.Src.Integration.Config; + +namespace RebusNeo.Src.Repository.RebusCore.Data +{ + public class DirectCallJourneyData : IDirectCallJourneyData + { + public string GetJourney(string parameters) + { + return AsyncHelper.RunSync(() => WebApiClient.Call(IntegrationConfig.GetRebusCoreUri() + "journey" + parameters)); + } + } +} diff --git a/RebusNeo/Src/Repository/RebusCore/Data/FlightData.cs b/RebusNeo/Src/Repository/RebusCore/Data/FlightData.cs new file mode 100644 index 0000000..fe8e7c4 --- /dev/null +++ b/RebusNeo/Src/Repository/RebusCore/Data/FlightData.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Repository.Common; +using RebusNeo.Src.Integration.Config; + +namespace RebusNeo.Src.Repository.RebusCore.Data +{ + public class FlightData : IFlightData + { + public string GetFlight(ulong flightId) + { + return AsyncHelper.RunSync(() => WebApiClient.Call(IntegrationConfig.GetRebusCoreUri() + "flight?flightid=" + flightId)); + } + } +} diff --git a/RebusNeo/Src/Repository/RebusCore/Data/LocationData.cs b/RebusNeo/Src/Repository/RebusCore/Data/LocationData.cs new file mode 100644 index 0000000..fbc4226 --- /dev/null +++ b/RebusNeo/Src/Repository/RebusCore/Data/LocationData.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Repository.Common; +using RebusNeo.Src.Integration.Config; + +namespace RebusNeo.Src.Repository.RebusCore.Data +{ + public class LocationData : ILocationData + { + public string GetAllLocationResults() + { + return AsyncHelper.RunSync(() => WebApiClient.Call(IntegrationConfig.GetRebusCoreUri() + "alllocations")); + } + } +} diff --git a/RebusNeo/Src/Repository/RebusCore/Data/TestConnData.cs b/RebusNeo/Src/Repository/RebusCore/Data/TestConnData.cs new file mode 100644 index 0000000..9731833 --- /dev/null +++ b/RebusNeo/Src/Repository/RebusCore/Data/TestConnData.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using RebusNeo.Src.Application.Interfaces.IData; +using RebusNeo.Src.Repository.Common; +using RebusNeo.Src.Integration.Config; + +namespace RebusNeo.Src.Repository.RebusCore.Data +{ + public class TestConnData : ITestConnData + { + public string GetTestConnResults() + { + return AsyncHelper.RunSync(() => WebApiClient.Call(IntegrationConfig.GetRebusCoreUri() + "testconn")); + } + } +} diff --git a/RebusNeo/appsettings.Development.json b/RebusNeo/appsettings.Development.json new file mode 100644 index 0000000..8983e0f --- /dev/null +++ b/RebusNeo/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/RebusNeo/appsettings.json b/RebusNeo/appsettings.json new file mode 100644 index 0000000..d9d9a9b --- /dev/null +++ b/RebusNeo/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d941955 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,54 @@ +version: '3' + +services: + rebusadmin: + build: + context: . + dockerfile: RebusAdmin/Dockerfile + ports: + - "5003:5003" + expose: + - "5003" + links: + - neo4j + - rebusneo + rebusneo: + build: + context: . + dockerfile: RebusNeo/Dockerfile + ports: + - "5002:5002" + expose: + - "5002" + links: + - "rebuscore" + - "ms-sql-server" + rebuscore: + build: + context: . + dockerfile: RebusCore/Dockerfile + ports: + - "5001:5001" + expose: + - "5001" + links: + - neo4j + neo4j: + build: + context: . + dockerfile: Neo4J/Dockerfile + ports: + - "7474:7474" + - "7687:7687" + expose: + - "7687" + ms-sql-server: + image: mcr.microsoft.com/mssql/server:2017-latest-ubuntu + environment: + ACCEPT_EULA: "Y" + SA_PASSWORD: "ABC123abc" + MSSQL_PID: "Express" + ports: + - "1433:1433" + expose: + - "1433" \ No newline at end of file