Beispiel #1
0
        public void ProcessRequest(HttpContextBase context)
        {
            context.Response.ContentType = "application/json";
            context.Response.ContentEncoding = Encoding.UTF8;

            // Great article on verbs to use in API design http://stackoverflow.com/questions/2001773/understanding-rest-verbs-error-codes-and-authentication/2022938#2022938
            ApiRequest apiRequest;
            using (var apiRequestBuilder = new ApiRequestBuilder())
            {
                apiRequest = apiRequestBuilder.Build(context.Request);
            }

            var apiResponse = new ApiResponse();

            if (!string.IsNullOrEmpty(apiRequest.ErrorMessage))
            {
                using (var apiResponseBuilder = new ApiResponseBuilder())
                {
                    apiResponse = apiResponseBuilder.WithBody(
                            string.Format(apiRequest.ErrorMessage))
                            .WithHttpStatusCode(apiRequest.ErrorMessageHttpStatusCode);
                }
            }
            else
            {
                switch (apiRequest.HttpMethod)
                {
                    case "GET":
                        apiResponse = ProcessApiRequest.ProcessGetRequest(apiRequest);
                        break;
                    case "PUT":
                        apiResponse = ProcessApiRequest.ProcessPutRequest(apiRequest);
                        break;
                }

                if (apiResponse == null)
                {
                    using (var apiResponseBuilder = new ApiResponseBuilder())
                    {
                        apiResponse = apiResponseBuilder.WithBody(
                                string.Format("Error: API GET request returned NULL"))
                                .WithHttpStatusCode(HttpStatusCode.InternalServerError);
                    }
                }
            }
            context.Response.StatusCode = apiResponse.HttpStatusCode;
            context.Response.Write(apiResponse.Body);
        }
Beispiel #2
0
        /// <summary>
        /// Responsible for processing all HTTP GET requests in to the femah API. Receives an ApiRequest object, orchestrates and hands off to 
        /// the ApiResponseBuilder class to create the ApiResponse object.
        /// </summary>
        /// <param name="apiRequest" type="ApiRequest">A custom request object built predominantly from the http context request object, contains everything we need to route the API request appropriately.</param>
        /// <returns type="ApiResponse">The complete response to the GET request from the API, including body and HTTP status code.</returns>
        public static ApiResponse ProcessGetRequest(ApiRequest apiRequest)
        {
            using (var apiResponseBuilder = new ApiResponseBuilder())
            {
                //Example GET: http://example.com/femah.axd/api/featureswitches - Lists all Feature Switches FeatureSwitching.AllFeatures()
                //Example GET: http://example.com/femah.axd/api/featureswitches/improvedcheckout - Retrieve the Feature Switch ImprovedCheckout
                //Example GET: http://example.com/femah.axd/api/featureswitchtypes - Lists all Feature Switch Types FeatureSwitching.AllSwitchTypes()
                ApiResponse response;
                switch (apiRequest.Service)
                {
                    case ApiRequest.ApiService.featureswitches:
                        if (string.IsNullOrEmpty(apiRequest.Parameter))
                            response = apiResponseBuilder.CreateWithFeatureSwitches(Femah.AllFeatures());
                        else
                        {
                            var featureSwitch = Femah.GetFeature(apiRequest.Parameter);
                            if (featureSwitch != null)
                            {
                                response = apiResponseBuilder.CreateWithFeatureSwitches(featureSwitch);
                                break;
                            }
                            response = apiResponseBuilder.WithBody(String.Empty).WithHttpStatusCode(HttpStatusCode.NotFound);
                        }
                        break;
                    case ApiRequest.ApiService.featureswitchtypes:
                        if (!string.IsNullOrEmpty(apiRequest.Parameter))
                        {
                            response = apiResponseBuilder.WithBody(
                                String.Format("Error: Service '{0}' does not support parameter querying.", apiRequest.Service))
                                .WithHttpStatusCode(HttpStatusCode.MethodNotAllowed);
                            break;
                        }

                        response = apiResponseBuilder.CreateWithFeatureSwitchTypes(Femah.AllSwitchTypes());
                        break;
                    default:
                        response = apiResponseBuilder.WithBody(
                            String.Format("Error: '{0}' is not a valid Femah API service.", apiRequest.Service))
                            .WithHttpStatusCode(HttpStatusCode.MethodNotAllowed);
                        break;
                }
                return response;
            }
        }
