-
Notifications
You must be signed in to change notification settings - Fork 28
validation_system
The current validation system in toolkit has many weak points. This article documents a proposed new architecture for validation components.
Use the same execution engine we have now. Modifications are acceptable.
Current validators and new-style validators must run together in the (updated) validation engine. This allows a gradual shift.
Assertions are first class objects so that tools can be written to analyse and manipulate them. A first class object in Java/Groovy is the Class so an assertion is defined as a class.
There are multiple validation contexts. They must all be dealt with. Some are: Metadata class, Assertion class (test engine).
Branching must be accommodated. By branching I mean that the execution of an assertion updates the validation context thus determining what validation steps are executed. An example is a message received by the proxy. The inputs are HTTP header and HTTP body. Once the header is examined it can be decide whether SIMPLE SOAP model or MTOM model is executed. In some cases the validation request will include information on what transaction type is expected. In other cases not.
An assertion is a class (Java or Groovy). Assertions are grouped into bundles by the Java package they belong in. As a result the assertion database is a Java Classpath. The Java Reflection API is used to scan the assertion database. Assertion bundles are grouped in other bundles. This grouping is done to help organize for the editor the assertions. It plays no role in the order of execution or the selection for execution.
An assertion has metadata associated with it. Since an assertion is a class, the metadata is accessible via a getter.
An assertion group/bundle has metadata associated with it. Since an assertion group/bundle is a Java package, the metadata is defined by / held in a class. This special class is identified by its implementing a marker interface. Group metadata is optional. There will be defaults for all settings.
Assertion group metadata may contain:
-
Order of execution of assertions in the group. (List) This is a gettable list of class objects (MyClass.class in Java). If this list does not exist or is empty then the assertions are executed in arbitrary order. Assertions defined in the group that are not included in the order of execution list are executed in arbitrary order after the listed assertions.
-
Actor, transaction, option, assertion identification. A child group inherits and may further constrain this identification.
-
Inherited assertion groups
Assertion id (String) - Formally defines the assertion.
Assertion text (String) - Description output to the validation report.
Macro assertion id (List) - The assertion id identified in Gazelle assertion manager. Adding this linkage defines this micro assertion as supporting the indicated macro assertion.
PTO - PTO stands for Profile/Transaction/Option, a context labeling scheme for assertions. Profiles, transactions, and options each have a ID labeling them. In this way PTO, in the form of a path or URN, is a context label for an assertion. As a path it would be written /profile/transaction/option. As a URN it would be
urn:Assertion:profileID:transactionID:optionID.
A blank level such as urn:Assertion:xds:reg
would indicate that the assertion applies to any metadata in the XDS profile, Register transaction. It is not selective on option.
The PTO is specified as a list of PTO values with OR semantics. If the effective PTO (in the validator engine) is compatible with any PTO in this list then the assertion or assertion group is selected for execution.
To know the validation requirements, the actor, transaction, and option(s) must be identified. Validator group(s) are optionally tagged with the profile, transaction, and option(s) (PTO metadata) that describes the grouped validators. A validator group with no PTO metadata inherits the PTO metadata from its parent. In defining this inheritance, a sub-group may add or remove PTO metadata tags thus declaring its context as a delta off its parent PTO. This delta could be specified in two ways: as a new absolute PTO that applies to this group and its children, overriding the parent PTO, or as a delta off the parent PTO. No syntax is established to describe PTO deltas (yet).
Assertion groups are reusable. An example is the Register On-Demand Document transaction. It is a Register transaction with some assertions added and some removed. This transaction should be defined separately from the Register transaction, should inherit the assertions defined for Register, and define a delta off its parent. The delta would remove some validation requirements and add new ones.
Note that the following examples exist to identify semantics. This is not a syntax proposal.
A metadata element such as
inherits urn:Assertion:xds:register
in the top level On-Demand assertion group indicates that all assertions selected by urn:Assertion:xds:register apply in this group. Assertions defined in this group would add to that inherited set. Assertions from the above inherits statement to be excluded are
except AREG0034, AREG0089, AREG2390
where these identifiers are assertion IDs defined for the Register transaction.
Note that the Java class system gives us referential integrity. These assertions are classes so the IDs for the assertions are really class references. If the classes do not exist then the build system will complain. In Java the reference would be AREG00334.class and in Groovy it would be just AREG0034. In both cases the package names are not shown for readability. In the code the package names would have to be present. Tool based refactoring would automatically update the package names and the assertion ids if necessary.
The selection of validator groups to execute and the order of their execution is controlled by a validator classpath.
Assertion class are identified through Java Reflection based on an interface. This interface includes
public interface Assertion {
List<String> ptos;
Class<?> getAssertionId();
List<String> getMacroAssertions();
String getText();
boolean isOptional();
boolean execute();
}
The isOptional value controls how the assertion is reported. Non-optional assertions that return false from execute() fail. Optional assertions that return false from execute() report the status as an informational message instead of as a failure.
Assertion group classes are optional but if present there may be a single class of this type in a package. A sub-package may have its own assertion group class. The assertion group class is identified by its implementing an interface
public interface AssertionGroup {
List<String> getMacroAssertions();
List<Class> getInherits();
List<Class> getExcepts();
boolean execute();
}
Note that getInherits() must return classes, not packages, but it is intended to reference assertion groups, not assertions. So getInherits() must reference assertion group classes. This implies that even though having an assertion group class in an assertion group is optional, it is required if another assertion group references it through an inherits relationship.
Macro assertions are maintained in Gazelle with string-based identifiers.
If an assertion group class is missing (it is optional) then the validation system has a default implementation. In most cases the only reason to define an assertion group is to specifiy the metadata. There will be a base implementation of this class, AssertionGroupBase, that will implement the default execute method. Only in special cases will this need to be overridden.
There are a collection of assertion types, each defined by an (probably) abstract base class that is extended. Known assertion types are
IsNull
IsNotNull
IsEquals
IsLessThan ...
IsContainedIn
IsNotContainedIn
IsSubStringOf
IsNotSubStringOf
There are several parts to validating a slot
Single or multi-value - boolean passed to Slot#validate
Allowed/required in this object - handled in the parent object
Format of the values - this is delegated to a FormatValidator which is passed into th Slot#validate method.
In part these are built into the current AbstractRegistryObject class which DocumentEntry extends:
abstract public void validateSlotsLegal(ErrorRecorder er);
abstract public void validateRequiredSlotsPresent(ErrorRecorder er, ValidationContext vc);
abstract public void validateSlotsCodedCorrectly(ErrorRecorder er, ValidationContext vc);
The parent object all extend AbstractRegistryObject which has the validationSlot method. A call is made for each slot that is allowed. The calls are made from DocumentEntry.validateSlotsCodedCorrectly. A separate validation is made to determine if the slot is acceptable. This is driven off of List DocumentEntry.definedSlots and List<String DocumentEntry.requiredSlots.
These types of slot validators are needed: slot name acceptable, slot name required, multivalued, value format.
List<String> pto = ['urn:Assertion:xds:pnr', 'urn:Assertion:xds:r']
String assertionId = 'xxxx'
String macroAssertion = 'yyyy'
String text = 'DocumentEntry acceptable Slot name'
List<String> slotNames = [
'creationTime',
'languageCode' // ...
]
public boolean execute() { slotNames.contains slot.name }
Where does slot.name come from? What makes this DocumentEntry specific? These need to come from some context object. Who generates this context object?
AbstractRegistryObject has List. This assertion could accept an interface as a parameter containing
List<Slot> getSlots()
What links this to a DocumentEntry? There could be a factory/manager call
getAssertions(SlotAssertion, DocumentEntryContext).each { it.execute() }
BUT THIS DOES NOT ITERATE ACROSS ALL SLOTS IN THE DOCUMENTENTRY!!!!!!! This has to be nested inside something that does.
where getAssertions returns
List<Assertion>
The above assertion class could be
implements SlotAssertion, DocumentEntryContext
which could be just marker interfaces used by Reflection API.
The getAssertions method takes care of the finding of the assertion classes and building the instances. The finding would be to match the two marker interfaces (SlotAssertion, DocumentEntryContext) with the class definition of the assertion which would carry the above implements clause.
The assertion class has grown to
class MyAssertion implements SlotAssertion, DocumentEntryContext {
List<String> pto = ['urn:Assertion:xds:pnr', 'urn:Assertion:xds:r']
String assertionId = 'xxxx'
String macroAssertion = 'yyyy'
String text = 'DocumentEntry acceptable Slot name'
List<String> slotNames = [
'creationTime',
'languageCode' // ...
]
public boolean execute() { slotNames.contains slot.name }
}
The marker interfaces SlotAssertion and DocumentEntryContext make the validator findable. The pto value declares the PTO context. AssertionId and macroAssertion support tooling. Text supports reporting. SlotNames supports the implementation of the execute method.
To make the code usable, getAssertions would have to be more like
getAssertions(List<Class<?>>)
Is that doable? Yes.
That handles the callee. What about the caller?
There would have to be a DocumentEntry assertion group tied to pnr and r assertions (matching this assertion). The execute method would use a bunch of yet undefined helper classes to iterate through the guts of a DocumentEntry.
Identify the validation models used:
-
Metadata class
-
Assertion class
MessageValidatorFactory#validateBasedOnValidationContext becomes ValidationContextValidationFactory
vs
MessageValidatorFactory#validateBasedOnRootElement becomes RootElementValidationFactory
separate out into two different factory classes
Toolkit
Downloads
Installing Toolkit
Configuring Toolkit for Imaging Tests
Reporting Toolkit Installation Problems
Environment
Test Session
Conformance Test Tool
Writing Conformance Tests
Overview of Imaging Tests
Test Context Definition
Launching Conformance Tool from Gazelle
Inspector
External Cache
Support Tools
Test Organization
Configuring Test Kits
Managing Multiple Test Kits
SAML Validation against Gazelle
Renaming Toolkit
Toolkit API
Managing system configurations
Configuring Toolkit for Connectathon
Developer's blog