Beispiel #1
0
        /// <summary>
        /// Adds a constraint instance for the given key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">
        /// The constraint instance. Must either be a string or an instance of <see cref="IRouteConstraint"/>.
        /// </param>
        /// <remarks>
        /// If the <paramref name="value"/> is a string, it will be converted to a <see cref="RegexRouteConstraint"/>.
        ///
        /// For example, the string <code>Product[0-9]+</code> will be converted to the regular expression
        /// <code>^(Product[0-9]+)</code>. See <see cref="System.Text.RegularExpressions.Regex"/> for more details.
        /// </remarks>
        public void AddConstraint(string key, object value)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            var constraint = value as IRouteConstraint;

            if (constraint == null)
            {
                var regexPattern = value as string;
                if (regexPattern == null)
                {
                    throw new RouteCreationException($"The constraint entry '{key}' - '{value}' on the route '{_displayName}' must have a string value or be of a type which implements '{typeof(IRouteConstraint)}'.");
                }

                var constraintsRegEx = "^(" + regexPattern + ")$";
                constraint = new RegexRouteConstraint(constraintsRegEx);
            }

            Add(key, constraint);
        }
        public void RegexConstraintIsCultureInsensitiveWhenConstructedWithString(string culture)
        {
            if (TestPlatformHelper.IsMono)
            {
                // The Regex in Mono returns true when matching the Turkish I for the a-z range which causes the test
                // to fail. Tracked via #100.
                return;
            }

            // Arrange
            var constraint = new RegexRouteConstraint("^([a-z]+)$");
            var values     = new RouteValueDictionary(new { controller = "\u0130" }); // Turkish upper-case dotted I

            using (new CultureReplacer(culture))
            {
                // Act
                var match = constraint.Match(
                    httpContext: new Mock <ProtoContext>().Object,
                    route: new Mock <IRouter>().Object,
                    routeKey: "controller",
                    values: values,
                    routeDirection: RouteDirection.IncomingRequest);

                // Assert
                Assert.False(match);
            }
        }
Beispiel #3
0
        /// <summary>
        /// Adds a constraint instance for the given key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">
        /// The constraint instance. Must either be a string or an instance of <see cref="IRouteConstraint"/>.
        /// </param>
        /// <remarks>
        /// If the <paramref name="value"/> is a string, it will be converted to a <see cref="RegexRouteConstraint"/>.
        ///
        /// For example, the string <code>Product[0-9]+</code> will be converted to the regular expression
        /// <code>^(Product[0-9]+)</code>. See <see cref="System.Text.RegularExpressions.Regex"/> for more details.
        /// </remarks>
        public void AddConstraint(string key, object value)
        {
            if (key == null)
            {
                throw new ArgumentNullException(nameof(key));
            }

            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            var constraint = value as IRouteConstraint;

            if (constraint == null)
            {
                var regexPattern = value as string;
                if (regexPattern == null)
                {
                    throw new RouteCreationException(
                              Resources.FormatRouteConstraintBuilder_ValidationMustBeStringOrCustomConstraint(
                                  key,
                                  value,
                                  _displayName,
                                  typeof(IRouteConstraint)));
                }

                var constraintsRegEx = "^(" + regexPattern + ")$";
                constraint = new RegexRouteConstraint(constraintsRegEx);
            }

            Add(key, constraint);
        }
        public void RegexRouteConstraintTests(string pattern, string parameterValue, bool expected)
        {
            var constraint = new RegexRouteConstraint(pattern);
            var actual     = TestValue(constraint, parameterValue);

            Assert.Equal(expected, actual);
        }
        public void ResolveConstraint_RegexConstraint()
        {
            var constraint = new DefaultInlineConstraintResolver().ResolveConstraint("regex(abc,defg)");

            RegexRouteConstraint regexConstraint = Assert.IsType <RegexRouteConstraint>(constraint);

            Assert.Equal("abc,defg", regexConstraint.Pattern);
        }
