public async Task <string> CreateTemplate(long id, AuthorInfo authorInfo, ITemplateDescriptor templateDescriptor)
        {
            if (id == 0)
            {
                throw new ArgumentException("Template Id must be set", nameof(id));
            }

            var redLock = await _distributedLockManager.CreateLockAsync(id);

            try
            {
                if (await _templatesStorageReader.IsTemplateExists(id))
                {
                    throw new ObjectAlreadyExistsException(id);
                }

                await PutTemplate(id, authorInfo, templateDescriptor);

                // ceph does not return version-id response header, so we need to do another request to get version
                return(await _templatesStorageReader.GetTemplateLatestVersion(id));
            }
            finally
            {
                redLock?.Dispose();
            }
        }
        private async Task <string> PutTemplate(long id, AuthorInfo authorInfo, ITemplateDescriptor templateDescriptor, int?prevVersionIndex = null)
        {
            await VerifyElementDescriptorsConsistency(templateDescriptor.Elements);

            var versionIndex = prevVersionIndex + 1 ?? 0;
            var json         = JsonConvert.SerializeObject(templateDescriptor, SerializerSettings.Default);
            var template     = new Template
            {
                Id           = id,
                VersionIndex = versionIndex,
                Data         = json,
                Author       = authorInfo.Author,
                AuthorLogin  = authorInfo.AuthorLogin,
                AuthorName   = authorInfo.AuthorName,
                LastModified = DateTime.UtcNow
            };

            using (var scope = await _context.Database.BeginTransactionAsync(IsolationLevel.RepeatableRead))
            {
                await _context.Templates.AddAsync(template);

                await _context.SaveChangesAsync();

                scope.Commit();
            }

            return(template.VersionId);
        }
Exemple #3
0
 public async Task <IActionResult> Create(
     long id,
     [FromHeader(Name = Http.HeaderNames.AmsAuthor)] string author,
     [FromHeader(Name = Http.HeaderNames.AmsAuthorLogin)] string authorLogin,
     [FromHeader(Name = Http.HeaderNames.AmsAuthorName)] string authorName,
     [FromBody] ITemplateDescriptor templateDescriptor)
 {
     return(await CreateInternal(id, author, authorLogin, authorName, templateDescriptor, GenerateTemplateErrorJson));
 }
Exemple #4
0
 public async Task <IActionResult> Modify(
     long id,
     ApiVersion apiVersion,
     [FromHeader(Name = HeaderNames.IfMatch)] string ifMatch,
     [FromHeader(Name = Http.HeaderNames.AmsAuthor)] string author,
     [FromHeader(Name = Http.HeaderNames.AmsAuthorLogin)] string authorLogin,
     [FromHeader(Name = Http.HeaderNames.AmsAuthorName)] string authorName,
     [FromBody] ITemplateDescriptor templateDescriptor)
 => await ModifyInternal(id, ifMatch, apiVersion, author, authorLogin, authorName, templateDescriptor, GenerateTemplateErrorJson);
Exemple #5
0
 public static IReadOnlyCollection <int> GetBinaryElementTemplateCodes(this ITemplateDescriptor templateDescriptor)
 {
     return(templateDescriptor.Elements
            .Where(x => x.Type == ElementDescriptorType.Article ||
                   x.Type == ElementDescriptorType.BitmapImage ||
                   x.Type == ElementDescriptorType.VectorImage)
            .Select(x => x.TemplateCode)
            .ToList());
 }
Exemple #6
0
        private async Task <IActionResult> ModifyInternal(
            long id,
            string ifMatch,
            string author,
            string authorLogin,
            string authorName,
            ITemplateDescriptor templateDescriptor,
            Func <TemplateValidationException, JToken> errorGenerator)
        {
            if (string.IsNullOrEmpty(ifMatch))
            {
                return(BadRequest($"'{HeaderNames.IfMatch}' request header must be specified."));
            }

            if (string.IsNullOrEmpty(author) || string.IsNullOrEmpty(authorLogin) || string.IsNullOrEmpty(authorName))
            {
                return(BadRequest(
                           $"'{Http.HeaderNames.AmsAuthor}', '{Http.HeaderNames.AmsAuthorLogin}' and '{Http.HeaderNames.AmsAuthorName}' " +
                           "request headers must be specified."));
            }

            if (templateDescriptor == null)
            {
                return(BadRequest("Template descriptor must be set."));
            }

            try
            {
                var latestVersionId = await _templatesManagementService.ModifyTemplate(
                    id,
                    ifMatch.Trim('"'),
                    new AuthorInfo(author, authorLogin, authorName),
                    templateDescriptor);

                var url = Url.AbsoluteAction("GetVersion", "Templates", new { id, versionId = latestVersionId });

                Response.Headers[HeaderNames.ETag] = $"\"{latestVersionId}\"";
                return(NoContent(url));
            }
            catch (ObjectNotFoundException)
            {
                return(NotFound());
            }
            catch (TemplateValidationException ex)
            {
                return(Unprocessable(errorGenerator(ex)));
            }
            catch (LockAlreadyExistsException)
            {
                return(Locked("Simultaneous modification of template"));
            }
            catch (ConcurrencyException)
            {
                return(PreconditionFailed());
            }
        }
