public void NotificationInstallation_Serialization_IsConsistent()
        {
            // Arrange
            NotificationTemplate testSecondaryTemplate = new NotificationTemplate
            {
                Body = "mySecondaryBody",
                Headers = new Dictionary<string, string>()
            };
            testSecondaryTemplate.Headers["mySecondaryHeaderName"] = "mySecondaryHeaderValue";

            NotificationSecondaryTile testSecondaryTile = new NotificationSecondaryTile
            {
                PushChannel = "myPushChannel",
                Tags = new List<string>(),
                Templates = new Dictionary<string, NotificationTemplate>()
            };
            testSecondaryTile.Tags.Add("myTag");
            testSecondaryTile.Templates["mySecondaryTemplateName"] = testSecondaryTemplate;

            NotificationInstallation installation = new NotificationInstallation
            {
                PushChannel = "myPushChannel",
                Platform = "myPlatform",
                InstallationId = "myId",
                SecondaryTiles = new Dictionary<string, NotificationSecondaryTile>(),
                Templates = new Dictionary<string, NotificationTemplate>()
            };
            installation.SecondaryTiles["myTestTile"] = testSecondaryTile;
            NotificationTemplate testTemplate = new NotificationTemplate
            {
                Body = "myBody",
                Headers = new Dictionary<string, string>()
            };
            testSecondaryTemplate.Headers["myHeaderName"] = "myHeaderValue";
            installation.Templates["myTemplateName"] = testTemplate;

            // Assert
            SerializationAssert.VerifySerialization(installation, "{\"pushChannel\":\"myPushChannel\",\"platform\":\"myPlatform\",\"installationId\":\"myId\",\"templates\":{\"myTemplateName\":{\"body\":\"myBody\",\"headers\":{},\"tags\":[]}},\"secondaryTiles\":{\"myTestTile\":{\"pushChannel\":\"myPushChannel\",\"tags\":[\"myTag\"],\"templates\":{\"mySecondaryTemplateName\":{\"body\":\"mySecondaryBody\",\"headers\":{\"mySecondaryHeaderName\":\"mySecondaryHeaderValue\",\"myHeaderName\":\"myHeaderValue\"},\"tags\":[]}}}},\"tags\":[]}");
        }
        /// <summary>
        /// Enables the registration of a device installation for push notifications via PUT request.
        /// If the specified installationId does not exist on the notification hub, a new installation is created and registered.
        /// If the specified installationId already exists, the installation is modified.
        /// </summary>
        /// <param name="installationId">The installation id to register or modify.</param>
        /// <param name="notificationInstallation">The <see cref="NotificationInstallation"/> object to register.</param>
        /// <returns><see cref="HttpResponseMessage"/> describing the result of the operation.</returns>
        public async Task<HttpResponseMessage> PutInstallation(string installationId, NotificationInstallation notificationInstallation)
        {
            if (!ModelState.IsValid)
            {
                throw new HttpResponseException(this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, this.ModelState));
            }

            this.ValidateInstallationId(installationId);
            this.ValidateNotificationInstallation(installationId, notificationInstallation);

            ITraceWriter traceWriter = this.Configuration.Services.GetTraceWriter();

            // The installation object that will be sent to NH.
            Installation installation = this.CreateInstallation(notificationInstallation);
            HashSet<string> tagsAssociatedWithInstallationId = await this.GetTagsAssociatedWithInstallationId(notificationInstallation.InstallationId);
            ClaimsPrincipal serviceUser = this.User as ClaimsPrincipal;
            if (tagsAssociatedWithInstallationId.Count == 0)
            {
                // Installation does not exist on NH.  Add it.
                if (installation.Tags == null)
                {
                    installation.Tags = new List<string>();
                }

                // Tag the installation with the UserId if authenticated.
                if (serviceUser != null && serviceUser.Identity.IsAuthenticated)
                {
                    Claim userIdClaim = serviceUser.FindFirst(ClaimTypes.NameIdentifier);
                    if (userIdClaim != null)
                    {
                        string incomingUserTag = string.Format(UserIdTagPlaceholder, userIdClaim.Value);
                        installation.Tags.Add(incomingUserTag);
                    }
                }

                try
                {
                    await this.UpsertInstallationAsync(installation);
                }
                catch (HttpResponseException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    string error = RResources.NotificationHub_CreateOrUpdateInstallationFailed.FormatForUser(installationId, ex.Message);

                    traceWriter.Error(error, ex, this.Request, ServiceLogCategories.NotificationControllers);
                    traceWriter.Error(error, ex, this.Request, LogCategories.NotificationControllers);

                    // We return 4xx status code on error as it is impossible to know whether it is bad input or a
                    // server error from NH. As a result we err on the bad request side.
                    return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.Message);
                }
            }
            else
            {
                // Installation already existed on NH.
                if (serviceUser != null && serviceUser.Identity.IsAuthenticated)
                {
                    // Because the user is authenticated, copy all previous tags except UserId.
                    CopyTagsToInstallation(installation, tagsAssociatedWithInstallationId, false);

                    // Add the incoming UserId.
                    Claim userIdClaim = serviceUser.FindFirst(ClaimTypes.NameIdentifier);
                    if (userIdClaim != null)
                    {
                        string incomingUserTag = string.Format(UserIdTagPlaceholder, userIdClaim.Value);
                        AddTagToInstallation(installation, incomingUserTag);
                    }
                }
                else
                {
                    // Because the request is anonymous, copy all previous tags to the installation object, including the previous user tag.
                    CopyTagsToInstallation(installation, tagsAssociatedWithInstallationId, true);
                }

                try
                {
                    await this.UpsertInstallationAsync(installation);
                }
                catch (HttpResponseException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    string error = RResources.NotificationHub_CreateOrUpdateInstallationFailed.FormatForUser(installationId, ex.Message);

                    traceWriter.Error(error, ex, this.Request, ServiceLogCategories.NotificationControllers);
                    traceWriter.Error(error, ex, this.Request, LogCategories.NotificationControllers);

                    // We return 4xx status code on error as it is impossible to know whether it is bad input or a
                    // server error from NH. As a result we err on the bad request side.
                    return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex.Message);
                }
            }

            return this.Request.CreateResponse(HttpStatusCode.OK);
        }
        internal void ValidateNotificationInstallation(string installationId, NotificationInstallation notificationInstallation)
        {
            if (notificationInstallation == null)
            {
                throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_NotificationInstallationWasNull.FormatForUser()));
            }

            if (notificationInstallation.InstallationId != null && !installationId.ToString().Equals(notificationInstallation.InstallationId, StringComparison.OrdinalIgnoreCase))
            {
                throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_InstallationIdDoesntMatchUri.FormatForUser()));
            }

            // Store the installation id into NotificationInstallation object.
            notificationInstallation.InstallationId = installationId.ToString();

            // Check the required fields of the NotificationInstallation object.  If failed, return Bad Request.
            if (string.IsNullOrEmpty(notificationInstallation.PushChannel))
            {
                throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_MissingPropertyInRequestBody.FormatForUser("pushChannel")));
            }

            if (string.IsNullOrEmpty(notificationInstallation.Platform))
            {
                throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_MissingPropertyInRequestBody.FormatForUser("platform")));
            }

            if (string.Equals(notificationInstallation.Platform, WindowsStorePlatform, StringComparison.OrdinalIgnoreCase))
            {
                if (notificationInstallation.SecondaryTiles != null)
                {
                    this.ValidateSecondaryTiles(notificationInstallation);
                }
            }

            foreach (NotificationTemplate template in notificationInstallation.Templates.Values)
            {
                this.ValidateTemplate(template, notificationInstallation.Platform);
            }
        }
        internal void ValidateSecondaryTiles(NotificationInstallation notificationInstallation)
        {
            if (notificationInstallation == null)
            {
                throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_NotificationInstallationWasNull.FormatForUser()));
            }

            foreach (NotificationSecondaryTile secondaryTile in notificationInstallation.SecondaryTiles.Values)
            {
                if (secondaryTile == null)
                {
                    throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_SecondaryTileWasNull.FormatForUser()));
                }

                this.ValidateSecondaryTile(secondaryTile);
            }
        }
        internal Installation CreateInstallation(NotificationInstallation notificationInstallation)
        {
            if (notificationInstallation == null)
            {
                throw new ArgumentNullException("notificationInstallation");
            }

            if (notificationInstallation.Platform == null)
            {
                throw new ArgumentException("notificationInstallation.Platform is null");
            }

            Installation installation = new Installation();
            installation.InstallationId = notificationInstallation.InstallationId;
            installation.PushChannel = notificationInstallation.PushChannel;

            // Set the Platform. Create and add WnsSecondaryTile objects to the installation if WNS.
            if (notificationInstallation.Platform.Equals(WindowsStorePlatform, StringComparison.OrdinalIgnoreCase))
            {
                installation.Platform = NotificationPlatform.Wns;

                // Parse each Secondary Tile passed in request and add to installation object.
                foreach (string tileName in notificationInstallation.SecondaryTiles.Keys)
                {
                    NotificationSecondaryTile notificationTile = notificationInstallation.SecondaryTiles[tileName];
                    if (installation.SecondaryTiles == null)
                    {
                        installation.SecondaryTiles = new Dictionary<string, WnsSecondaryTile>();
                    }

                    installation.SecondaryTiles[tileName] = CreateWnsSecondaryTile(notificationTile);
                }
            }
            else if (notificationInstallation.Platform.Equals(ApplePlatform, StringComparison.OrdinalIgnoreCase))
            {
                installation.Platform = NotificationPlatform.Apns;
            }
            else if (notificationInstallation.Platform.Equals(GooglePlatform, StringComparison.OrdinalIgnoreCase))
            {
                installation.Platform = NotificationPlatform.Gcm;
            }
            else
            {
                throw new HttpResponseException(this.Request.CreateBadRequestResponse(RResources.NotificationHub_UnsupportedPlatform.FormatForUser(notificationInstallation.Platform)));
            }

            // Create and add InstallationTemplate objects to the installation.
            foreach (string templateName in notificationInstallation.Templates.Keys)
            {
                NotificationTemplate template = notificationInstallation.Templates[templateName];
                if (installation.Templates == null)
                {
                    installation.Templates = new Dictionary<string, InstallationTemplate>();
                }

                installation.Templates[templateName] = CreateInstallationTemplate(template, installation.Platform);
            }

            return installation;
        }
        public void CreateInstallationObject_ParsingIsConsistent(string pushChannel, string platformAsString, NotificationPlatform platformAsEnum)
        {
            string installationId = "12345678-1234-1234-1234-123456789012";
            string tileName = "myTile";
            string tileTemplateName = "templateNameTile";
            string tileTemplateBody = "myTemplateBodyTile";
            string tileTemplateHeaderName = "templateHeaderNameTile";
            string tileTemplateHeaderValue = "templateHeaderValueTile";
            string installationTemplateName = "installationTemplateName";
            string installationTemplateBody = "installationTemplateBody";
            string installationTemplateHeaderName = "installationTemplateHeaderName";
            string installationTemplateHeaderValue = "installationTemplateHeaderBody";

            // Arrange
            Dictionary<string, string> tileTemplateHeaders = new Dictionary<string, string>();
            tileTemplateHeaders[tileTemplateHeaderName] = tileTemplateHeaderValue;

            Dictionary<string, NotificationTemplate> tileTemplates = new Dictionary<string, NotificationTemplate>();
            tileTemplates[tileTemplateName] = new NotificationTemplate
            {
                Body = tileTemplateBody,
                Headers = tileTemplateHeaders
            };

            Dictionary<string, NotificationSecondaryTile> tiles = new Dictionary<string, NotificationSecondaryTile>();
            tiles[tileName] = new NotificationSecondaryTile
            {
                PushChannel = pushChannel,
                Tags = new List<string> { "tag1", "tag2" },
                Templates = tileTemplates
            };

            Dictionary<string, string> installationTemplateHeaders = new Dictionary<string, string>();
            installationTemplateHeaders[installationTemplateHeaderName] = installationTemplateHeaderValue;

            Dictionary<string, NotificationTemplate> installationTemplates = new Dictionary<string, NotificationTemplate>();
            installationTemplates[installationTemplateName] = new NotificationTemplate
            {
                Body = installationTemplateBody,
                Headers = installationTemplateHeaders
            };

            NotificationInstallation notificationInstallation = new NotificationInstallation
            {
                Platform = platformAsString,
                PushChannel = pushChannel,
                SecondaryTiles = tiles,
                Tags = new List<string> { "tagA", "tagB" },
                Templates = installationTemplates
            };

            notificationInstallation.InstallationId = installationId;

            NotificationInstallationsControllerMock mock = new NotificationInstallationsControllerMock();

            // Act
            Installation testInstallation = mock.CreateInstallation(notificationInstallation);

            // Assert
            Assert.NotNull(testInstallation);
            Assert.Equal(installationId, testInstallation.InstallationId);
            Assert.Equal(platformAsEnum, testInstallation.Platform);
            Assert.Equal(pushChannel, testInstallation.PushChannel);
            if (platformAsEnum == NotificationPlatform.Wns)
            {
                Assert.NotNull(testInstallation.SecondaryTiles);
                Assert.NotNull(testInstallation.SecondaryTiles[tileName]);
                Assert.Equal(pushChannel, testInstallation.SecondaryTiles[tileName].PushChannel);
                Assert.Equal(0, testInstallation.SecondaryTiles[tileName].Tags.Count);
                Assert.NotNull(testInstallation.SecondaryTiles[tileName].Templates);
                Assert.NotNull(testInstallation.SecondaryTiles[tileName].Templates[tileTemplateName]);

                // Tags were stripped within the tile 
                Assert.Equal(tileTemplateBody, testInstallation.SecondaryTiles[tileName].Templates[tileTemplateName].Body);
                Assert.Equal(installationTemplateHeaderValue, testInstallation.Templates[installationTemplateName].Headers[installationTemplateHeaderName]);
            }
            else
            {
                Assert.Null(testInstallation.SecondaryTiles);
                Assert.Null(testInstallation.Templates[installationTemplateName].Headers);
            }

            Assert.NotNull(testInstallation.Templates[installationTemplateName]);
            Assert.Equal(installationTemplateBody, testInstallation.Templates[installationTemplateName].Body);

            // Tags were stripped within the template
            Assert.Equal(0, testInstallation.Templates[installationTemplateName].Tags.Count);
        }