Beispiel #3
0
        /// <summary>
        /// Responsible for processing all HTTP PUT requests in to the femah API. Receives an ApiRequest object, orchestrates and hands off to 
        /// the ApiResponseBuilder class to create the ApiResponse object.
        /// </summary>
        /// <param name="apiRequest" type="ApiRequest">A custom request object built predominantly from the http context request object, contains everything we need to route the API request appropriately.</param>
        /// <returns type="ApiResponse">The complete response to the PUT request from the API, including body and HTTP status code.</returns>
        public static ApiResponse ProcessPutRequest(ApiRequest apiRequest)
        {
            using (var apiResponseBuilder = new ApiResponseBuilder())
            {
                ApiResponse response = null;

                if (String.IsNullOrEmpty(apiRequest.Parameter))
                {
                    response = apiResponseBuilder.WithBody(
                            String.Format("Error: Service '{0}' requires a parameter to be passed. Url must match the format /femah.axd/api/[service]/[parameter].", apiRequest.Service))
                            .WithHttpStatusCode(HttpStatusCode.MethodNotAllowed);
                    return response;
                }
                switch (apiRequest.Service)
                {
                    //Example PUT: http://example.com/femah.axd/api/featureswitches/TestFeatureSwitch1 - Update a single Feature Switch (IsEnabled, SetCustomAttributes, FeatureType)
                    case ApiRequest.ApiService.featureswitches:
                        var featureSwitch = DeSerialiseJsonBodyToFeatureSwitch(apiRequest.Body);
                        if (featureSwitch != null)
                            response = apiResponseBuilder.CreateWithUpdatedFeatureSwitch(featureSwitch);
                        else
                        {
                            response = apiResponseBuilder.WithBody("Error: Unable to deserialise the request body.  Either the JSON is invalid or the supplied 'FeatureType' value is incorrect, have you used the AssemblyQualifiedName as the 'FeatureType' in the request?")
                            .WithHttpStatusCode(HttpStatusCode.BadRequest);
                        }

                        break;
                    default:
                        response = apiResponseBuilder.WithBody(
                            String.Format("Error: Service '{0}' does not support parameter querying.", apiRequest.Service))
                            .WithHttpStatusCode(HttpStatusCode.MethodNotAllowed);
                        break;
                }
                return response;
            }
        }
Beispiel #4
0
        public void AssertApiResponseBuilderCallsUpdateOnFeatureTypeStateIsEnabledStateAndAttributes()
        {
            //Arrange
            const string validFeatureType = "Femah.Core.FeatureSwitchTypes.PercentageFeatureSwitch, Femah.Core, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null";
            //const string jsonResponse = "{\"PercentageOn\":75,\"Description\":\"A simple feature switch that is on for a set percentage of users. The state of the switch is persisted in the user's cookies.If no cookie exists the state is chosen at random (weighted according to the percentage), and then stored in a cookie.\",\"ConfigurationInstructions\":\"Set PercentageOn to the percentage of users who should see this feature. Eg. 10 means the feature is on for 10% of users.\",\"IsEnabled\":true,\"Name\":\"TestFeatureSwitch1\",\"FeatureType\":\"Femah.Core.FeatureSwitchTypes.PercentageFeatureSwitch, Femah.Core, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null\"}";

            var currentFeatureSwitchState = new PercentageFeatureSwitch
            {
                Name = "TestFeatureSwitch1",
                FeatureType = validFeatureType,
                IsEnabled = true,
                PercentageOn = 50
            };

            var desiredFeatureSwitchState = new PercentageFeatureSwitch
            {
                Name = "TestFeatureSwitch1",
                FeatureType = validFeatureType,
                IsEnabled = true,
                PercentageOn = 75
            };

            var providerMock = new Mock<IFeatureSwitchProvider>();
            providerMock.Setup(p => p.Get("TestFeatureSwitch1"))
                .Returns(currentFeatureSwitchState);

            var femahMock = new Mock<Femah>();
            //femahMock.Setup(f => f)
            //TODO: Can't currently mock Femah easily, seeing as it's both sealed and the methods we're interested in are internal static, thoughts?

            Femah.Configure()
                .FeatureSwitchEnum(typeof(FeatureSwitches))
                .Provider(providerMock.Object)
                .Initialise();

            //Act
            ApiResponse apiResponse;
            using (var apiResponseBuilder = new ApiResponseBuilder())
            {
                apiResponse = apiResponseBuilder.CreateWithUpdatedFeatureSwitch(desiredFeatureSwitchState);
            }

            //Assert
            //Assert.AreEqual((int)HttpStatusCode.OK, apiResponse.HttpStatusCode);
            //Assert.AreEqual(jsonResponse, apiResponse.Body);
        }