Exemple #7
0
        private async Task <IActionResult> CreateInternal(
            long id,
            string author,
            string authorLogin,
            string authorName,
            ApiVersion apiVersion,
            ITemplateDescriptor templateDescriptor,
            Func <TemplateValidationException, JToken> errorGenerator)
        {
            if (string.IsNullOrEmpty(author) || string.IsNullOrEmpty(authorLogin) || string.IsNullOrEmpty(authorName))
            {
                return(BadRequest(
                           $"'{Http.HeaderNames.AmsAuthor}', '{Http.HeaderNames.AmsAuthorLogin}' and '{Http.HeaderNames.AmsAuthorName}' " +
                           "request headers must be specified."));
            }

            if (TryGetModelErrors(out var errors))
            {
                return(BadRequest(errors));
            }

            try
            {
                var versionId = await _templatesManagementService.CreateTemplate(id, new AuthorInfo(author, authorLogin, authorName), templateDescriptor);

                Response.Headers[HeaderNames.ETag] = $"\"{versionId}\"";
                var routeValues = new Dictionary <string, string> {
                    ["api-version"] = apiVersion.ToString(), [nameof(id)] = id.ToString(), [nameof(versionId)] = versionId
                };
                return(CreatedAtAction(nameof(GetVersion), routeValues, null));
            }
            catch (ObjectAlreadyExistsException)
            {
                return(Conflict("Template with the same id already exists"));
            }
            catch (LockAlreadyExistsException)
            {
                return(Locked("Simultaneous creation of template with the same id"));
            }
            catch (TemplateValidationException ex)
            {
                return(Unprocessable(errorGenerator(ex)));
            }
            catch (InputDataValidationException ex)
            {
                return(BadRequest(ex.Message));
            }
        }
        public async Task <string> CreateTemplate(long id, AuthorInfo authorInfo, ITemplateDescriptor templateDescriptor)
        {
            if (id == default)
            {
                throw new InputDataValidationException("Template Id must be set");
            }

            using (await _distributedLockManager.AcquireLockAsync(id))
            {
                if (await _templatesStorageReader.IsTemplateExists(id))
                {
                    throw new ObjectAlreadyExistsException(id);
                }

                return(await PutTemplate(id, authorInfo, templateDescriptor));
            }
        }
Exemple #9
0
        private async Task <IActionResult> CreateInternal(
            long id,
            string author,
            string authorLogin,
            string authorName,
            ITemplateDescriptor templateDescriptor,
            Func <TemplateValidationException, JToken> errorGenerator)
        {
            if (string.IsNullOrEmpty(author) || string.IsNullOrEmpty(authorLogin) || string.IsNullOrEmpty(authorName))
            {
                return(BadRequest(
                           $"'{Http.HeaderNames.AmsAuthor}', '{Http.HeaderNames.AmsAuthorLogin}' and '{Http.HeaderNames.AmsAuthorName}' " +
                           "request headers must be specified."));
            }

            if (templateDescriptor == null)
            {
                return(BadRequest("Template descriptor must be set."));
            }

            try
            {
                var versionId = await _templatesManagementService.CreateTemplate(id, new AuthorInfo(author, authorLogin, authorName), templateDescriptor);

                var url = Url.AbsoluteAction("GetVersion", "Templates", new { id, versionId });

                Response.Headers[HeaderNames.ETag] = $"\"{versionId}\"";
                return(Created(url, null));
            }
            catch (ObjectAlreadyExistsException)
            {
                return(Conflict("Template with the same id already exists"));
            }
            catch (LockAlreadyExistsException)
            {
                return(Locked("Simultaneous creation of template with the same id"));
            }
            catch (TemplateValidationException ex)
            {
                return(Unprocessable(errorGenerator(ex)));
            }
        }
        private async Task PutTemplate(long id, AuthorInfo authorInfo, ITemplateDescriptor templateDescriptor)
        {
            await VerifyElementDescriptorsConsistency(templateDescriptor.Elements);

            var putRequest = new PutObjectRequest
            {
                Key         = id.ToString(),
                BucketName  = _bucketName,
                ContentType = ContentType.Json,
                ContentBody = JsonConvert.SerializeObject(templateDescriptor, SerializerSettings.Default),
                CannedACL   = S3CannedACL.PublicRead,
            };
            var metadataWrapper = MetadataCollectionWrapper.For(putRequest.Metadata);

            metadataWrapper.Write(MetadataElement.Author, authorInfo.Author);
            metadataWrapper.Write(MetadataElement.AuthorLogin, authorInfo.AuthorLogin);
            metadataWrapper.Write(MetadataElement.AuthorName, authorInfo.AuthorName);

            await _s3Client.PutObjectAsync(putRequest);
        }
