Search in sources :

Example 1 with SchemaOrRef

use of com.linkedin.avroutil1.model.SchemaOrRef in project avro-util by linkedin.

the class AvscParserTest method testSelfReference.

@Test
public void testSelfReference() throws Exception {
    String avsc = TestUtil.load("schemas/LongList.avsc");
    AvscParser parser = new AvscParser();
    AvscParseResult result = parser.parse(avsc);
    Assert.assertNull(result.getParseError());
    AvroRecordSchema schema = (AvroRecordSchema) result.getTopLevelSchema();
    // schema.next[1] == schema
    AvroSchemaField nextField = schema.getField("next");
    AvroUnionSchema union = (AvroUnionSchema) nextField.getSchema();
    SchemaOrRef secondBranch = union.getTypes().get(1);
    Assert.assertSame(secondBranch.getSchema(), schema);
}
Also used : SchemaOrRef(com.linkedin.avroutil1.model.SchemaOrRef) AvroUnionSchema(com.linkedin.avroutil1.model.AvroUnionSchema) AvroRecordSchema(com.linkedin.avroutil1.model.AvroRecordSchema) AvroSchemaField(com.linkedin.avroutil1.model.AvroSchemaField) Test(org.testng.annotations.Test)

Example 2 with SchemaOrRef

use of com.linkedin.avroutil1.model.SchemaOrRef in project avro-util by linkedin.

the class AvscParser method parseNamedSchema.

