public void Validate_MissingDescriptions_ThrowsException()
        {
            var mainArmTemplateFile        = MainArmTemplateFile.ReadFromFileSystem(this.fileSystem);
            var mainArmTemplateFileElement = JsonElementFactory.CreateElement(mainArmTemplateFile.Content);

            var patchedElement = mainArmTemplateFileElement.Patch(
                JsonPatchOperations.Remove("/parameters/sshRSAPublicKey/metadata/description"),
                JsonPatchOperations.Remove("/parameters/clusterName/metadata/description"),
                JsonPatchOperations.Remove("/parameters/osDiskSizeGB/metadata/description"),
                JsonPatchOperations.Remove("/outputs/controlPlaneFQDN/metadata/description"));

            fileSystem.AddFile(mainArmTemplateFile.Path, patchedElement.ToJsonString());

            var latestArmTemplateFile = MainArmTemplateFile.ReadFromFileSystem(this.fileSystem);
            var sut = this.CreateDescriptionsValidator(latestArmTemplateFile);

            FluentActions.Invoking(() => sut.Validate(this.fileToValidate)).Should()
            .Throw <InvalidModuleFileException>()
            .WithMessage(
                $@"The file ""{this.fileToValidate.Path}"" is invalid. Descriptions for the following parameters are missing:
  - sshRSAPublicKey
  - clusterName
  - osDiskSizeGB

The file ""{this.fileToValidate.Path}"" is invalid. Descriptions for the following outputs are missing:
  - controlPlaneFQDN
".ReplaceLineEndings());
        }
        public override JsonElement Execute(JsonElement input)
        {
            input = Expression.Execute(input);
            bool result = input.IsTruthy();

            return(JsonElementFactory.CreateBool(result));
        }
        public override JsonElement Execute(JsonElement input)
        {
            input = ExecutedOn.Execute(input);

            if (input.ValueKind != JsonValueKind.Array)
            {
                return(JsonElementFactory.CreateNull());
            }

            List <JsonElement> array = input.EnumerateArray().ToList();

            if (IsAllArrayElemets)
            {
                return(input);
            }

            if (ExactElementsAccess != null && ExactElementsAccess.Any())
            {
                IEnumerable <JsonElement> res = array.GetByIndexes(ExactElementsAccess);
                return(JsonElementFactory.CreateArray(res));
            }

            List <JsonElement> result = array.GetSlice(SliceStart, SliceEnd, SliceStep);

            return(JsonElementFactory.CreateArray(result));
        }
        public void PropertyFilterSubExpression_DeeplyNestedProperty_ReturnsCorrectValue()
        {
            string json = @"
                {
                    `a`: {
                        `b`: {
                            `c`: {
                                `d`: {
                                    `e`: 4
                                }
                            }
                        }
                    }        
                }"
                          .Replace("`", "\"");
            JsonElement data = JsonDocument.Parse(json).RootElement;

            FilterSubExpression exp = FilterParser.Parse(FilterExpressionTokenizer.Tokenize("@.a.b.c.d.e == @.b"));

            exp = (exp as ComparisonFilterSubExpression)?.LeftSide as PropertyFilterSubExpression;
            Assert.NotNull(exp);

            JsonElement expected = JsonElementFactory.CreateNumber(4);
            JsonElement result   = exp.Execute(data);

            Assert.Equal(expected, result, JsonElementEqualityComparer.Default);
        }
Beispiel #5
0
        public void Validate(MainBicepTestFile file)
        {
            var tempFilePath = this.fileSystem.Path.GetTempFileName();

            try
            {
                bicepCliProxy.Build(file.Path, tempFilePath);
            }
            catch (Exception exception)
            {
                this.fileSystem.File.Delete(tempFilePath);

                if (exception is BicepException)
                {
                    // Failed to build the test file. Convert it to InvalidModuleException so that
                    // the validate command can continue validating other files.
                    throw new InvalidModuleException(exception.Message, exception);
                }

                throw;
            }

            this.logger.LogInformation("Making sure the test file contains at least one test...");

            using var tempFileStream = fileSystem.FileStream.CreateDeleteOnCloseStream(tempFilePath);
            var testTemplateElement = JsonElementFactory.CreateElement(tempFileStream);
            var testDeployments     = testTemplateElement.Select($@"$.resources[?(@.type == ""Microsoft.Resources/deployments"" && @.properties.template.metadata._generator.templateHash == ""{this.latestMainArmTemplateFile.TemplateHash}"")]");

            if (testDeployments.IsEmpty())
            {
                throw new InvalidModuleException($"The file \"{file.Path}\" is invalid. Could not find tests in the file. Please make sure to add at least one module referencing the main Bicep file.");
            }
        }
        public override JsonElement Execute(JsonElement input)
        {
            JsonElement left  = LeftSide.Execute(input);
            JsonElement right = RightSide.Execute(input);

            bool result = false;

            if ((IsEqual || IsGreaterOrEqual || IsLessOrEqual) && JsonElementEqualityComparer.Default.Equals(left, right))
            {
                result = true;
            }
            else if (IsNotEqual && !JsonElementEqualityComparer.Default.Equals(left, right))
            {
                result = true;
            }
            else if (left.ValueKind == right.ValueKind)
            {
                int r = JsonElementComparer.Default.Compare(left, right);

                if ((IsGreater || IsGreaterOrEqual) && r > 0)
                {
                    result = true;
                }
                else if ((IsLess || IsLessOrEqual) && r < 0)
                {
                    result = true;
                }
            }

            return(JsonElementFactory.CreateBool(result));
        }
        public void DifferentStrings_ReturnsFalse()
        {
            JsonElement n1     = JsonElementFactory.CreateString("a");
            JsonElement n2     = JsonElementFactory.CreateString(" a");
            bool        result = JsonElementEqualityComparer.Default.Equals(n1, n2);

            Assert.False(result);
        }
        public void SameStrings_ReturnsTrue()
        {
            JsonElement n1     = JsonElementFactory.CreateString("abc");
            JsonElement n2     = JsonElementFactory.CreateString("abc");
            bool        result = JsonElementEqualityComparer.Default.Equals(n1, n2);

            Assert.True(result);
        }
        public void DifferentNumbers_ReturnsFalse()
        {
            JsonElement n1     = JsonElementFactory.CreateNumber(1.123);
            JsonElement n2     = JsonElementFactory.CreateNumber(1.121);
            bool        result = JsonElementEqualityComparer.Default.Equals(n1, n2);

            Assert.False(result);
        }
        public void SameNumbers_ReturnsTrue()
        {
            JsonElement n1     = JsonElementFactory.CreateNumber(1.123);
            JsonElement n2     = JsonElementFactory.CreateNumber(1.123);
            bool        result = JsonElementEqualityComparer.Default.Equals(n1, n2);

            Assert.True(result);
        }
        public void DifferentBools_ReturnsTrue()
        {
            JsonElement n1     = JsonElementFactory.CreateBool(true);
            JsonElement n2     = JsonElementFactory.CreateBool(false);
            bool        result = JsonElementEqualityComparer.Default.Equals(n1, n2);

            Assert.False(result);
        }
        public void DifferentTypes_ReturnsFalse()
        {
            JsonElement n1 = JsonElementFactory.CreateBool(true);
            JsonElement n2 = JsonElementFactory.CreateArray(new List <object> {
                1, "abc", 3
            });
            bool result = JsonElementEqualityComparer.Default.Equals(n1, n2);

            Assert.False(result);
        }
        public void SameArrays_ReturnsTrue()
        {
            JsonElement n1 = JsonElementFactory.CreateArray(new List <object> {
                1, "abc", false
            });
            JsonElement n2 = JsonElementFactory.CreateArray(new List <object> {
                1, "abc", false
            });
            bool result = JsonElementEqualityComparer.Default.Equals(n1, n2);

            Assert.True(result);
        }
        public MainArmTemplateFile(string path, string content)
            : base(path)
        {
            this.Content = content;

            this.lazyRootElement = new(() => JsonElementFactory.CreateElement(content));
            this.lazyParameters  = new(() => !lazyRootElement.Value.TryGetProperty("parameters", out var parametersElement)
                ? Enumerable.Empty <MainArmTemplateParameter>()
                : parametersElement.EnumerateObject().Select(ToParameter));
            this.lazyOutputs = new(() => !lazyRootElement.Value.TryGetProperty("outputs", out var outputsElement)
                    ? Enumerable.Empty <MainArmTemplateOutput>()
                    : outputsElement.EnumerateObject().Select(ToOutput));
        }
        public void PropertyFilterSubExpression_LengthOnArray_ReturnsCorrectValue()
        {
            string      json = "{ \"a\": [ \"abc\" ] }";
            JsonElement data = JsonDocument.Parse(json).RootElement;

            FilterSubExpression exp = FilterParser.Parse(FilterExpressionTokenizer.Tokenize("@.a.length == @.b"));

            exp = (exp as ComparisonFilterSubExpression)?.LeftSide as PropertyFilterSubExpression;
            Assert.NotNull(exp);

            JsonElement expected = JsonElementFactory.CreateNumber(1);
            JsonElement result   = exp.Execute(data);

            Assert.Equal(expected, result, JsonElementEqualityComparer.Default);
        }
        public override JsonElement Execute(JsonElement input)
        {
            JsonElement result = JsonElementFactory.CreateNull();

            if (PropertyChain.Length == 0)
            {
                result = input;
            }

            foreach (string p in PropertyChain)
            {
                if (p == "length" && input.TryGetArrayOrStringLength(out int length))
                {
                    result = JsonElementFactory.CreateNumber(length);
                }
                else if (input.ValueKind == JsonValueKind.Object && input.TryGetProperty(p, out JsonElement t))
                {
                    result = t;
                    input  = t;
                }
                else
                {
                    return(JsonElementFactory.CreateNull());
                }
            }

            if (IsWhildcard)
            {
                JsonElement[] resultArray = new JsonElement[0];
                if (result.ValueKind == JsonValueKind.Object)
                {
                    resultArray = result.EnumerateObject().Select(x => x.Value).ToArray();
                }
                else if (result.ValueKind == JsonValueKind.Array)
                {
                    return(result);
                }

                return(JsonElementFactory.CreateArray(resultArray));
            }

            if (IsRecursive)
            {
                return(JsonElementFactory.CreateArray(result.EnumerateRecursively()));
            }

            return(result);
        }
        public static MainArmTemplateParametersFile ReadFromFileSystem(IFileSystem fileSystem)
        {
            var path = fileSystem.Path.GetFullPath(FileName);

            try
            {
                var content     = fileSystem.File.ReadAllText(path);
                var jsonElement = JsonElementFactory.CreateElement(content);

                return(new(path, content, jsonElement));
            }
            catch (JsonException jsonException)
            {
                throw new BicepException($"The ARM template parameters file \"{path}\" is not a valid JSON file. {jsonException.Message}");
            }
        }
Beispiel #18
0
        public static MetadataFile ReadFromFileSystem(IFileSystem fileSystem)
        {
            var path = fileSystem.Path.GetFullPath(FileName);

            try
            {
                using var stream = fileSystem.FileStream.Create(path, FileMode.Open, FileAccess.Read);
                var jsonElement = JsonElementFactory.CreateElement(stream);

                return(new(path, jsonElement));
            }
            catch (JsonException jsonException)
            {
                throw new BicepException($"The metadata file \"{path}\" is not a valid JSON file. {jsonException.Message}");
            }
        }
        public static MainArmTemplateParametersFile Generate(IFileSystem fileSystem, MainArmTemplateFile mainArmTemplateFile)
        {
            var bufferWriter = new ArrayBufferWriter <byte>();

            using (var writer = new Utf8JsonWriter(bufferWriter, new JsonWriterOptions {
                Indented = true
            }))
            {
                writer.WriteStartObject();

                writer.WriteString("$schema", "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#");
                writer.WriteString("contentVersion", "1.0.0.0");

                writer.WritePropertyName("parameters");
                writer.WriteStartObject();

                var parameterInstances = GenerateParameterInstances(fileSystem, mainArmTemplateFile);

                foreach (var(parameterName, parameterValue) in parameterInstances)
                {
                    writer.WritePropertyName(parameterName);
                    writer.WriteStartObject();

                    if (parameterValue is not null)
                    {
                        writer.WritePropertyName("value");
                        JsonElementFactory.CreateElement(parameterValue).WriteTo(writer);
                    }

                    writer.WriteEndObject();
                }

                writer.WriteEndObject();

                writer.WriteEndObject();
            }

            var path        = fileSystem.Path.GetFullPath(FileName);
            var content     = Encoding.UTF8.GetString(bufferWriter.WrittenSpan);
            var rootElement = JsonElementFactory.CreateElement(bufferWriter.WrittenMemory);

            return(new(path, content, rootElement));
        }
Beispiel #20
0
 private static JsonElement GetParameters(string parameterFilePath)
 {
     if (string.IsNullOrWhiteSpace(parameterFilePath))
     {
         return(JsonElementFactory.CreateElement("{}"));
     }
     else
     {
         try
         {
             string text = File.ReadAllText(parameterFilePath);
             return(JsonElementFactory.CreateElement(text));
         }
         catch (Exception e)
         {
             throw new Exception(string.Format(LangServerResources.InvalidParameterFileDeploymentFailedMessage, e.Message));
         }
     }
 }
Beispiel #21
0
        public static VersionFile ReadFromFileSystem(IFileSystem fileSystem)
        {
            var path = fileSystem.Path.GetFullPath(FileName);

            try
            {
                var content     = fileSystem.File.ReadAllText(FileName);
                var rootElement = JsonElementFactory.CreateElement(content);

                if (rootElement.ValueKind != JsonValueKind.Object)
                {
                    throw new BicepException($"The version file \"{path}\" must contain a JSON object at the root level.");
                }

                return(new(path, content, rootElement));
            }
            catch (JsonException jsonException)
            {
                throw new BicepException($"The version file \"{path}\" is not a valid JSON file. {jsonException.Message}");
            }
        }
        public override JsonElement Execute(JsonElement input)
        {
            if (input.IsNullOrUndefined())
            {
                return(JsonElementFactory.CreateNull());
            }

            List <JsonElement> args = Arguments.Select(x => x.Execute(input)).ToList();

            input = CalledOnExpression.Execute(input);

            if (input.ValueKind == JsonValueKind.String && input.TryExecuteStringMethod(MethodName, args, out JsonElement result1))
            {
                return(result1);
            }

            if (input.ValueKind == JsonValueKind.Array && input.TryExecuteArrayMethod(MethodName, args, out JsonElement result2))
            {
                return(result2);
            }

            return(JsonElementFactory.CreateNull());
        }
        public override JsonElement Execute(JsonElement input)
        {
            bool left = LeftSide.Execute(input).IsTruthy();

            if (left && IsOr)
            {
                return(JsonElementFactory.CreateBool(true));
            }

            bool right = RightSide.Execute(input).IsTruthy();

            bool result;

            if (IsAnd)
            {
                result = left && right;
            }
            else
            {
                result = left || right;
            }

            return(JsonElementFactory.CreateBool(result));
        }
Beispiel #24
0
        private void Validate(string filePath, JsonSchema schema, JsonElement element)
        {
            this.logger.LogInformation("Validating \"{FilePath}\" against JSON schema...", filePath);

            var results = schema.Validate(element, new ValidationOptions
            {
                OutputFormat = OutputFormat.Basic, // Indicates that all nodes will be listed as children of the top node.
                ValidateAs   = Draft.Draft7
            });

            if (!results.IsValid)
            {
                var errorMessageBuilder = new StringBuilder();
                errorMessageBuilder.AppendLine($"The file \"{filePath}\" is invalid:");

                // TODO: enumerable.
                var invalidResults = results.NestedResults.Count == 0 ? new[] { results } : results.NestedResults;
                var shouldSkipAdditionalPropertyError = invalidResults.Any(x => !IsAdditionalPropertyError(x));

                foreach (var result in invalidResults)
                {
                    var isAdditionalPropertyError = IsAdditionalPropertyError(result);

                    if (isAdditionalPropertyError && shouldSkipAdditionalPropertyError)
                    {
                        // According to the JSON schema spec, if any non-additional property is not valid, "properties" doesn't
                        // generate any annotaion. As a result, "additionalProperties": false applies to all properties, which
                        // creates additional property errors even for non-additional properties. To make it less confusing,
                        // we skip additional property errors if there are other error types.
                        // See https://github.com/gregsdennis/json-everything/issues/39#issuecomment-730116851 for details.
                        continue;
                    }

                    errorMessageBuilder.Append("  ");
                    errorMessageBuilder.Append(result.InstanceLocation.Source);
                    errorMessageBuilder.Append(": ");

                    if (isAdditionalPropertyError)
                    {
                        // The built-in error message is "All values fail against the false schema" which is not very intuitive.
                        errorMessageBuilder.AppendLine("The property is not allowed");

                        // All errors are additional property errors. Only keep the first one as the others could be on the parent
                        // properties which are triggered by the child property, e.g., an additional property /properties/foo/bar
                        // can make /properties/foo fail against the "additionalProperties": false check if it is also declared on
                        // the property foo. Technically this complies with what the spec defines and is what Json.Schema implements,
                        // but it may confuse users.
                        break;
                    }
                    else if (IsRegexError(result))
                    {
                        // The built-in error message does not include the regex pattern.
                        var schemaElement = JsonElementFactory.CreateElement(JsonSerializer.Serialize(schema));
                        var regex         = result.SchemaLocation.Evaluate(schemaElement);
                        errorMessageBuilder.AppendLine($"Value does not match the pattern of \"{regex}\"");
                    }
                    else
                    {
                        errorMessageBuilder.AppendLine(result.Message);
                    }
                }

                throw new InvalidModuleException(errorMessageBuilder.ToString());
            }
        }
 public virtual void WriteTo(Utf8JsonWriter writer) => JsonElementFactory.CreateElement(this.Data).WriteTo(writer);
Beispiel #26
0
        /// <summary>
        /// Starts a deployment at provided target scope and returns <see cref="BicepDeploymentStartResponse"/>.
        /// </summary>
        /// <param name="deploymentCollectionProvider">deployment collection provider</param>
        /// <param name="armClient">arm client</param>
        /// <param name="documentPath">path to bicep file used in deployment</param>
        /// <param name="template">template used in deployment</param>
        /// <param name="parametersFilePath">path to parameter file used in deployment</param>
        /// <param name="id">id string to create the ResourceIdentifier from</param>
        /// <param name="scope">target scope</param>
        /// <param name="location">location to store the deployment data</param>
        /// <param name="deploymentId">deployment id</param>
        /// <param name="parametersFileName">parameters file name</param>
        /// <param name="parametersFileUpdateOption"><see cref="ParametersFileUpdateOption"/>update, create or overwrite parameters file</param>
        /// <param name="updatedDeploymentParameters">parameters that were updated during deployment flow</param>
        /// <param name="portalUrl">azure management portal URL</param>
        /// <param name="deploymentName">deployment name</param>
        /// <param name="deploymentOperationsCache">deployment operations cache that needs to be updated</param>
        /// <returns><see cref="BicepDeploymentStartResponse"/></returns>
        public static async Task <BicepDeploymentStartResponse> StartDeploymentAsync(
            IDeploymentCollectionProvider deploymentCollectionProvider,
            ArmClient armClient,
            string documentPath,
            string template,
            string parametersFilePath,
            string id,
            string scope,
            string location,
            string deploymentId,
            string parametersFileName,
            ParametersFileUpdateOption parametersFileUpdateOption,
            List <BicepUpdatedDeploymentParameter> updatedDeploymentParameters,
            string portalUrl,
            string deploymentName,
            IDeploymentOperationsCache deploymentOperationsCache)
        {
            if ((scope == LanguageConstants.TargetScopeTypeSubscription ||
                 scope == LanguageConstants.TargetScopeTypeManagementGroup) &&
                string.IsNullOrWhiteSpace(location))
            {
                return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.MissingLocationDeploymentFailedMessage, documentPath), null));
            }

            ArmDeploymentCollection?deploymentCollection;
            var resourceIdentifier = new ResourceIdentifier(id);

            try
            {
                deploymentCollection = deploymentCollectionProvider.GetDeploymentCollection(armClient, resourceIdentifier, scope);
            }
            catch (Exception e)
            {
                return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.DeploymentFailedWithExceptionMessage, documentPath, e.Message), null));
            }

            if (deploymentCollection is not null)
            {
                JsonElement parameters;

                try
                {
                    var updatedParametersFileContents = DeploymentParametersHelper.GetUpdatedParametersFileContents(documentPath, parametersFileName, parametersFilePath, parametersFileUpdateOption, updatedDeploymentParameters);
                    parameters = JsonElementFactory.CreateElement(updatedParametersFileContents);
                }
                catch (Exception e)
                {
                    return(new BicepDeploymentStartResponse(false, e.Message, null));
                }

                var deploymentProperties = new ArmDeploymentProperties(ArmDeploymentMode.Incremental)
                {
                    Template   = new BinaryData(JsonDocument.Parse(template).RootElement),
                    Parameters = new BinaryData(parameters)
                };
                var armDeploymentContent = new ArmDeploymentContent(deploymentProperties)
                {
                    Location = location,
                };

                try
                {
                    var deploymentOperation = await deploymentCollection.CreateOrUpdateAsync(WaitUntil.Started, deploymentName, armDeploymentContent);

                    if (deploymentOperation is null)
                    {
                        return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.DeploymentFailedMessage, documentPath), null));
                    }

                    deploymentOperationsCache.CacheDeploymentOperation(deploymentId, deploymentOperation);

                    var linkToDeploymentInAzurePortal = GetLinkToDeploymentInAzurePortal(portalUrl, id, deploymentName);

                    return(new BicepDeploymentStartResponse(
                               true,
                               string.Format(LangServerResources.DeploymentStartedMessage, documentPath),
                               string.Format(LangServerResources.ViewDeploymentInPortalMessage, linkToDeploymentInAzurePortal)));
                }
                catch (Exception e)
                {
                    return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.DeploymentFailedWithExceptionMessage, documentPath, e.Message), null));
                }
            }

            return(new BicepDeploymentStartResponse(false, string.Format(LangServerResources.DeploymentFailedMessage, documentPath), null));
        }
 public StringConstantFilterSubExpression(string value)
 {
     Value    = value ?? throw new ArgumentNullException(nameof(value));
     _element = JsonElementFactory.CreateString(value);
 }
 public BooleanConstantFilterSubExpression(bool value)
 {
     Value    = value;
     _element = JsonElementFactory.CreateBool(value);
 }
 public NumberConstantFilterSubExpression(double value)
 {
     Value    = value;
     _element = JsonElementFactory.CreateNumber(value);
 }