Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions FlightJournal.Tests/FlightJournal.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DefineConstants>TRACE;DEBUG;CFG_DEBUG</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
Expand Down Expand Up @@ -57,8 +57,8 @@
<HintPath>..\packages\Microsoft.Data.OData.5.6.3\lib\net40\Microsoft.Data.OData.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Newtonsoft.Json, Version=11.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.11.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="RestSharp, Version=105.0.1.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
Expand Down
46 changes: 39 additions & 7 deletions FlightJournal.Tests/RouteFilters.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
using FakeItEasy;
using FlightJournal.Web;
using FlightJournal.Web.Controllers;
using FlightJournal.Web.Models;
using FlightJournal.Web.Repositories;
using FlightJournal.Web.Validators;
using Microsoft.VisualStudio.TestTools.UnitTesting;

Expand All @@ -21,11 +23,25 @@ namespace FlightJournal.Tests
[TestClass]
public class RouteFilters
{
public class TestClubRepository : IClubRepository
{
public bool ClubExists(string shortName)
{
return shortName == "ØSF" || shortName == "AASVK";
}

public Club CloneClubByShortName(string shortName)
{
return new Club() { ShortName = shortName };
}
}

[TestInitialize]
public void init()
{
var folder = System.IO.Directory.GetDirectoryRoot(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "..\\..\\..\\FlightJournal.Web\\App_Data"));
AppDomain.CurrentDomain.SetData("DataDirectory", folder.ToString());

}

[TestMethod]
Expand Down Expand Up @@ -111,10 +127,26 @@ public void Root_and_a_non_existing_club_should_set_currentclub_to_non()

[TestMethod]
[TestCategory("Routes")]
public void Root_and_a_non_existing_club_should_send_to_non_existing_controller_and_index_action()
public void Root_and_a_non_existing_club_should_send_to_pagenotfound_error_controller_and_index_action()
{
// Will send through the route pipeline and land on the controller default route (missingclub does not exist and will result in a 404)
should_return_expected_controller_and_action("~/MISSINGCLUB", "missingclub", "Index");
should_return_expected_controller_and_action("~/MISSINGCLUB", "error", "pagenotfound");
}

[TestMethod]
[TestCategory("Routes")]
public void Root_valid_controller_should_send_to_pagenotfound_error_controller_and_index_action()
{
should_return_expected_controller_and_action("~/admin", "error", "pagenotfound");
should_return_expected_controller_and_action("~/logbook", "logbook", "index");
}

[TestMethod]
[TestCategory("Routes")]
public void Root_club_valid_controller_should_send_to_pagenotfound_error_controller_and_index_action()
{
should_return_expected_controller_and_action("~/ØSF/admin", "error", "pagenotfound");
should_return_expected_controller_and_action("~/ØSF/logbook", "logbook", "index");
}

