private static void ValidateProperties()

in src/Elastic.Apm.Specification/Validator.cs [370:514]


		private static void ValidateProperties(Type specType, JsonSchema schema, ImplementationProperty[] properties, TypeValidationResult result)
		{
			IReadOnlyDictionary<string, JsonSchemaProperty> schemaProperties;

			try
			{
				schemaProperties = schema.ActualProperties;
			}
			catch (InvalidOperationException e)
			{
				throw new JsonSchemaException($"Cannot get schema properties for {schema.GetNameOrSpecificationId()}. {e.Message}", e);
			}

			foreach (var kv in schemaProperties)
			{
				var schemaProperty = kv.Value.ActualSchema;
				var name = kv.Value.Name;
				var specTypeProperty = properties.SingleOrDefault(p => p.Name == name);
				if (specTypeProperty == null)
				{
					// No "type" means any type, which maps to None
					if (schemaProperty.Type.HasFlag(JsonObjectType.None) || schemaProperty.Type.HasFlag(JsonObjectType.Null))
					{
						if (result.Validation == Validation.SpecToType)
							result.AddError(TypeValidationError.NotFound(specType, schema.GetNameOrSpecificationId(), name));
					}
					else
						result.AddError(TypeValidationError.NotFound(specType, schema.GetNameOrSpecificationId(), name));

					continue;
				}

				// check certain .NET types first before defaulting to the JSON schema flags.
				// A property might be represented as more than one type e.g. ["integer", "string", "null],
				// so it's better to look at the .NET type first and try to look for the associated JSON schema flag.
				switch (specTypeProperty.PropertyType.FullName)
				{
					case "System.UInt16":
					case "System.Int16":
					case "System.Byte":
					case "System.SByte":
					case "System.UInt32":
					case "System.Single":
					case "System.Decimal":
					case "System.Double":
					case "System.UInt64":
						if (schemaProperty.Type.HasFlag(JsonObjectType.Number))
							ValidateNumber(specType, schema, schemaProperty, specTypeProperty, result);
						else
						{
							result.AddError(
								TypeValidationError.ExpectedType(
									specType,
									specTypeProperty.PropertyType,
									"number",
									schema.GetNameOrSpecificationId(),
									name));
						}
						break;
					case "System.Int64":
					case "System.Int32":
						if (schemaProperty.Type.HasFlag(JsonObjectType.Number))
							ValidateNumber(specType, schema, schemaProperty, specTypeProperty, result);
						else if (schemaProperty.Type.HasFlag(JsonObjectType.Integer))
							ValidateInteger(specType, schema, schemaProperty, specTypeProperty, result);
						else
						{
							result.AddError(
								TypeValidationError.ExpectedType(
									specType,
									specTypeProperty.PropertyType,
									"integer",
									schema.GetNameOrSpecificationId(),
									name));
						}
						break;
					case "System.String":
						if (schemaProperty.Type.HasFlag(JsonObjectType.String))
							ValidateString(specType, schema, schemaProperty, specTypeProperty, result);
						else
						{
							result.AddError(
								TypeValidationError.ExpectedType(
									specType,
									specTypeProperty.PropertyType,
									"string",
									schema.GetNameOrSpecificationId(),
									name));
						}
						break;
					case "System.Boolean":
						if (schemaProperty.Type.HasFlag(JsonObjectType.Boolean))
							ValidateBoolean(specType, schema, schemaProperty, specTypeProperty, result);
						else
						{
							result.AddError(
								TypeValidationError.ExpectedType(
									specType,
									specTypeProperty.PropertyType,
									"boolean",
									schema.GetNameOrSpecificationId(),
									name));
						}
						break;
					default:
						// Are there multiple types? If so, based on the .NET type not being a primitive type, we would expect the presence of the
						// schema "object" or "array" type in the majority of cases, so check these first.
						// For types with custom serialization, the schema type may be a primitive type, so we can't easily statically validate it.
						if (HasMultipleNonNullTypes(schemaProperty.Type))
						{
							if (schemaProperty.Type.HasFlag(JsonObjectType.Object))
								ValidateProperties(specTypeProperty.PropertyType, schemaProperty, result);
							else if (schemaProperty.Type.HasFlag(JsonObjectType.Array) &&
								IsEnumerableType(specTypeProperty.PropertyType, out var elementType))
								ValidateProperties(elementType, schemaProperty.Item.ActualSchema, result);
							else
							{
								result.AddIgnore(new TypeValidationIgnore(schema.GetNameOrSpecificationId(), name,
									$"Cannot statically check type. .NET type '{specType}', schema type '{schemaProperty.Type}'"));
							}
						}
						else if (schemaProperty.Type.HasFlag(JsonObjectType.Boolean))
							ValidateBoolean(specType, schema, schemaProperty, specTypeProperty, result);
						else if (schemaProperty.Type.HasFlag(JsonObjectType.Integer))
							ValidateInteger(specType, schema, schemaProperty, specTypeProperty, result);
						else if (schemaProperty.Type.HasFlag(JsonObjectType.Number))
							ValidateNumber(specType, schema, schemaProperty, specTypeProperty, result);
						else if (schemaProperty.Type.HasFlag(JsonObjectType.String))
						{
							if (schemaProperty.IsEnumeration && specTypeProperty.PropertyType.IsEnum)
								ValidateEnum(specType, schema, schemaProperty, specTypeProperty, result);
							else
								ValidateString(specType, schema, schemaProperty, specTypeProperty, result);
						}
						else if (schemaProperty.Type.HasFlag(JsonObjectType.Object))
							ValidateProperties(specTypeProperty.PropertyType, schemaProperty, result);
						else if (schemaProperty.Type.HasFlag(JsonObjectType.Array))
						{
							if (IsEnumerableType(specTypeProperty.PropertyType, out var elementType))
								ValidateProperties(elementType, schemaProperty.Item.ActualSchema, result);
						}
						break;
				}
			}
		}