Beispiel #5
0
        public void ApiResponseBuilderSetsHttpStatusCodeTo304IfPutRequestBodyIsValidJsonButFeatureSwitchHasNoChanges()
        {
            //Arrange
            const string validFeatureType = "Femah.Core.FeatureSwitchTypes.SimpleFeatureSwitch, Femah.Core, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null";
            string jsonRequestAndResponse = string.Format(
                    "{{\"IsEnabled\":true,\"Name\":\"TestFeatureSwitch1\",\"FeatureType\":\"{0}\",\"Description\":\"Define a short description of the feature switch type here.\",\"ConfigurationInstructions\":\"Add configuration context and instructions to be displayed in the admin UI\"}}",
                    validFeatureType);

            var featureSwitch = new SimpleFeatureSwitch
            {
                Name = "TestFeatureSwitch1",
                IsEnabled = true,
                FeatureType = validFeatureType
            };

            var providerMock = new Mock<IFeatureSwitchProvider>();
            providerMock.Setup(p => p.Get("TestFeatureSwitch1"))
                .Returns(featureSwitch);

            Femah.Configure()
                .FeatureSwitchEnum(typeof(FeatureSwitches))
                .Provider(providerMock.Object)
                .Initialise();

            //Act
            ApiResponse apiResponse;
            using (var apiResponseBuilder = new ApiResponseBuilder())
            {
                apiResponse = apiResponseBuilder.CreateWithUpdatedFeatureSwitch(featureSwitch);
            }

            //Assert
            Assert.AreEqual((int)HttpStatusCode.NotModified, apiResponse.HttpStatusCode);
            Assert.AreEqual(string.Empty, apiResponse.Body);
        }
Beispiel #6
0
        public void ApiResponseBuilderSetsHttpStatusCodeTo200AndReturnsDesiredFeatureSwitchStateIfPutRequestIncludesFeatureSwitchChangesToCustomParameters()
        {
            //Arrange
            const string validFeatureType = "Femah.Core.FeatureSwitchTypes.PercentageFeatureSwitch, Femah.Core, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null";
            const string jsonResponse = "{\"PercentageOn\":75,\"Description\":\"A simple feature switch that is on for a set percentage of users. The state of the switch is persisted in the user's cookies.If no cookie exists the state is chosen at random (weighted according to the percentage), and then stored in a cookie.\",\"ConfigurationInstructions\":\"Set PercentageOn to the percentage of users who should see this feature. Eg. 10 means the feature is on for 10% of users.\",\"IsEnabled\":true,\"Name\":\"TestFeatureSwitch1\",\"FeatureType\":\"Femah.Core.FeatureSwitchTypes.PercentageFeatureSwitch, Femah.Core, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null\"}";

            var currentFeatureSwitchState = new PercentageFeatureSwitch
            {
                Name = "TestFeatureSwitch1",
                FeatureType = validFeatureType,
                IsEnabled = true,
                PercentageOn = 50
            };

            var desiredFeatureSwitchState = new PercentageFeatureSwitch
            {
                Name = "TestFeatureSwitch1",
                FeatureType = validFeatureType,
                IsEnabled = true,
                PercentageOn = 75
            };

            var providerMock = new Mock<IFeatureSwitchProvider>();
            providerMock.Setup(p => p.Get("TestFeatureSwitch1"))
                .Returns(currentFeatureSwitchState);

            Femah.Configure()
                .FeatureSwitchEnum(typeof(FeatureSwitches))
                .Provider(providerMock.Object)
                .Initialise();

            //Act
            ApiResponse apiResponse;
            using (var apiResponseBuilder = new ApiResponseBuilder())
            {
                apiResponse = apiResponseBuilder.CreateWithUpdatedFeatureSwitch(desiredFeatureSwitchState);
            }

            //Assert
            Assert.AreEqual((int)HttpStatusCode.OK, apiResponse.HttpStatusCode);
            Assert.AreEqual(jsonResponse, apiResponse.Body);
        }