Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds additional linting to make description required (solves #264) #266

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import java.util.UUID
import scopt.OptionParser

// Schema DDL
import com.snowplowanalytics.iglu.schemaddl.jsonschema.SanityLinter.{ SeverityLevel, FirstLevel, SecondLevel }
import com.snowplowanalytics.iglu.schemaddl.jsonschema.SanityLinter.{ SeverityLevel, FirstLevel, SecondLevel, ThirdLevel }

// This library
import PushCommand._
Expand Down Expand Up @@ -93,6 +93,7 @@ object Command {
implicit val severityLevelRead: scopt.Read[SeverityLevel] = scopt.Read.reads {
case "1" => FirstLevel
case "2" => SecondLevel
case "3" => ThirdLevel
case l => throw new IllegalArgumentException(s"Error: $l is invalid severity level")
}

Expand Down
4 changes: 2 additions & 2 deletions 0-common/schema-ddl/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Also there's ``schemaddl.generators.redshift.Ddl`` module providing AST-like str

### JSON Paths

Amazon Redhshift uses **[COPY] [redshift-copy]** command to load data into table.
Amazon Redshift uses **[COPY] [redshift-copy]** command to load data into table.
To map data into columns JSONPaths file used.
It may be generated with ``schemaddl.generators.redshift.JsonPathGenerator.getJsonPathsFile`` method.
Which accepts list of ``schemaddl.generators.redshift.Ddl.Column`` objects (which can be taken from ``Table`` DDL object) and returns JSONPaths file as a string.
Expand All @@ -38,7 +38,7 @@ but JSONPaths file always should have the same order of fields and thus we canno

## Copyright and License

Schema DDL is copyright 2014-2016 Snowplow Analytics Ltd.
Schema DDL is copyright 2014-2017 Snowplow Analytics Ltd.

Licensed under the **[Apache License, Version 2.0] [license]** (the "License");
you may not use this software except in compliance with the License.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ object CommonProperties {
*/
case class OneOf(value: List[Schema]) extends JsonSchemaProperty { def keyName = "oneOf" }


/**
* Type representing value for `description` key
*
* @see http://json-schema.org/latest/json-schema-validation.html#rfc.section.7.2
*/
case class Description(value: String) extends JsonSchemaProperty {
def keyName = "description"
}

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,20 @@ object SanityLinter {
}
}

// Third Severity Level

/**
* Check that each 'field' contains a description property
*/
val lintDescriptionPresent: Linter = (schema: Schema) => {
(schema.description) match {
case Some(_) => propertySuccess
case None => s"${schema.`type`.get} Schema doesn't contain description property".failure
case _ => propertySuccess
}
}


trait SeverityLevel {
def linters: List[Linter]
}
Expand All @@ -305,5 +319,9 @@ object SanityLinter {
case object SecondLevel extends SeverityLevel {
val linters = FirstLevel.linters ++ List(lintMinMaxPresent, lintMaxLength)
}

case object ThirdLevel extends SeverityLevel {
val linters = FirstLevel.linters ++ SecondLevel.linters ++ List(lintDescriptionPresent)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,13 @@ case class Schema(
// common
`type`: Option[Type] = None,
enum: Option[Enum] = None,
oneOf: Option[OneOf] = None
oneOf: Option[OneOf] = None,
description: Option[Description] = None
) {

private[iglu] val allProperties = List(multipleOf, minimum, maximum, maxLength, minLength,
pattern, format, items, additionalItems, minItems, maxItems, properties,
additionalProperties, required, patternProperties, `type`, enum, oneOf)
additionalProperties, required, patternProperties, `type`, enum, oneOf, description)

/**
* Concise representation of Schema object
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ object CommonSerializers {
}
))

object DescriptionSerializer extends CustomSerializer[Description](x => (
{
case JString(value) => Description(value)
case x => throw new MappingException(x + " isn't valid description")
},

{
case Description(value) => JString(value)
}
))


object EnumSerializer extends CustomSerializer[Enum](_ => (
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ object Json4sFromSchema {
CommonSerializers.TypeSerializer +
CommonSerializers.EnumSerializer +
CommonSerializers.OneOfSerializer +
CommonSerializers.DescriptionSerializer +
NumberSerializers.MaximumSerializer +
NumberSerializers.MinimumSerializer +
NumberSerializers.MultipleOfSerializer +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ object Json4sToSchema {
CommonSerializers.TypeSerializer +
CommonSerializers.EnumSerializer +
CommonSerializers.OneOfSerializer +
CommonSerializers.DescriptionSerializer +
NumberSerializers.MaximumSerializer +
NumberSerializers.MinimumSerializer +
NumberSerializers.MultipleOfSerializer +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class SanityLinterSpec extends Specification { def is = s2"""
recognize impossibility to fulfill required property $e3
recognize errors for second severity level $e4
recognize error in the middle of object $e5
recognize errors for third severity level $e6
"""

def e1 = {
Expand Down Expand Up @@ -176,4 +177,57 @@ class SanityLinterSpec extends Specification { def is = s2"""
)

}

def e6 = {
val schema = Schema.parse(parse(
"""
|{
| "type": "object",
| "description": "Placeholder object",
| "properties": {
| "sku": {
| "type": "string",
| "maxLength": 10
| },
| "name": {
| "type": "string",
| "maxLength": 10
| },
| "category": {
| "type": "string",
| "maxLength": 10
| },
| "unitPrice": {
| "type": "number",
| "minimum": 0,
| "maximum": 1
| },
| "quantity": {
| "type": "number",
| "minimum": 0,
| "maximum": 1,
| "description": "Quantity (whole number)"
| },
| "currency": {
| "type": "string",
| "maxLength": 10,
| "description": "Store currency code"
| }
| },
| "required": ["sku", "quantity"],
| "additionalProperties": false
|}
""".stripMargin
)).get

SanityLinter.lint(schema, SanityLinter.ThirdLevel) must beEqualTo(
Failure(NonEmptyList(
"String Schema doesn't contain description property",
"String Schema doesn't contain description property",
"String Schema doesn't contain description property",
"Number Schema doesn't contain description property"
))
)

}
}