private AvroNamedSchema parseNamedSchema(JsonObjectExt objectNode, AvscFileParseContext context, AvroType avroType, CodeLocation codeLocation, JsonPropertiesContainer extraProps) {
    Located<String> nameStr = getRequiredString(objectNode, "name", () -> avroType + " is a named type");
    Located<String> namespaceStr = getOptionalString(objectNode, "namespace");
    // technically the avro spec does not allow "doc" on type fixed, but screw that
    Located<String> docStr = getOptionalString(objectNode, "doc");
    String name = nameStr.getValue();
    String namespace = namespaceStr != null ? namespaceStr.getValue() : null;
    String doc = docStr != null ? docStr.getValue() : null;
    String schemaSimpleName;
    String schemaNamespace;
    if (name.contains(".")) {
        // the name specified is a full name (namespace included)
        context.addIssue(AvscIssues.useOfFullName(new CodeLocation(context.getUri(), nameStr.getLocation(), nameStr.getLocation()), avroType, name));
        if (namespace != null) {
            // namespace will be ignored, but it's confusing to even list it
            context.addIssue(AvscIssues.ignoredNamespace(new CodeLocation(context.getUri(), namespaceStr.getLocation(), namespaceStr.getLocation()), avroType, namespace, name));
        }
        // TODO - validate names (no ending in dot, no spaces, etc)
        int lastDot = name.lastIndexOf('.');
        schemaSimpleName = name.substring(lastDot + 1);
        schemaNamespace = name.substring(0, lastDot);
    } else {
        schemaSimpleName = name;
        schemaNamespace = namespace;
    }
    // != null
    String contextNamespace = context.getCurrentNamespace();
    boolean namespaceChanged = false;
    // check if context namespace changed
    if (schemaNamespace != null) {
        if (!contextNamespace.equals(schemaNamespace)) {
            context.pushNamespace(schemaNamespace);
            namespaceChanged = true;
            contextNamespace = schemaNamespace;
        }
    }
    AvroNamedSchema namedSchema;
    switch(avroType) {
        case RECORD:
            AvroRecordSchema recordSchema = new AvroRecordSchema(codeLocation, schemaSimpleName, contextNamespace, doc, extraProps);
            JsonArrayExt fieldsNode = getRequiredArray(objectNode, "fields", () -> "all avro records must have fields");
            List<AvroSchemaField> fields = new ArrayList<>(fieldsNode.size());
            for (int fieldNum = 0; fieldNum < fieldsNode.size(); fieldNum++) {
                // !=null
                JsonValueExt fieldDeclNode = (JsonValueExt) fieldsNode.get(fieldNum);
                JsonValue.ValueType fieldNodeType = fieldDeclNode.getValueType();
                if (fieldNodeType != JsonValue.ValueType.OBJECT) {
                    throw new AvroSyntaxException("field " + fieldNum + " for record " + schemaSimpleName + " at " + fieldDeclNode.getStartLocation() + " expected to be an OBJECT, not a " + JsonPUtil.describe(fieldNodeType) + " (" + fieldDeclNode + ")");
                }
                TextLocation fieldStartLocation = Util.convertLocation(fieldDeclNode.getStartLocation());
                TextLocation fieldEndLocation = Util.convertLocation(fieldDeclNode.getEndLocation());
                CodeLocation fieldCodeLocation = new CodeLocation(context.getUri(), fieldStartLocation, fieldEndLocation);
                JsonObjectExt fieldDecl = (JsonObjectExt) fieldDeclNode;
                Located<String> fieldName = getRequiredString(fieldDecl, "name", () -> "all record fields must have a name");
                JsonValueExt fieldTypeNode = getRequiredNode(fieldDecl, "type", () -> "all record fields must have a type");
                SchemaOrRef fieldSchema = parseSchemaDeclOrRef(fieldTypeNode, context, false);
                JsonValueExt fieldDefaultValueNode = fieldDecl.get("default");
                AvroLiteral defaultValue = null;
                if (fieldDefaultValueNode != null) {
                    if (fieldSchema.isResolved()) {
                        LiteralOrIssue defaultValurOrIssue = parseLiteral(fieldDefaultValueNode, fieldSchema.getSchema(), fieldName.getValue(), context);
                        if (defaultValurOrIssue.getIssue() == null) {
                            defaultValue = defaultValurOrIssue.getLiteral();
                        }
                    // TODO - handle issues
                    } else {
                        // TODO - implement delayed default value parsing
                        throw new UnsupportedOperationException("delayed parsing of default value for " + fieldName.getValue() + " TBD");
                    }
                }
                LinkedHashMap<String, JsonValueExt> props = parseExtraProps(fieldDecl, CORE_FIELD_PROPERTIES);
                JsonPropertiesContainer propsContainer = props.isEmpty() ? JsonPropertiesContainer.EMPTY : new JsonPropertiesContainerImpl(props);
                AvroSchemaField field = new AvroSchemaField(fieldCodeLocation, fieldName.getValue(), null, fieldSchema, defaultValue, propsContainer);
                fields.add(field);
            }
            recordSchema.setFields(fields);
            namedSchema = recordSchema;
            break;
        case ENUM:
            JsonArrayExt symbolsNode = getRequiredArray(objectNode, "symbols", () -> "all avro enums must have symbols");
            List<String> symbols = new ArrayList<>(symbolsNode.size());
            for (int ordinal = 0; ordinal < symbolsNode.size(); ordinal++) {
                JsonValueExt symbolNode = (JsonValueExt) symbolsNode.get(ordinal);
                JsonValue.ValueType symbolNodeType = symbolNode.getValueType();
                if (symbolNodeType != JsonValue.ValueType.STRING) {
                    throw new AvroSyntaxException("symbol " + ordinal + " for enum " + schemaSimpleName + " at " + symbolNode.getStartLocation() + " expected to be a STRING, not a " + JsonPUtil.describe(symbolNodeType) + " (" + symbolNode + ")");
                }
                symbols.add(symbolNode.toString());
            }
            String defaultSymbol = null;
            Located<String> defaultStr = getOptionalString(objectNode, "default");
            if (defaultStr != null) {
                defaultSymbol = defaultStr.getValue();
                if (!symbols.contains(defaultSymbol)) {
                    context.addIssue(AvscIssues.badEnumDefaultValue(locationOf(context.getUri(), defaultStr), defaultSymbol, schemaSimpleName, symbols));
                    // TODO - support "fixing" by selecting 1st symbol as default?
                    defaultSymbol = null;
                }
            }
            namedSchema = new AvroEnumSchema(codeLocation, schemaSimpleName, contextNamespace, doc, symbols, defaultSymbol, extraProps);
            break;
        case FIXED:
            JsonValueExt sizeNode = getRequiredNode(objectNode, "size", () -> "fixed types must have a size property");
            if (sizeNode.getValueType() != JsonValue.ValueType.NUMBER || !(((JsonNumberExt) sizeNode).isIntegral())) {
                throw new AvroSyntaxException("size for fixed " + schemaSimpleName + " at " + sizeNode.getStartLocation() + " expected to be an INTEGER, not a " + JsonPUtil.describe(sizeNode.getValueType()) + " (" + sizeNode + ")");
            }
            int fixedSize = ((JsonNumberExt) sizeNode).intValue();
            Parsed<AvroLogicalType> logicalTypeResult = parseLogicalType(objectNode, context, avroType, codeLocation);
            if (logicalTypeResult.hasIssues()) {
                context.addIssues(logicalTypeResult.getIssues());
            }
            namedSchema = new AvroFixedSchema(codeLocation, schemaSimpleName, contextNamespace, doc, fixedSize, logicalTypeResult.getData(), extraProps);
            break;
        default:
            throw new IllegalStateException("unhandled: " + avroType + " for object at " + codeLocation.getStart());
    }
    if (namespaceChanged) {
        context.popNamespace();
    }
    return namedSchema;
}
Also used : AvroSyntaxException(com.linkedin.avroutil1.parser.exceptions.AvroSyntaxException) ArrayList(java.util.ArrayList) JsonPropertiesContainer(com.linkedin.avroutil1.model.JsonPropertiesContainer) AvroNamedSchema(com.linkedin.avroutil1.model.AvroNamedSchema) JsonArrayExt(com.linkedin.avroutil1.parser.jsonpext.JsonArrayExt) AvroEnumSchema(com.linkedin.avroutil1.model.AvroEnumSchema) AvroFixedSchema(com.linkedin.avroutil1.model.AvroFixedSchema) AvroLiteral(com.linkedin.avroutil1.model.AvroLiteral) JsonNumberExt(com.linkedin.avroutil1.parser.jsonpext.JsonNumberExt) CodeLocation(com.linkedin.avroutil1.model.CodeLocation) SchemaOrRef(com.linkedin.avroutil1.model.SchemaOrRef) JsonValue(jakarta.json.JsonValue) AvroRecordSchema(com.linkedin.avroutil1.model.AvroRecordSchema) AvroLogicalType(com.linkedin.avroutil1.model.AvroLogicalType) JsonObjectExt(com.linkedin.avroutil1.parser.jsonpext.JsonObjectExt) JsonValueExt(com.linkedin.avroutil1.parser.jsonpext.JsonValueExt) TextLocation(com.linkedin.avroutil1.model.TextLocation) AvroSchemaField(com.linkedin.avroutil1.model.AvroSchemaField)