Beispiel #6
0
 private bool Match(RegexRouteConstraint contraint, RouteValueDictionary values)
 {
     return(contraint.Match(
                new DefaultHttpContext(),
                route: new Mock <IRouter>().Object,
                routeKey: "controller",
                values: values,
                routeDirection: RouteDirection.IncomingRequest));
 }
        public void RegexConstraintFailsIfKeyIsNotFoundInRouteValues()
        {
            // Arrange
            var constraint = new RegexRouteConstraint(new Regex("^abc$"));
            var values     = new RouteValueDictionary(new { action = "abc" });

            // Assert
            Assert.False(EasyMatch(constraint, "controller", values));
        }
        public void RegexConstraintConstructedWithRegex_SimpleFailedMatch()
        {
            // Arrange
            var constraint = new RegexRouteConstraint(new Regex("^abc$"));
            var values     = new RouteValueDictionary(new { controller = "Abc" });

            // Assert
            Assert.False(EasyMatch(constraint, "controller", values));
        }
        public void RegexConstraint_TakesRegexAsInput_SimpleMatch()
        {
            // Arrange
            var constraint = new RegexRouteConstraint(new Regex("^abc$"));
            var values     = new RouteValueDictionary(new { controller = "abc" });

            // Assert
            Assert.True(EasyMatch(constraint, "controller", values));
        }
Beispiel #10
0
        /// <summary>
        ///
        /// </summary>
        /// <param name="apiDesc"></param>
        /// <param name="targetApiVersion"></param>
        /// <returns></returns>
        private static bool ResolveVersionSupportByRouteConstraint(ApiDescription apiDesc, string targetApiVersion)
        {
            RegexRouteConstraint versionConstraint = (apiDesc.Route.Constraints.ContainsKey("apiVersion"))
                    ? apiDesc.Route.Constraints["apiVersion"] as RegexRouteConstraint
                    : null;

            return((versionConstraint == null)
                ? false
                : versionConstraint.Pattern.Split('|').Contains(targetApiVersion));
        }
        [InlineData("Abc", " abc", false)]  // Missing char
        public void RegexConstraintBuildRegexVerbatimFromInput(string routeValue,
                                                               string constraintValue,
                                                               bool shouldMatch)
        {
            // Arrange
            var constraint = new RegexRouteConstraint(constraintValue);
            var values     = new RouteValueDictionary(new { controller = routeValue });

            // Assert
            Assert.Equal(shouldMatch, EasyMatch(constraint, "controller", values));
        }
Beispiel #12
0
        public void RegexConstraint_TakesRegexAsInput_SimpleMatch()
        {
            // Arrange
            var constraint = new RegexRouteConstraint(new Regex("^abc$"));
            var values     = new RouteValueDictionary(new { controller = "abc" });

            // Act
            var match = constraint.Match(
                httpContext: Mock.Of <HttpContext>(),
                route: new Mock <IRouter>().Object,
                routeKey: "controller",
                values: values,
                routeDirection: RouteDirection.IncomingRequest);

            // Assert
            Assert.True(match);
        }
        public void RegexConstraintFailsIfKeyIsNotFoundInRouteValues()
        {
            // Arrange
            var constraint = new RegexRouteConstraint(new Regex("^abc$"));
            var values     = new RouteValueDictionary(new { action = "abc" });

            // Act
            var match = constraint.Match(
                new DefaultProtoContext(),
                route: new Mock <IRouter>().Object,
                routeKey: "controller",
                values: values,
                routeDirection: RouteDirection.IncomingRequest);

            // Assert
            Assert.False(match);
        }
        public void RegexConstraintConstructedWithRegex_SimpleFailedMatch()
        {
            // Arrange
            var constraint = new RegexRouteConstraint(new Regex("^abc$"));
            var values     = new RouteValueDictionary(new { controller = "Abc" });

            // Act
            var match = constraint.Match(
                new DefaultProtoContext(),
                route: new Mock <IRouter>().Object,
                routeKey: "controller",
                values: values,
                routeDirection: RouteDirection.IncomingRequest);

            // Assert
            Assert.False(match);
        }
        BuildConstraintsCore(IDictionary <string, object> inputConstraints, string routeTemplate)
        {
            if (inputConstraints == null || inputConstraints.Count == 0)
            {
                return(null);
            }

            var constraints = new Dictionary <string, IRouteConstraint>(inputConstraints.Count,
                                                                        StringComparer.OrdinalIgnoreCase);

            foreach (var kvp in inputConstraints)
            {
                var constraint = kvp.Value as IRouteConstraint;

                if (constraint == null)
                {
                    var regexPattern = kvp.Value as string;

                    if (regexPattern == null)
                    {
                        if (routeTemplate != null)
                        {
                            throw new InvalidOperationException(
                                      Resources.FormatTemplateRoute_ValidationMustBeStringOrCustomConstraint(
                                          kvp.Key, routeTemplate, typeof(IRouteConstraint)));
                        }
                        else
                        {
                            throw new InvalidOperationException(
                                      Resources.FormatGeneralConstraints_ValidationMustBeStringOrCustomConstraint(
                                          kvp.Key, typeof(IRouteConstraint)));
                        }
                    }

                    var constraintsRegEx = "^(" + regexPattern + ")$";

                    constraint = new RegexRouteConstraint(constraintsRegEx);
                }

                constraints.Add(kvp.Key, constraint);
            }

            return(constraints);
        }
        [InlineData(@"*****@*****.**", @"^\w+[\w\.]*\@\w+((-\w+)|(\w*))\.[a-z]{2,3}$", true)] // email
        public void RegexConstraintBuildRegexVerbatimFromInput(
            string routeValue,
            string constraintValue,
            bool shouldMatch)
        {
            // Arrange
            var constraint = new RegexRouteConstraint(constraintValue);
            var values     = new RouteValueDictionary(new { controller = routeValue });

            // Act
            var match = constraint.Match(
                new DefaultProtoContext(),
                route: new Mock <IRouter>().Object,
                routeKey: "controller",
                values: values,
                routeDirection: RouteDirection.IncomingRequest);

            // Assert
            Assert.Equal(shouldMatch, match);
        }