Exemple #11
0
 public static IReadOnlyCollection <UploadUrl> Generate(ITemplateDescriptor templateDescriptor, Func <int, string> urlComposer)
 {
     return(templateDescriptor.GetBinaryElementTemplateCodes()
            .Select(x => new UploadUrl(x, urlComposer(x)))
            .ToList());
 }
        public async Task <string> ModifyTemplate(long id, string versionId, AuthorInfo authorInfo, ITemplateDescriptor templateDescriptor)
        {
            if (id == 0)
            {
                throw new ArgumentException("Template Id must be set", nameof(id));
            }

            if (string.IsNullOrEmpty(versionId))
            {
                throw new ArgumentException("VersionId must be set", nameof(versionId));
            }

            using (await _distributedLockManager.AcquireLockAsync(id))
            {
                if (!await _templatesStorageReader.IsTemplateExists(id))
                {
                    throw new ObjectNotFoundException($"Template '{id}' does not exist");
                }

                var latestVersionId = await _templatesStorageReader.GetTemplateLatestVersion(id);

                if (!versionId.Equals(latestVersionId, StringComparison.Ordinal))
                {
                    throw new ConcurrencyException(id, versionId, latestVersionId);
                }

                await PutTemplate(id, authorInfo, templateDescriptor);

                // ceph does not return version-id response header, so we need to do another request to get version
                return(await _templatesStorageReader.GetTemplateLatestVersion(id));
            }
        }
Exemple #13
0
        private async Task <IActionResult> ModifyInternal(
            long id,
            string ifMatch,
            ApiVersion apiVersion,
            string author,
            string authorLogin,
            string authorName,
            ITemplateDescriptor templateDescriptor,
            Func <TemplateValidationException, JToken> errorGenerator)
        {
            if (string.IsNullOrEmpty(ifMatch))
            {
                return(BadRequest($"'{HeaderNames.IfMatch}' request header must be specified."));
            }

            if (string.IsNullOrEmpty(author) || string.IsNullOrEmpty(authorLogin) || string.IsNullOrEmpty(authorName))
            {
                return(BadRequest(
                           $"'{Http.HeaderNames.AmsAuthor}', '{Http.HeaderNames.AmsAuthorLogin}' and '{Http.HeaderNames.AmsAuthorName}' " +
                           "request headers must be specified."));
            }

            if (TryGetModelErrors(out var errors))
            {
                return(BadRequest(errors));
            }

            try
            {
                var versionId = await _templatesManagementService.ModifyTemplate(
                    id,
                    ifMatch.Trim('"'),
                    new AuthorInfo(author, authorLogin, authorName),
                    templateDescriptor);

                var routeValues = new Dictionary <string, string> {
                    ["api-version"] = apiVersion.ToString(), [nameof(id)] = id.ToString(), [nameof(versionId)] = versionId
                };
                var url = Url.AbsoluteAction("GetVersion", "Templates", routeValues);

                Response.Headers[HeaderNames.ETag] = $"\"{versionId}\"";
                return(NoContent(url));
            }
            catch (ObjectNotFoundException)
            {
                return(NotFound());
            }
            catch (TemplateValidationException ex)
            {
                return(Unprocessable(errorGenerator(ex)));
            }
            catch (LockAlreadyExistsException)
            {
                return(Locked("Simultaneous modification of template"));
            }
            catch (ConcurrencyException)
            {
                return(PreconditionFailed());
            }
            catch (InputDataValidationException ex)
            {
                return(BadRequest(ex.Message));
            }
        }