Example 3 with SchemaOrRef

use of com.linkedin.avroutil1.model.SchemaOrRef in project avro-util by linkedin.

the class AvscParser method parseComplexSchema.

private SchemaOrRef parseComplexSchema(JsonObjectExt objectNode, AvscFileParseContext context, boolean topLevel) {
    CodeLocation codeLocation = locationOf(context.getUri(), objectNode);
    Located<String> typeStr = getRequiredString(objectNode, "type", () -> "it is a schema declaration");
    AvroType avroType = AvroType.fromJson(typeStr.getValue());
    if (avroType == null) {
        throw new AvroSyntaxException("unknown avro type \"" + typeStr.getValue() + "\" at " + typeStr.getLocation() + ". expecting \"record\", \"enum\" or \"fixed\"");
    }
    LinkedHashMap<String, JsonValueExt> propsMap = parseExtraProps(objectNode, CORE_SCHEMA_PROPERTIES);
    JsonPropertiesContainer props = propsMap.isEmpty() ? JsonPropertiesContainer.EMPTY : new JsonPropertiesContainerImpl(propsMap);
    AvroSchema definedSchema;
    if (avroType.isNamed()) {
        definedSchema = parseNamedSchema(objectNode, context, avroType, codeLocation, props);
    } else if (avroType.isCollection()) {
        definedSchema = parseCollectionSchema(objectNode, context, avroType, codeLocation, props);
    } else if (avroType.isPrimitive()) {
        definedSchema = parseDecoratedPrimitiveSchema(objectNode, context, avroType, codeLocation, props);
    } else {
        throw new IllegalStateException("unhandled avro type " + avroType + " at " + typeStr.getLocation());
    }
    context.defineSchema(definedSchema, topLevel);
    return new SchemaOrRef(codeLocation, definedSchema);
}
Also used : CodeLocation(com.linkedin.avroutil1.model.CodeLocation) AvroSchema(com.linkedin.avroutil1.model.AvroSchema) SchemaOrRef(com.linkedin.avroutil1.model.SchemaOrRef) AvroType(com.linkedin.avroutil1.model.AvroType) AvroSyntaxException(com.linkedin.avroutil1.parser.exceptions.AvroSyntaxException) JsonPropertiesContainer(com.linkedin.avroutil1.model.JsonPropertiesContainer) JsonValueExt(com.linkedin.avroutil1.parser.jsonpext.JsonValueExt)

Example 4 with SchemaOrRef

use of com.linkedin.avroutil1.model.SchemaOrRef in project avro-util by linkedin.

the class AvscParser method parseSimplePrimitiveOrRef.