Beispiel #17
0
        /// <summary>
        /// Adds a constraint instance for the given key.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <param name="value">
        /// The constraint instance. Must either be a string or an instance of <see cref="IRouteConstraint"/>.
        /// </param>
        /// <remarks>
        /// If the <paramref name="value"/> is a string, it will be converted to a <see cref="RegexRouteConstraint"/>.
        ///
        /// For example, the string <code>Product[0-9]+</code> will be converted to the regular expression
        /// <code>^(Product[0-9]+)</code>. See <see cref="System.Text.RegularExpressions.Regex"/> for more details.
        /// </remarks>
        public void AddConstraint([NotNull] string key, [NotNull] object value)
        {
            var constraint = value as IRouteConstraint;

            if (constraint == null)
            {
                var regexPattern = value as string;
                if (regexPattern == null)
                {
                    throw new InvalidOperationException(
                              Resources.FormatRouteConstraintBuilder_ValidationMustBeStringOrCustomConstraint(
                                  key,
                                  value,
                                  _displayName,
                                  typeof(IRouteConstraint)));
                }

                var constraintsRegEx = "^(" + regexPattern + ")$";
                constraint = new RegexRouteConstraint(constraintsRegEx);
            }

            Add(key, constraint);
        }
        public void RegexConstraintIsCultureInsensitiveWhenConstructedWithString()
        {
            if (TestPlatformHelper.IsMono)
            {
                // The Regex in Mono returns true when matching the Turkish I for the a-z range which causes the test
                // to fail. Tracked via #100.
                return;
            }

            // Arrange
            var constraint = new RegexRouteConstraint("^([a-z]+)$");
            var values     = new RouteValueDictionary(new { controller = "\u0130" }); // Turkish upper-case dotted I

            var currentThread = Thread.CurrentThread;
            var backupCulture = currentThread.CurrentCulture;

            bool matchInTurkish;
            bool matchInUsEnglish;

            // Act
            try
            {
                currentThread.CurrentCulture = new CultureInfo("tr-TR"); // Turkish culture
                matchInTurkish = EasyMatch(constraint, "controller", values);

                currentThread.CurrentCulture = new CultureInfo("en-US");
                matchInUsEnglish             = EasyMatch(constraint, "controller", values);
            }
            finally
            {
                currentThread.CurrentCulture = backupCulture;
            }

            // Assert
            Assert.False(matchInUsEnglish); // this just verifies the test
            Assert.False(matchInTurkish);
        }