[TestMethod]
Expand All @@ -130,7 +162,7 @@ public void Root_club_and_any_date_part_should_match_currentclub()
[TestCategory("Routes")]
public void RouteConstrint_Club_validator_test()
{
var validator = new ClubValidator();
var validator = new ClubValidator(new TestClubRepository());

// Valid Country
Assert.IsTrue(validator.IsValid("ØSF"), "ØSF should be valid");
Expand Down Expand Up @@ -216,7 +248,7 @@ public RouteData GetRouteData(string path)
{
// attempting with https://gist.github.com/ExploreMqt/3719348 for getting access to route
var collection = new RouteCollection();
RouteConfig.RegisterRoutes(collection, true);
RouteConfig.RegisterRoutes(collection, new TestClubRepository(), true);

var httpContext = A.Fake<HttpContextBase>();
A.CallTo(() => httpContext.Request.AppRelativeCurrentExecutionFilePath).Returns(path);
Expand All @@ -228,7 +260,7 @@ public RouteData GetRouteData(string path)
public void should_return_expected_controller_and_action(string path, string expectedController, string expectedAction)
{
var collection = new RouteCollection();
RouteConfig.RegisterRoutes(collection, true);
RouteConfig.RegisterRoutes(collection, new TestClubRepository(), true);

var httpContext = A.Fake<HttpContextBase>();
A.CallTo(() => httpContext.Request.AppRelativeCurrentExecutionFilePath).Returns(path);
Expand All @@ -241,14 +273,14 @@ public void should_return_expected_controller_and_action(string path, string exp
public void should_return_expected_club(string path, string expectedClub)
{
var collection = new RouteCollection();
RouteConfig.RegisterRoutes(collection, true);
RouteConfig.RegisterRoutes(collection, new TestClubRepository(), true);

var httpContext = A.Fake<HttpContextBase>();
A.CallTo(() => httpContext.Request.AppRelativeCurrentExecutionFilePath).Returns(path);
A.CallTo(() => httpContext.Request.Url).Returns(new Uri("http://localhost/" + path.Replace("~/", string.Empty)));
var routeData = collection.GetRouteData(httpContext);
Assert.IsNotNull(routeData);
var currentClub = ClubController.GetCurrentClub(httpContext, routeData);
var currentClub = ClubController.GetCurrentClub(httpContext, routeData, new TestClubRepository());
Assert.IsNotNull(currentClub);
Assert.AreEqual(expectedClub, currentClub.ShortName);
}
Expand Down
2 changes: 1 addition & 1 deletion FlightJournal.Tests/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<package id="Microsoft.Data.OData" version="5.8.4" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.28" targetFramework="net45" />
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net45" />
<package id="Newtonsoft.Json" version="10.0.1" targetFramework="net472" />
<package id="Newtonsoft.Json" version="11.0.2" targetFramework="net472" />
<package id="RestSharp" version="105.0.1" targetFramework="net45" />
<package id="System.Spatial" version="5.8.4" targetFramework="net472" />
<package id="Twilio" version="3.6.27" targetFramework="net45" />
Expand Down
3 changes: 2 additions & 1 deletion FlightJournal.Web/App_Start/DbCommandInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ private void LogEndOfExec<TResult>(DbCommand command, DbCommandInterceptionConte
System.Diagnostics.Trace.TraceError($"Command {command.CommandText} failed with exception {interceptionContext.Exception}");
#if (DEBUG)
// Note that EF __Migration exception with CreatedOn is related to https://stackoverflow.com/a/20670134 and is being throwned often but captured
throw new Exception($"Command {command.CommandText} failed with exception {interceptionContext.Exception}");
if (command.CommandText != "SELECT TOP (1) \r\n [c].[CreatedOn] AS [CreatedOn]\r\n FROM [dbo].[__MigrationHistory] AS [c]")
throw new Exception($"Command {command.CommandText} failed with exception {interceptionContext.Exception}");
#endif
}
else
Expand Down
102 changes: 57 additions & 45 deletions FlightJournal.Web/App_Start/RouteConfig.cs
Original file line number Diff line number Diff line change
@@ -1,78 +1,90 @@
using System.Linq.Expressions;
using System.Security.Policy;
using FlightJournal.Web.Constraints;
using FlightJournal.Web.Repositories;
using FlightJournal.Web.Validators;
using System.Web.Mvc;
using System.Web.Routing;
using FlightJournal.Web.Constraints;
using FlightJournal.Web.Validators;

namespace FlightJournal.Web{
namespace FlightJournal.Web
{
public class RouteConfig {
public static void RegisterRoutes(RouteCollection routes, bool unittest = false) {
public static void RegisterRoutes(RouteCollection routes, IClubRepository clubRepository, bool unittest = false) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

if (!unittest)
routes.MapMvcAttributeRoutes();

//// Custom club Urls
//routes.MapRoute(
// name: "Root", // Route name
// url: "", // URL with parameters
// defaults: new { controller = "Home", action = "Index" }
//);
// Specific routes to avoid HttpException with The controller for path e.g. '/.well-known/traffic-advice' was not found or does not implement IController.
// Array of URLs to handle as 404
string[] notFoundUrls = {
"NaN-NaN-NaN/Index",
"sql/Index",
".well-known/traffic-advice",
".well-known/apple-app-site-association",
"app/Index",
"actuator;/env;",
"Admin/ashx",
"old/Index",
"root/Index",
"'123/Index",
"includ/Index",
"wordpress/Index",
"actuator/heapdump",
"apple-app-site-association/Index" // Assuming no Apple app association
};
foreach (var url in notFoundUrls) // Generate routes for known 404 URLs - avoiding validation in ClubRouteConstraint
{
routes.MapRoute(
name: url.Replace("/", "").Replace(";", ""), // Generate a unique name for each route
url: url,
defaults: new { controller = "Error", action = "PageNotFound" }
);
}

// Custom club Urls
routes.MapRoute(
"RootClub", // Route name
"{club}", // URL with parameters
new { controller = "Report", action = "Index" },
new { club = new ClubRouteConstraint(new ClubValidator()) }
name: "GetReportByClub",
url: "{club}",
defaults: new { controller = "Report", action = "Index" },
constraints: new { club = new ClubRouteConstraint(new ClubValidator(clubRepository)), controller = "Report" }
);

// Custom report url /{yyyy} or /{yyyy-MM} or startlist on date {yyyy-MM-dd}
routes.MapRoute(
"ReportingDate", // Route name
"{date}", // URL with parameters
new { controller = "Report", action = "Index" },
new { date = new DateRouteConstraint(new DatePathValidator()) }
name: "GetReportByDate",
url: "{date}",
defaults: new { controller = "Report", action = "Index" },
constraints: new { date = new DateRouteConstraint(new DatePathValidator()), controller = "Report" }
);

// Custom club Urls with custom Date
routes.MapRoute(
"ReportingClubDate", // Route name
"{club}/{date}", // URL with parameters
new { controller = "Report", action = "Index" },
new { club = new ClubRouteConstraint(new ClubValidator())
, date = new DateRouteConstraint(new DatePathValidator()) }
name: "GetReportByClubByDate",
url: "{club}/{date}",
defaults: new { controller = "Report", action = "Index" },
constraints: new { club = new ClubRouteConstraint(new ClubValidator(clubRepository)), date = new DateRouteConstraint(new DatePathValidator()), controller = "Report" }
);

//// Custom club Urls with default behavior
//routes.MapRoute(
// "LogbookWithClubFlavor", // Route name
// "{club}/logbook/{year}", // URL with parameters
// new { club = UrlParameter.Optional, controller = "Logbook", action = "Index", year = UrlParameter.Optional },
// new { club = new ClubRouteConstraint(new ClubValidator()) }
//);
//routes.MapRoute(
// "LogbookWithClubFlavor2", // Route name
// "{club}/logbook", // URL with parameters
// new { club = UrlParameter.Optional, controller = "Logbook", action = "Index"},
// new { club = new ClubRouteConstraint(new ClubValidator()) }
//);


// Custom club Urls with default behavior
routes.MapRoute(
"DefaultWithClubFlavor", // Route name
"{club}/{controller}/{action}/{id}", // URL with parameters
new { club = UrlParameter.Optional, controller = "Report", action = "Index", id = UrlParameter.Optional },
new { club = new ClubRouteConstraint(new ClubValidator()) }
name: "GetDefaultByClub", // Route name
url: "{club}/{controller}/{action}/{id}", // URL with parameters
defaults: new { club = UrlParameter.Optional, controller = "Report", action = "Index", id = UrlParameter.Optional },
constraints: new { club = new ClubRouteConstraint(new ClubValidator(clubRepository)), controller = "Report|About|Account|Club|CodePlayGround|CommentaryAdmin|CommentaryTypeAdmin|Error|Flight|GradingAdmin|Import|Language|Location|Logbook|Manage|ManouvreAdmin|Pilot|PilotStatus|Plane|Report|RolesAdmin|StartType|TrainingExerciseAdmin|TrainingLessonAdmin|TrainingLogAdmin|TrainingLogHistoryAdmin|TrainingProgramAdmin|TrainingStatus|UsersAdmin" } // Constraint to club and all known controllers
);

// Default behaviour
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Report", action = "Index", id = UrlParameter.Optional }
defaults: new { controller = "Report", action = "Index", id = UrlParameter.Optional },
constraints: new { controller = "Report|About|Account|Club|CodePlayGround|CommentaryAdmin|CommentaryTypeAdmin|Error|Flight|GradingAdmin|Import|Language|Location|Logbook|Manage|ManouvreAdmin|Pilot|PilotStatus|Plane|Report|RolesAdmin|StartType|TrainingExerciseAdmin|TrainingLessonAdmin|TrainingLogAdmin|TrainingLogHistoryAdmin|TrainingProgramAdmin|TrainingStatus|UsersAdmin" } // Constraint to all known controllers
);

// Catch-all route for unmatched URLs
routes.MapRoute(
name: "NotFound",
url: "{*url}", // Matches any unmatched URL
defaults: new { controller = "Error", action = "PageNotFound" }
);
}
}
Expand Down
39 changes: 7 additions & 32 deletions FlightJournal.Web/Controllers/ClubController.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
using System;
using FlightJournal.Web.Models;
using FlightJournal.Web.Repositories;
using System.Data;
using System.Data.Entity;
using System.IO;
using System.Linq;
using System.Runtime.Remoting.Channels;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
using FlightJournal.Web.Models;

namespace FlightJournal.Web.Controllers
{
Expand All @@ -29,9 +27,8 @@ public static Club CurrentClub
/// <param name="context"></param>
/// <param name="routeData"></param>
/// <returns></returns>
public static Club GetCurrentClub(HttpContextBase context, RouteData routeData)
public static Club GetCurrentClub(HttpContextBase context, RouteData routeData, IClubRepository clubRepository)
{

// Fetch from URL
var urlClubFilter = routeData.Values["club"] as string;

Expand Down Expand Up @@ -60,36 +57,14 @@ public static Club GetCurrentClub(HttpContextBase context, RouteData routeData)
// Read Url
if (urlClubFilter != null && !string.IsNullOrWhiteSpace(urlClubFilter))
{
Club ghost = new Club();
using (var shortDb = new FlightContext())
{
var club = shortDb.Clubs.SingleOrDefault(d => d.ShortName == urlClubFilter);
if (club != null)
{
ghost = new Club();
ghost.LocationId = club.LocationId;
ghost.Location = club.Location; // for allowing country
ghost.ContactInformation = club.ContactInformation;
ghost.ShortName = club.ShortName;
ghost.Name = club.Name;
if (club.Website != null && (club.Website.StartsWith("http://") || club.Website.StartsWith("https://")))
{
ghost.Website = club.Website;
}
else if (club.Website != null)
{
ghost.Website = "http://" + club.Website;
}
ghost.ClubId = club.ClubId;
}
}
var clubCopy = clubRepository.CloneClubByShortName(urlClubFilter);

// Set Session Cache
context.Items.Remove("CurrentClub");
context.Items.Add("CurrentClub", ghost);
context.Items.Add("CurrentClub", clubCopy);

// Return Current Club
return ghost;
return clubCopy;
}
return new Club();
}
Expand All @@ -104,7 +79,7 @@ public static Club GetCurrentClub()
return new Club();
}

return GetCurrentClub(context, routeData);
return GetCurrentClub(context, routeData, new ClubRepository(() => new FlightJournal.Web.Models.FlightContext()));
}

/// <summary>
Expand Down
Loading