private SchemaOrRef parseSimplePrimitiveOrRef(JsonStringExt stringNode, AvscFileParseContext context) {
    CodeLocation codeLocation = locationOf(context.getUri(), stringNode);
    String typeString = stringNode.getString();
    AvroType avroType = AvroType.fromJson(typeString);
    // TODO - screen for reserved words??
    if (avroType == null) {
        // assume it's a ref
        return new SchemaOrRef(codeLocation, typeString);
    }
    if (avroType.isPrimitive()) {
        // no logical type information, string representation or props in the schema if we got here
        return new SchemaOrRef(codeLocation, AvroPrimitiveSchema.forType(codeLocation, avroType, null, null, 0, 0, JsonPropertiesContainer.EMPTY));
    }
    // if we got here it means we found something like "record" as a type literal. which is not valid syntax
    throw new AvroSyntaxException("Illegal avro type \"" + typeString + "\" at " + codeLocation.getStart() + ". " + "Expecting a primitive type, an inline type definition or a reference the the fullname of a named type defined elsewhere");
}
Also used : CodeLocation(com.linkedin.avroutil1.model.CodeLocation) SchemaOrRef(com.linkedin.avroutil1.model.SchemaOrRef) AvroType(com.linkedin.avroutil1.model.AvroType) AvroSyntaxException(com.linkedin.avroutil1.parser.exceptions.AvroSyntaxException)

Example 5 with SchemaOrRef

use of com.linkedin.avroutil1.model.SchemaOrRef in project avro-util by linkedin.

the class AvscFileParseContext method resolveReferences.

private void resolveReferences(SchemaOrRef possiblyRef) {
    if (possiblyRef.isResolved()) {
        // either an already- resolved reference or an inline definition
        if (possiblyRef.getDecl() != null) {
            // recurse into inline definitions
            resolveReferences(possiblyRef.getDecl());
        }
    } else {
        // unresolved (and so must be a) reference
        String fullName = possiblyRef.getRef();
        AvroSchema resolved = definedNamedSchemas.get(fullName);
        if (resolved != null) {
            possiblyRef.setResolvedTo(resolved);
        } else {
            externalReferences.computeIfAbsent(fullName, s -> new ArrayList<>(1)).add(possiblyRef);
        }
    }
}
Also used : AvroNamedSchema(com.linkedin.avroutil1.model.AvroNamedSchema) Collection(java.util.Collection) HashMap(java.util.HashMap) AvroSchema(com.linkedin.avroutil1.model.AvroSchema) SchemaOrRef(com.linkedin.avroutil1.model.SchemaOrRef) Deque(java.util.Deque) AvroSchemaField(com.linkedin.avroutil1.model.AvroSchemaField) File(java.io.File) ArrayList(java.util.ArrayList) AvroArraySchema(com.linkedin.avroutil1.model.AvroArraySchema) List(java.util.List) AvroRecordSchema(com.linkedin.avroutil1.model.AvroRecordSchema) Map(java.util.Map) AvroUnionSchema(com.linkedin.avroutil1.model.AvroUnionSchema) AvroSyntaxException(com.linkedin.avroutil1.parser.exceptions.AvroSyntaxException) AvroType(com.linkedin.avroutil1.model.AvroType) URI(java.net.URI) ArrayDeque(java.util.ArrayDeque) AvroMapSchema(com.linkedin.avroutil1.model.AvroMapSchema) AvroSchema(com.linkedin.avroutil1.model.AvroSchema) ArrayList(java.util.ArrayList)

Aggregations

SchemaOrRef (com.linkedin.avroutil1.model.SchemaOrRef)9 AvroRecordSchema (com.linkedin.avroutil1.model.AvroRecordSchema)4 AvroSchemaField (com.linkedin.avroutil1.model.AvroSchemaField)4 AvroType (com.linkedin.avroutil1.model.AvroType)4 AvroUnionSchema (com.linkedin.avroutil1.model.AvroUnionSchema)4 CodeLocation (com.linkedin.avroutil1.model.CodeLocation)4 AvroSyntaxException (com.linkedin.avroutil1.parser.exceptions.AvroSyntaxException)4 JsonValueExt (com.linkedin.avroutil1.parser.jsonpext.JsonValueExt)4 ArrayList (java.util.ArrayList)4 AvroArraySchema (com.linkedin.avroutil1.model.AvroArraySchema)3 AvroMapSchema (com.linkedin.avroutil1.model.AvroMapSchema)3 AvroSchema (com.linkedin.avroutil1.model.AvroSchema)3 AvroNamedSchema (com.linkedin.avroutil1.model.AvroNamedSchema)2 JsonPropertiesContainer (com.linkedin.avroutil1.model.JsonPropertiesContainer)2 JsonValue (jakarta.json.JsonValue)2 HashMap (java.util.HashMap)2 List (java.util.List)2 Map (java.util.Map)2 AvroEnumSchema (com.linkedin.avroutil1.model.AvroEnumSchema)1 AvroFixedSchema (com.linkedin.avroutil1.model.AvroFixedSchema)1