Beispiel #19
0
        public static void RegisterRoutes(this IRouteBuilder routes)
        {
            //MIGRATION: Implement middleware to redirect users to lower case URLs
            //routes.LowercaseUrls = true;

            //routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

            #region Constraints

            const string formats   = "rss|atom";
            const string postKinds = "Stories|Releases|Factsheets|Updates";

            //Aug. 3/2018
            //const string categories = "Ministries|Sectors|Regions|Tags|Themes";

            //The line above has been updated. During the News work in Fall 2017 (BC Gov news public web api) and move to OpenShift
            //Regions were not implemented in the application as they were never used. Therefore to prohibit the appliation throwing
            //errors on routes containing regions, it is being removed fromt he routes.

            //If regions are reimplemented in Repository.cs and the Web API, put them back in the route table.
            const string categories = "Ministries|Sectors|Tags|Themes|News-Subscribe";

            IRouteConstraint yearConstraint               = new RegexRouteConstraint(@"^\d{4}$");
            IRouteConstraint monthConstraint              = new RegexRouteConstraint(@"^(\d{2})?$");
            IRouteConstraint formatConstraint             = new OptionalRouteConstraint(new RegexRouteConstraint(formats));
            IRouteConstraint postKindConstraint           = new OptionalRouteConstraint(new RegexRouteConstraint(postKinds));
            IRouteConstraint categoryControllerConstraint = new RegexRouteConstraint(categories);

            #endregion

            //context.MapRoute(
            //    "Newsroom_default",
            //    "Newsroom/{controller}/{action}/{id}",
            //    new { action = "Index", id = UrlParameter.Optional }
            //);

            //#region Files
            routes.MapRoute(
                name: "Newsroom-Files",
                template: "files/{*path}",
                defaults: new { controller = "Files", action = "Single" }
                );

            //#endregion

            #region Assets

            routes.MapRoute(
                name: "Newsroom-Assets-License",
                template: "assets/license",
                defaults: new { controller = "Assets", action = "License" }
                );

            routes.MapRoute(
                name: "Newsroom-Assets",
                template: "assets/{*path}",
                defaults: new { controller = "Assets", action = "Single" }
                );

            #endregion

            #region Default

            /* The Default controller supports the following URL patterns:
             *
             *   index
             *   more-news
             *   (feed | embed [format])
             *   archive [year [month]]
             *   (top | feature) feed [format]
             *   biography
             *
             */

            routes.MapRoute(
                name: "NewsroomReference",
                template: "{reference}",
                defaults: new { controller = "Default", action = "Reference" },
                constraints: new { reference = @"\d+" }
                );

            //index
            routes.MapRoute(
                name: "Newsroom",
                template: "",
                defaults: new { controller = "Default", action = "Index" }
                );

            routes.MapRoute(
                name: "Newsroom-Sitemap",
                template: "sitemap/html",
                defaults: new { controller = "Default", action = "Sitemap" }
                );

            routes.MapRoute(
                name: "Newsroom-SiteStatus",
                template: "SiteStatus",
                defaults: new { controller = "Default", action = "SiteStatus" }
                );

            routes.MapRoute(
                name: "CarouselImage",
                template: "CarouselImage/{slideId}",
                defaults: new { controller = "Default", action = "CarouselImage" }
                );

            //more-news
            routes.MapRoute(
                name: "Newsroom-MoreNews",
                template: "more-news",
                defaults: new { controller = "Default", action = "MoreNews" }
                );

            //archive [year [month]]
            {
                routes.MapRoute(
                    name: "Newsroom-Archive",
                    template: "archive",
                    defaults: new { controller = "Default", action = "Archive" }
                    );

                routes.MapRoute(
                    name: "Newsroom-Archive-Month",
                    template: "archive/{year}/{month?}",
                    defaults: new { controller = "Default", action = "Month" },
                    constraints: new { year = yearConstraint, month = monthConstraint }
                    );
            }

            //feed | embed [format]
            routes.MapRoute(
                name: "Newsroom-Action",
                template: "{action}/{format?}",
                defaults: new { controller = "Default" },
                constraints: new { action = "Feed|Embed", format = formatConstraint }
                );

            //(top | feature) feed [format]
            routes.MapRoute(
                name: "Newsroom-Special-Action",
                template: "{action}/{type}/{format?}",
                defaults: new { controller = "Default" },
                constraints: new { action = "Top|Feature", format = formatConstraint }
                );

            //(top | feature) feed [format]
            routes.MapRoute(
                name: "Newsroom-Special-Action-Feed",
                template: "{action}/{key}/{type}/{format?}",
                defaults: new { controller = "Default" },
                constraints: new { action = "CategoryTop|CategoryFeature", format = formatConstraint }
                );

            #endregion

            //biographies key
            MapRouteOptional(
                routes: routes,
                name: "Newsroom[-Parent]-Biography",
                template: "ministries[/{parentKey}]/{key}/biography",
                defaults: new { controller = "Ministries", action = "Biography" },
                constraints: null
                );

            #region Ministries, Regions, Sectors, Tags, Themes

            /* The Categories controller supports the following URL patterns:
             *
             *   category [parentKey] [postKind]
             *   category [parentKey] key [postKind]
             *   category [parentKey] key [postKind] more-news
             *   category [parentKey] key [postKind] (feed | embed [format])
             *   category [parentKey] key [postKind] archive [year [month]]
             *
             */

            //category key [postKind] more-news
            MapRouteOptional(
                routes: routes,
                name: "Newsroom[-Parent]-Category[-Type]-More",
                template: "{category}[/{parentKey}]/{key}[/{postKind?}]/more-news",
                defaults: new { controller = "Category", action = "MoreNews" },
                constraints: new { category = categoryControllerConstraint, postKind = postKindConstraint }
                );

            MapRouteOptional(
                routes: routes,
                name: "Newsroom[-Parent]-Category[-Type]-MoreArticles",
                template: "{category}[/{parentKey}]/{key}[/{postKind?}]/morearticles",
                defaults: new { controller = "Category", action = "MoreArticles" },
                constraints: new { category = categoryControllerConstraint, postKind = postKindConstraint }
                );

            //category key [postKind] archive [year [month]]
            {
                //i.e. category key [postKind] archive
                MapRouteOptional(
                    routes: routes,
                    name: "Newsroom[-Parent]-Category[-Type]-Archive",
                    template: "{category}[/{parentKey}]/{key}[/{postKind?}]/archive",
                    defaults: new { controller = "Category", action = "Archive" },
                    constraints: new { category = categoryControllerConstraint, postKind = postKindConstraint }
                    );

                //i.e. category key [postKind] archive year [month]
                MapRouteOptional(
                    routes: routes,
                    name: "Newsroom[-Parent]-Category[-Type]-Archive-Year",
                    template: "{category}[/{parentKey}]/{key}[/{postKind?}]/archive/{year}/{month?}",
                    defaults: new { controller = "Category", action = "Month" },
                    constraints: new { category = categoryControllerConstraint, year = yearConstraint, month = monthConstraint, postKind = postKindConstraint }
                    );
            }

            //category [parent] key [postKind] (feed | embed [format])
            MapRouteOptional(
                routes: routes,
                name: "Newsroom[-Parent]-Category[-Type]-Action",
                template: "{category}[/{parentKey}]/{key}[/{postKind?}]/{action}/{format?}",
                defaults: new { controller = "Category" },
                constraints: new { category = categoryControllerConstraint, postKind = postKindConstraint, action = "Feed|Embed", format = formatConstraint }
                );

            //category [parent] key [postKind]
            MapRouteOptional(
                routes: routes,
                name: "Newsroom[-Parent]-Category-Type",
                template: "{category}[/{parentKey}]/{key}/{postKind?}",
                defaults: new { controller = "Category", action = "Details" },
                constraints: new { category = categoryControllerConstraint, postKind = postKindConstraint }
                );

            //TODO: Determine if [postKind] should be present
            //category [postKind]
            routes.MapRoute(
                name: "Newsroom-Category-Index-Type",
                template: "{category}/{postKind?}",
                defaults: new { controller = "Category", action = "Index" },
                constraints: new { category = categoryControllerConstraint, postKind = postKindConstraint }
                );

            routes.MapRoute(
                name: "News-Subscribe-Index-Type",
                template: "news-subscribe/{category}/{postKind?}",
                defaults: new { controller = "Category", action = "Index" },
                constraints: new { category = categoryControllerConstraint, postKind = postKindConstraint }
                );


            #endregion

            //#region Tags

            ///* The Tags controller supports the following URL patterns:
            // *
            // *   Tag key [postKind] (feed | embed) [format]
            // *
            // */

            ////Tag key [postKind] (feed | embed) [format]
            //MapRouteOptional(
            //    context: context,
            //    name: "Newsroom-Tags[-Type]-Action",
            //    template: "Tags/{key}[/{postKind}]/{action}/{format}",
            //    defaults: new { controller = "Tags", format = UrlParameter.Optional },
            //    constraints: new { postKind = postKindConstraint, action = "Feed|Embed", format = formatConstraint }
            //);

            //#endregion

            #region Stories, Releases, Factsheets, Updates

            foreach (string postKind in postKinds.Split(","))
            {
                /* The Posts controller supports the following URL patterns:
                 *
                 *   postKind
                 *   postKind more-news
                 *   postKind (feed | embed [format])
                 *   postKind archive [year [month]]
                 *   postKind key
                 *
                 */

                //postKind
                routes.MapRoute(
                    name: "Newsroom-Type-Index" + "-" + postKind,
                    template: "{postKind}",
                    defaults: new { controller = "Posts", postKind = postKind.ToLower(), action = "Index" },
                    constraints: new { postKind = postKind }
                    );

                //postKind more-news
                routes.MapRoute(
                    name: "Newsroom-Type-More" + "-" + postKind,
                    template: "{postKind}/more-news",
                    defaults: new { controller = "Posts", postKind = postKind.ToLower(), action = "MoreNews" },
                    constraints: new { postKind = postKind }
                    );

                routes.MapRoute(
                    name: "Newsroom-Type-MoreArticles" + "-" + postKind,
                    template: "{postKind}/MoreArticles",
                    defaults: new { controller = "Posts", postKind = postKind.ToLower(), action = "MoreArticles" },
                    constraints: new { postKind = postKind }
                    );


                //postKind (feed | embed [format])
                routes.MapRoute(
                    name: "Newsroom-Type-Stories-Action" + "-" + postKind,
                    template: "{postKind}/{action}/{format?}",
                    defaults: new { controller = "Posts", postKind = postKind.ToLower() },
                    constraints: new { postKind = postKind, action = "Feed|Embed" }
                    );

                //postKind archive [year [month]]
                {
                    //i.e. postKind archive
                    routes.MapRoute(
                        name: "Newsroom-Type-Archive" + "-" + postKind,
                        template: "{postKind}/archive",
                        defaults: new { controller = "Posts", postKind = postKind.ToLower(), action = "Archive" },
                        constraints: new { postKind = postKind }
                        );

                    //i.e. postKind archive year [month]
                    routes.MapRoute(
                        name: "Newsroom-Type-Archive-Year" + "-" + postKind,
                        template: "{postKind}/archive/{year}/{month?}",
                        defaults: new { controller = "Posts", postKind = postKind.ToLower(), action = "Month" },
                        constraints: new { postKind = postKind, year = yearConstraint, month = monthConstraint }
                        );
                }

                //postKind key
                routes.MapRoute(
                    name: "Newsroom-Type-Details" + "-" + postKind,
                    template: "{postKind}/{key}",
                    defaults: new { controller = "Posts", postKind = postKind.ToLower(), action = "Details" },
                    constraints: new { postKind = postKind }
                    );

                //return the image via our server.
                routes.MapRoute(
                    name: "Newsroom-Type-Details-Image" + "-" + postKind,
                    template: "{postKind}/{key}/image",
                    defaults: new { controller = "Posts", postKind = postKind.ToLower(), action = "Image" },
                    constraints: new { postKind = postKind }
                    );
            }
            #endregion

            ////controller minstryKey key
            //context.MapRoute(
            //    name: "Newsroom-Biography",
            //    template: "{controller}/{ministryKey}/{key}",
            //    defaults: new { action = "Details" },
            //    constraints: new { controller = categoryControllerConstraint }
            //);

            #region Office of the Premier - Speeches

            /* The Categories controller supports the following URL patterns:
             *
             *   category [parentKey] [postKind]
             *   category [parentKey] key [postKind]
             *   category [parentKey] key [postKind] more-news
             *   category [parentKey] key [postKind] (feed | embed [format])
             *   category [parentKey] key [postKind] archive [year [month]]
             *
             */

            //category key [postKind] more-news
            routes.MapRoute(
                name: "Newsroom-Premier-Speeches-More",
                template: "office-of-the-premier/speeches/more-news",
                defaults: new { controller = "Category", category = "tags", action = "MoreNews", key = "speeches" },
                constraints: new { category = categoryControllerConstraint }
                );

            //category key [postKind] archive [year [month]]
            {
                //i.e. category key [postKind] archive
                routes.MapRoute(
                    name: "Newsroom-Premier-Speeches-Archive",
                    template: "office-of-the-premier/speeches/archive",
                    defaults: new { controller = "Category", category = "tags", action = "Archive", key = "speeches" },
                    constraints: new { category = categoryControllerConstraint }
                    );

                //i.e. category key [postKind] archive year [month]
                routes.MapRoute(
                    name: "Newsroom-Premier-Speeches-Archive-Year",
                    template: "office-of-the-premier/speeches/archive/{year}/{month?}",
                    defaults: new { controller = "Category", category = "tags", key = "speeches", action = "Month" },
                    constraints: new { year = yearConstraint, month = monthConstraint }
                    );
            }

            //category key [postKind] (feed | embed [format])
            routes.MapRoute(
                name: "Newsroom-Premier-Speeches-Action",
                template: "office-of-the-premier/speeches/{action}/{format?}",
                defaults: new { controller = "Category", category = "tags", key = "speeches" },
                constraints: new { action = "Feed|Embed", format = formatConstraint, category = categoryControllerConstraint }
                );

            routes.MapRoute(
                name: "Newsroom-Premier-Speeches-Action-MoreArticles",
                template: "office-of-the-premier/speeches/morearticles",
                defaults: new { controller = "Category", category = "tags", key = "speeches", action = "MoreArticles" },
                constraints: new { category = categoryControllerConstraint }
                );

            routes.MapRoute(
                name: "Newsroom-Premier-Speeches",
                template: "office-of-the-premier/speeches",
                defaults: new { controller = "Category", category = "tags", key = "speeches", action = "Details" },
                constraints: new { category = categoryControllerConstraint }
                );

            #endregion

            #region Office of the Premier

            /* The Categories controller supports the following URL patterns:
             *
             *   office-of-the-premier biography
             *   office-of-the-premier [postKind]
             *   office-of-the-premier [postKind] more-news
             *   office-of-the-premier [postKind] (feed | embed [format])
             *   office-of-the-premier [postKind] archive [year [month]]
             *
             */

            MapRouteOptional(
                routes: routes,
                name: "Newsroom-Premier-Biography",
                template: "office-of-the-premier/biography",
                defaults: new { controller = "Ministries", action = "Biography", key = "office-of-the-premier" },
                constraints: null
                );

            //category key [postKind] more-news
            MapRouteOptional(
                routes: routes,
                name: "Newsroom-Premier[-Type]-More",
                template: "office-of-the-premier[/{postKind?}]/more-news",
                defaults: new { controller = "Category", category = "ministries", key = "office-of-the-premier", action = "MoreNews" },
                constraints: new { postKind = postKindConstraint }
                );

            MapRouteOptional(
                routes: routes,
                name: "Newsroom-Premier[-Type]-MoreArticles",
                template: "office-of-the-premier[/{postKind?}]/morearticles",
                defaults: new { controller = "Category", category = "ministries", key = "office-of-the-premier", action = "MoreArticles" },
                constraints: new { postKind = postKindConstraint }
                );

            //category key [postKind] archive [year [month]]
            {
                //i.e. category key [postKind] archive
                MapRouteOptional(
                    routes: routes,
                    name: "Newsroom-Premier[-Type]-Archive",
                    template: "office-of-the-premier[/{postKind?}]/archive",
                    defaults: new { controller = "Category", category = "ministries", key = "office-of-the-premier", action = "Archive" },
                    constraints: new { postKind = postKindConstraint }
                    );

                //i.e. category key [postKind] archive year [month]
                MapRouteOptional(
                    routes: routes,
                    name: "Newsroom-Premier[-Type]-Archive-Year",
                    template: "office-of-the-premier[/{postKind?}]/archive/{year}/{month?}",
                    defaults: new { controller = "Category", category = "ministries", key = "office-of-the-premier", action = "Month" },
                    constraints: new { year = yearConstraint, month = monthConstraint, postKind = postKindConstraint }
                    );
            }

            //category key [postKind] (feed | embed [format])
            MapRouteOptional(
                routes: routes,
                name: "Newsroom-Premier[-Type]-Action",
                template: "office-of-the-premier[/{postKind?}]/{action}/{format?}",
                defaults: new { controller = "Category", category = "ministries", key = "office-of-the-premier" },
                constraints: new { postKind = postKindConstraint, action = "Feed|Embed", format = formatConstraint }
                );

            MapRouteOptional(
                routes: routes,
                name: "Newsroom-Premier-Type",
                template: "office-of-the-premier/{postKind?}",
                defaults: new { controller = "Category", category = "ministries", key = "office-of-the-premier", action = "Details" },
                constraints: new { postKind = postKindConstraint }
                );

            #endregion

            routes.MapRoute(
                name: "Newsroom-Page",
                template: "{action}",
                defaults: new { controller = "Default" },
                constraints: new { action = "Connect|Search|Privacy|Live|Contact|Contacts|Error" }
                );

            routes.MapRoute(
                name: "Subscribe",
                template: "subscribe/{action}",
                defaults: new { controller = "Subscribe", action = "Index" },
                constraints: new { action = "Index|Manage|Save|Renew" }
                );

            //context.MapRoute(
            //    name: "Newsroom-Page",
            //    template: "{key}",
            //    defaults: new { controller = "Page", action = "Single" }
            //);

            //routes.MapRoute(
            //    name: "Newsroom-NotFound",
            //    template: "{*path}",
            //    defaults: new { controller = "Default", action = "NotFound" }
            //);

            //TODO: Apply only to Index controllers

            routes.MapRoute(
                name: "Newsletters",
                template: "newsletters",
                defaults: new { controller = "Newsletters", action = "Index" }
                );

            routes.MapRoute(
                name: "Editions",
                template: "newsletters/{newsletterKey}",
                defaults: new { controller = "Newsletters", action = "Editions" }
                );

            routes.MapRoute(
                name: "GetBinaryByGuid",
                template: "newsletters/{type}/{guid}",
                defaults: new { controller = "Newsletters", action = "GetBinaryByGuid" },
                constraints: new { type = "file|image" }
                );

            routes.MapRoute(
                name: "Edition",
                template: "newsletters/{newsletterKey}/{editionKey}",
                defaults: new { controller = "Newsletters", action = "Edition" }
                );

            routes.MapRoute(
                name: "GetArticle",
                template: "newsletters/{newsletterKey}/{editionKey}/{articleKey}",
                defaults: new { controller = "Newsletters", action = "GetArticle" }
                );

            routes.MapRoute(
                name: "Default",
                template: "{controller}/{action}",
                defaults: new { },
                constraints: new { action = "MoreArticles" }
                );

            routes.MapRoute(
                name: "NotFound",
                template: "{*path}",
                defaults: new { controller = "Default", action = "NotFound" }
                //TEST: Why was the namespaces parameter set?
                //namespaces: new[] { "Gov.News.Website.Controllers" }
                );
        }
 private static void ApplyRegexRouteConstraint(OpenApiSchema schema, RegexRouteConstraint regexRouteConstraint)
 => schema.Pattern = regexRouteConstraint.Constraint.ToString();