예제 #1
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;
            }
        }
예제 #2
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;
            }
        }
예제 #3
0
        public void ProcessPutRequestSetsHttpStatusCodeTo400AndReturnsGenericErrorMessageInResponseBodyIfPutRequestBodyContainsAJsonArrayOfFeatureSwitches()
        {
            //Arrange
            const string validFeatureType = "Femah.Core.FeatureSwitchTypes.SimpleFeatureSwitch, Femah.Core, Version=0.1.0.0, Culture=neutral, PublicKeyToken=null";

            var apiRequest = new ApiRequest
            {
                HttpMethod = "PUT",
                Service = ApiRequest.ApiService.featureswitches,
                Parameter = "TestFeatureSwitch",
                Body = 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\"}},{{\"IsEnabled\":true,\"Name\":\"TestFeatureSwitch2\",\"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)
            };

            //TODO: I'd like this to be a more specific error with regards to formatting
            const string expectedJsonBody = "\"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?\"";

            //Act
            ApiResponse apiResponse = ProcessApiRequest.ProcessPutRequest(apiRequest);

            //Assert
            Assert.AreEqual((int)HttpStatusCode.BadRequest, apiResponse.HttpStatusCode);
            Assert.AreEqual(expectedJsonBody, apiResponse.Body);
        }
예제 #4
0
        public void ProcessPutRequestSetsHttpStatusCodeTo304IfPutRequestBodyIsValidJsonButFeatureSwitchHasNoChanges()
        {
            //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 apiRequest = new ApiRequest
            {
                HttpMethod = "PUT",
                Service = ApiRequest.ApiService.featureswitches,
                Parameter = "TestFeatureSwitch",
                Body = jsonRequestAndResponse
            };

            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 = ProcessApiRequest.ProcessPutRequest(apiRequest);

            //Assert
            Assert.AreEqual((int)HttpStatusCode.NotModified, apiResponse.HttpStatusCode);
            Assert.AreEqual(string.Empty, apiResponse.Body);
        }
예제 #5
0
 public PutApiRequestFactory ForServiceType(ApiRequest.ApiService service)
 {
     _service = service;
     return this;
 }
예제 #6
0
        public ApiRequest Build(HttpRequestBase request)
        {
            var apiRequest = new ApiRequest();
            if (request.HttpMethod == "PUT" && request.ContentType != "application/json")
            {
                apiRequest.ErrorMessage = string.Format("Error: Content-Type '{0}' of request is not supported, expecting 'application/json'.", request.ContentType);
                apiRequest.ErrorMessageHttpStatusCode = HttpStatusCode.UnsupportedMediaType;
                return apiRequest;
            }

            if (request.Url == null)
            {
                apiRequest.ErrorMessage = string.Format("Error: The requested Url '{0}' is null.", request.Url);
                apiRequest.ErrorMessageHttpStatusCode = HttpStatusCode.InternalServerError;
                return apiRequest;
            }

            var uriSegments = request.Url.Segments;

            try
            {
                apiRequest.HttpMethod = request.HttpMethod;

                //If we have a request body then retrieve it from the InputStream.
                if (request.InputStream != null)
                {
                    string requestBody;
                    using (var stream = new MemoryStream())
                    {
                        request.InputStream.Seek(0, SeekOrigin.Begin);
                        request.InputStream.CopyTo(stream);
                        requestBody = Encoding.UTF8.GetString(stream.ToArray());
                    }
                    apiRequest.Body = requestBody;
                }

                if (uriSegments.Count() < 4 || uriSegments.Count()> 5)
                {
                    apiRequest.ErrorMessage = string.Format("Error: The requested Url '{0}' does not match the expected format /femah.axd/api/[service]/[parameter].", request.Url);
                    apiRequest.ErrorMessageHttpStatusCode = HttpStatusCode.InternalServerError;
                    return apiRequest;
                }

                //Retrieve the service passed in on the Url, service is mandatory.
                string service = uriSegments[3].ToLower().Replace("/", "");

                ApiRequest.ApiService apiService;
                if (EnumExtensions.TryParse(service, out apiService))
                {
                    if (Enum.IsDefined(typeof (ApiRequest.ApiService), apiService))
                        apiRequest.Service = apiService;
                }
                else
                {
                    apiRequest.Service = null;
                }

                if (uriSegments.Count() == 5)
                {
                    //Retrieve the optional parameter of the service passed in on the Url, this is optional so handle this a little better.
                    string member = uriSegments[4].ToLower().Replace("/", "");
                    apiRequest.Parameter = member;
                }

            }
            catch (IndexOutOfRangeException)
            {
                apiRequest.ErrorMessage = string.Format("Error: The requested Url '{0}' does not match the expected format /femah.axd/api/[service]/[parameter].", request.Url);
                apiRequest.ErrorMessageHttpStatusCode = HttpStatusCode.InternalServerError;
            }

            return apiRequest;
        }