Exemple #14
0
        public async Task TestTemplateDeserialization(string apiVersion)
        {
            const long          TemplateId             = 100500L;
            const string        CreatedTemplateVersion = "some_version_id";
            var                 authorInfo             = new AuthorInfo("id", "login", "name");
            ITemplateDescriptor receivedDescriptor     = null;

            _mockTemplatesManagementService.Reset();
            _mockTemplatesManagementService.Setup(x => x.CreateTemplate(It.IsAny <long>(), It.IsAny <AuthorInfo>(), It.IsAny <ITemplateDescriptor>()))
            .Callback <long, AuthorInfo, ITemplateDescriptor>((id, author, descriptor) => receivedDescriptor = descriptor)
            .ReturnsAsync(CreatedTemplateVersion);

            const string ObjectJson =
                @"{
    ""properties"": { ""baz"": 123, ""foo"": ""bar"" },
    ""elements"": [{
        ""type"": ""plainText"",
        ""templateCode"": 911,
        ""id"": 100500,
        ""properties"": { ""foo"": ""bar"", ""baz"": [ 321, 456 ] },
        ""constraints"": {
            ""unspecified"": {
                ""maxSymbols"": 10,
                ""maxSymbolsPerWord"": null,
                ""maxLines"": 2
            }
        }
    }]
}";

            using (var httpContent = new StringContent(ObjectJson, Encoding.UTF8, NuClear.VStore.Http.ContentType.Json))
            {
                using (var request = new HttpRequestMessage(HttpMethod.Post, $"/api/{apiVersion}/templates/{TemplateId}"))
                {
                    request.Content = httpContent;
                    request.Headers.Add(NuClear.VStore.Http.HeaderNames.AmsAuthor, authorInfo.Author);
                    request.Headers.Add(NuClear.VStore.Http.HeaderNames.AmsAuthorLogin, authorInfo.AuthorLogin);
                    request.Headers.Add(NuClear.VStore.Http.HeaderNames.AmsAuthorName, authorInfo.AuthorName);
                    using (var response = await _client.SendAsync(request))
                    {
                        Assert.Equal(HttpStatusCode.Created, response.StatusCode);
                        Assert.NotNull(response.Headers.Location);
                        Assert.Equal($"/api/{apiVersion}/templates/{TemplateId}/{CreatedTemplateVersion}", response.Headers.Location.PathAndQuery);
                        Assert.Equal($"\"{CreatedTemplateVersion}\"", response.Headers.ETag.Tag);

                        Assert.NotNull(receivedDescriptor);
                        Assert.Equal(JObject.Parse(@"{""foo"": ""bar"", ""baz"": 123}"), receivedDescriptor.Properties, JTokenEqualityComparer);

                        Assert.Single(receivedDescriptor.Elements);
                        var element = receivedDescriptor.Elements.First();
                        Assert.Equal(ElementDescriptorType.PlainText, element.Type);
                        Assert.Equal(911, element.TemplateCode);
                        Assert.Equal(JObject.Parse(@"{""foo"": ""bar"", ""baz"": [ 321, 456 ]}"), element.Properties, JTokenEqualityComparer);

                        Assert.Single(element.Constraints);
                        var constraintSetItem = element.Constraints.First <ConstraintSetItem>();
                        Assert.IsType <PlainTextElementConstraints>(constraintSetItem.ElementConstraints);
                        var constraints = (PlainTextElementConstraints)constraintSetItem.ElementConstraints;
                        Assert.Equal(new PlainTextElementConstraints {
                            MaxLines = 2, MaxSymbols = 10, MaxSymbolsPerWord = null
                        }, constraints);

                        _mockTemplatesManagementService.Verify(x => x.CreateTemplate(TemplateId,
                                                                                     It.Is <AuthorInfo>(a => a.Author == authorInfo.Author &&
                                                                                                        a.AuthorLogin == authorInfo.AuthorLogin &&
                                                                                                        a.AuthorName == authorInfo.AuthorName),
                                                                                     It.IsAny <ITemplateDescriptor>()),
                                                               Times.Exactly(1));
                    }
                }
            }

            _mockTemplatesManagementService.Reset();
        }
        public async Task <string> ModifyTemplate(long id, string versionId, AuthorInfo authorInfo, ITemplateDescriptor templateDescriptor)
        {
            if (id == default)
            {
                throw new InputDataValidationException("Template Id must be set");
            }

            if (string.IsNullOrEmpty(versionId))
            {
                throw new InputDataValidationException("VersionId must be set");
            }

            using (await _distributedLockManager.AcquireLockAsync(id))
            {
                var(latestVersionId, latestVersionIndex) = await _templatesStorageReader.GetTemplateLatestVersion(id);

                if (!versionId.Equals(latestVersionId, StringComparison.Ordinal))
                {
                    throw new ConcurrencyException(id, versionId, latestVersionId);
                }

                return(await PutTemplate(id, authorInfo, templateDescriptor, latestVersionIndex));
            }
        }
 public static IReadOnlyCollection <int> GetBinaryElementTemplateCodes(this ITemplateDescriptor templateDescriptor) =>
 templateDescriptor.Elements
 .GetBinaryElements()
 .Select(x => x.TemplateCode)
 .ToList();