assert¶
assert is one or many optional nested definitions within UDTs defining fields that allow object level
validations to be expressed.
Assert is used for object level validation. The rule is applied on a single object.
- The validation is to be applied on ‘complete’ objects, i.e., not partially constructed. Language
implementations may apply
assertas a object post-construction phase to validate that the object adheres to a set of validation rules. - entity, key, record, trait and union
support nested
assertdefinitions for object validation. - An
assertblock can define local variable using theletkeyword and use expressons syntax. Fields within objects cannot be modified with anassert. For example put cannot be called on amapdata-typed field, although locally declared variables can be modified. - An
asserthas an associated name. Multiple uniquely named asserts can be associated with a user-defined type. Asserts defined in traits take part in UDTs that includes the trait.
// Outer type definition ... assert <Unique assertion identifier> { < Expressions > } ...
An
assertdoes not return a value. Within anassert,raisecan be used to log one of manyerrororwarning. E.g. ..if ( Age < 18 ) raise error("Not an adult")
ALFA assert and expression syntax has been designed from ground-up using concepts such as higher-order functions. The expressions can be generated into Java, C#, Python, Scala, TypeScript etc., therefore validation can be executed in optimised native code.
An ALFA service, can also be used as part of
assertexpressions. Therefore using a service, it is possible to call out to external code as part of object validation.Errors or warning raised from
assertcan reference field values using$fieldsyntax in order to report meaningful errors. For example"Student $name aged $age cannot be in $schoolType school".
raise syntax¶
In addition to the message, raise can specify the Data Quality Dimension that should be given to the raised message/event. E.g.
if ( Age < 18 ) raise error(Confirmity, "Not an adult")
If a dimension is not specified, by default raise will use Unclassified as the dimension.
List of Data Quality Dimensions¶
The following dimensions can be specified to raise.
- Accuracy
- Completeness
- Conformity
- Consistency
- Coverage
- Integrity
- Provenance
- Timeliness
- Uniqueness
- Validity
- Unclassified
Simple assert Example¶
In the example below, object level validation is required to validate contents of 2 fields.
The CorrectSchool assert block contains expressions to that will determine if the schoolType
is assigned correctly.
enum SchoolType {
Primary Junior Senior
}
record Student {
name : string
dob : date
schoolType : SchoolType
assert SchoolingAge {
let age : int = year( dateDiff( today(), dob ) )
if ( age > 18 )
raise warning( Accuracy, "Student ${name} aged ${age} is an adult" )
}
assert CorrectSchool {
let age : int = year( dateDiff( today(), dob ) )
let errored = ( age < 7 && schoolType != SchoolType.Primary ) ||
( age >= 7 && and age < 12 && schoolType != SchoolType.Junior ) ||
( age >= 12 && schoolType != SchoolType.Senior ) ||
if ( errored )
raise error( Accuracy, "Student aged ${age} cannot be in ${schoolType} school" )
}
}
Decoupling Model Definition and Asserts¶
Using fragment definitions, existing types can be extended with assert statements.
The above example can be decoupled into 2 declarations as shown below.
Only fields are defined for the Teacher type.
record Teacher {
name : string
dob : date
schoolType : SchoolType
}
Student defined as a fragment with all assert definitions.
fragment record Teacher {
assert QualifiedAge {
let age : int = year( dateDiff( today(), dob ) )
if ( age < 18 )
raise warning( "Teacher ${name} aged ${age} is not an adult" )
}
}
Generated code for assert¶
The assert expressions are fully generated into underlying code, where supported in ALFA.
This is not applicable for some targets such as Avro or Protocol Buffers as those do not support such a feature.
Given the small example below:
record Passenger {
Name : string
LuggageWeights : list< int >
assert LuggageWeightLimit {
let total = reduce( LuggageWeights, 0, ( acc, w ) => acc + w )
if ( total > 100 )
raise warning("Passenger ${Name} has excess luggage")
}
}
The Java generates the following function corresponding to the assert definition. The function generated is called to validate the object:
private void __assertLuggageWeightLimit() {
int _total =
_luggageWeights
.stream()
.reduce(
0,
(_acc, _w) -> {
return _acc + _w;
},
(c1, c2) -> {
throw new UnsupportedOperationException();
});
if ( _total > 100 )
builderConfig().assertListener().addAlert("Passenger " + _name + " has excess luggage")
}