From 07dc5e09b68c5ddb095f178a5ce39a2b84ba07b3 Mon Sep 17 00:00:00 2001 From: Vinyl-Davyl Date: Tue, 23 Apr 2024 21:57:50 +0100 Subject: [PATCH] feat: updated template guides with concerto models in documentation Signed-off-by: Vinyl-Davyl --- docs/model-classes.md | 91 +++++++++++++++++++++ docs/model-concerto.md | 74 +++++++++++++++++ docs/model-decorators.md | 30 +++++++ docs/model-enums.md | 15 ++++ docs/model-namespaces.md | 53 ++++++++++++ docs/model-properties.md | 64 +++++++++++++++ docs/model-relationships.md | 36 ++++++++ docs/model-vocabulary.md | 159 ++++++++++++++++++++++++++++++++++++ 8 files changed, 522 insertions(+) create mode 100644 docs/model-classes.md create mode 100644 docs/model-concerto.md create mode 100644 docs/model-decorators.md create mode 100644 docs/model-enums.md create mode 100644 docs/model-namespaces.md create mode 100644 docs/model-properties.md create mode 100644 docs/model-relationships.md create mode 100644 docs/model-vocabulary.md diff --git a/docs/model-classes.md b/docs/model-classes.md new file mode 100644 index 00000000..618a3d30 --- /dev/null +++ b/docs/model-classes.md @@ -0,0 +1,91 @@ +--- +id: model-classes +title: Classes +--- + +## Concepts + +Concepts are similar to class declarations in most object-oriented languages, in that they may have a super-type and a set of typed properties: + +```js +abstract concept Animal { + o DateTime dob +} + +concept Dog extends Animal { + o String breed +} +``` + +Concepts can be declared `abstract` if it should not be instantiated (must be subclassed). + +## Identity + +Concepts may optionally declare an identifying field, using either the `identified by` (explicitly named identity field) or `identified` (`$identifier` system identity field) syntax. + +`Person` below is defined to use the `email` property as its identifying field. + +``` +concept Person identified by email { + o String email + o String firstName + o String lastName +} +``` + +While `Product` below will use `$identifier` as its identifying field. + +``` +concept Product identified { + o String name + o Double price +} +``` + +## Assets + +An asset is a class declaration that has a single `String` property which acts as an identifier. You can use the `modelManager.getAssetDeclarations` API to look up all assets. + +```js +asset Vehicle identified by vin { + o String vin +} +``` + +Assets are typically used in your models for the long-lived identifiable Things (or nouns) in the model: cars, orders, shipping containers, products, etc. + +## Participants + +Participants are class declarations that have a single `String` property acting as an identifier. You can use the `modelManager.getParticipantDeclarations` API to look up all participants. + +```js +participant Customer identified by email { + o String email +} +``` + +Participants are typically used for the identifiable people or organizations in the model: person, customer, company, business, auditor, etc. + +## Transactions + +Transactions are similar to participants in that they are also class declarations that have a single `String` property acting as an identifier. You can use the `modelManager.getTransactionDeclarations` API to look up all transactions. + +```js +transaction Order identified by orderId { + o String orderId +} +``` + +Transactions are typically used in models for the identifiable business events or messages that are submitted by Participants to change the state of Assets: cart check out, change of address, identity verification, place order, etc. + +## Events + +Events are similar to participants in that they are also class declarations that have a single `String` property acting as an identifier. You can use the `modelManager.getEventDeclarations` API to look up all events. + +```js +event LateDelivery identified by eventId { + o String eventId +} +``` + +Events are typically used in models for the identifiable business events or messages that are emitted by logic to signify that something of interest has occurred. diff --git a/docs/model-concerto.md b/docs/model-concerto.md new file mode 100644 index 00000000..1bddbe82 --- /dev/null +++ b/docs/model-concerto.md @@ -0,0 +1,74 @@ +--- +sidebar_position: 1 +--- + +# Introduction + +Concerto is a lightweight data modeling (schema) language and runtime for business concepts. + +Here is a simple model, expressed using the Concerto CTO syntax: + +```cs +namespace test@1.0.0 + +enum Country { + o UK + o USA + o FRANCE + o GERMANY + o JAPAN +} + +concept Address { + o String street + o String city + o String postCode + o Country country +} + +concept Person identified by name { + o String name + o Address address optional + @description("Height (cm)") + o Double height range=[0.0,] + o DateTime dateOfBirth +} +``` + +And here is the **generated** Plant UML diagram for the model: + +![UML diagram](/img/uml.svg) + +🏢 Concerto gives you “just enough” expressivity to capture real-world business models, while remaining easy to map to most runtime environments. + +⛳ An object-oriented language that is much easier to read and write than JSON/XML Schema, XMI or equivalents. + +📄 Serialize your instances to JSON + +🍪 Deserialize (and validate) instances from JSON + +🔎 Runtime introspection of the model using a powerful set of APIs + +🎛 Convert the model to [14+ common data model formats](./category/code-generation) + +🕸 Publish your reusable models to any website, including the Accord Project [model repository](https://models.accordproject.org) + +Infer models from other formats: + +- JSON document +- JSON Schema +- OpenAPI v3 specification + +## Accord Project + +Concerto is maintained by Accord Project: an open source, non-profit, initiative working to transform contract management and contract automation by digitizing contracts. Accord Project operates under the umbrella of the [Linux Foundation][linuxfound]. The technical charter for the Accord Project can be found [here][charter]. + +## License + +Accord Project source code files are made available under the [Apache License, Version 2.0][apache]. +Accord Project documentation files are made available under the [Creative Commons Attribution 4.0 International License][creativecommons] (CC-BY-4.0). + +[linuxfound]: https://www.linuxfoundation.org +[charter]: https://github.com/accordproject/governance/blob/main/accord-project-technical-charter.md +[apache]: https://github.com/accordproject/concerto/blob/master/LICENSE +[creativecommons]: http://creativecommons.org/licenses/by/4.0/ diff --git a/docs/model-decorators.md b/docs/model-decorators.md new file mode 100644 index 00000000..c8add6d1 --- /dev/null +++ b/docs/model-decorators.md @@ -0,0 +1,30 @@ +--- +id: model-decorators +title: Decorators +--- + +Model elements may have arbitrary decorators (aka annotations) placed on them. These are available via API and can be useful for tools to extend the model. Accord Project decorators are defined in the [Decorators Reference](ref-concerto-decorators.md). + +```js +@foo("arg1", 2) +asset Order identified by orderId { + o String orderId +} +``` + +Decorators have an arbitrary number of arguments. They support arguments of type: + +- String +- Boolean +- Number +- Type reference + +Resource definitions and properties may be decorated with 0 or more decorations. Note that only a single instance of a decorator is allowed on each element type. I.e. it is invalid to have the @bar decorator listed twice on the same element. + +Decorators are accessible at runtime via the `ModelManager` introspect APIs. This allows tools and utilities to use Concerto to describe a core model, while decorating it with sufficient metadata for their own purposes. + +The example below retrieves the 3rd argument to the foo decorator attached to the myField property of a class declaration: + +```js +const val = myField.getDecorator("foo").getArguments()[2]; +``` diff --git a/docs/model-enums.md b/docs/model-enums.md new file mode 100644 index 00000000..da12fad1 --- /dev/null +++ b/docs/model-enums.md @@ -0,0 +1,15 @@ +--- +id: model-enums +title: Enumerations +--- + +Enumerations are used to capture lists of domain values. + +```js +enum Cardsuit { + o CLUBS + o DIAMONDS + o HEARTS + o SPADES +} +``` diff --git a/docs/model-namespaces.md b/docs/model-namespaces.md new file mode 100644 index 00000000..df137d66 --- /dev/null +++ b/docs/model-namespaces.md @@ -0,0 +1,53 @@ +--- +id: model-namespaces +title: Namespaces +--- + +Each Concerto file starts with the name and version of a single namespace. A Concerto namespace declares a set of _declarations_. A declaration is one of: enumeration, concept, asset, participant, transaction, event. All declarations within a single file belong to the same namespace. + +```js +namespace org.acme@1.0.0 // declares version 1.0.0 of the org.acme namespace +``` + +### Imports + +In order for one namespace to reference types defined in another namespace, the types must be imported for a version of a namespace. + +## Simple + +```js +import org.accordproject.address@1.0.0.PostalAddress // imports PostalAddress from version 1.0.0 of the org.accordproject.address namespace +``` + +## Multiple Imports + +To import multiple types from the same namespace, use the `{}` syntax: + +```js +import org.accordproject.address@1.0.0.{PostalAddress,Country} // imports PostalAddress and Country from version 1.0.0 of the org.accordproject.address namespace +``` + +## Importing from model published to a public URL + +Import also can use the optional `from` declaration to import a model file that has been deployed to a URL. + +```js +import org.accordproject.address@1.0.0.PostalAddress from https://models.accordproject.org/address.cto +``` + +Imports using a `from` declaration can be downloaded into the model manager by calling `modelManager.updateExternalModels`. + +The Model Manager will resolve all imports to ensure that the set of declarations that have been loaded are globally consistent. + +## Strict:false mode + +For backwards compatability, and when running with `strict:false` imports may import types from unversioned namespaces, or may import all types in a namespace. + +> Please migrate models to use versioned namespaces and imports as this capability will be deprecated and removed in a future major release. + +Imports can be either qualified or can use wildcards. + +```js +import org.accordproject.address.PostalAddress // import a type from an unversioned namespace +import org.accordproject.address.* // import all types from an unversioned namespace +``` diff --git a/docs/model-properties.md b/docs/model-properties.md new file mode 100644 index 00000000..0c2aae56 --- /dev/null +++ b/docs/model-properties.md @@ -0,0 +1,64 @@ +--- +id: model-properties +title: Properties +--- + +Class declarations contain properties. Each property has a type which can either be a type defined in the same namespace, an imported type, or a primitive type. + +### Primitive types + +Concerto supports the following primitive types: + +| Type | Description | +| ---------- | ----------------------------------------------------------------------------- | +| `String` | a UTF8 encoded String. | +| `Double` | a double precision 64 bit numeric value. | +| `Integer` | a 32 bit signed whole number. | +| `Long` | a 64 bit signed whole number. | +| `DateTime` | an ISO-8601 compatible time instance, with optional time zone and UTZ offset. | +| `Boolean` | a Boolean value, either true or false. | + +### Meta Properties + +| Property | Description | +| ---------- | ----------------------------------------------------------------------- | +| `[]` | declares that the property is an array | +| `optional` | declares that the property is not required for the instance to be valid | +| `default` | declares a default value for the property, if no value is specified | +| `range` | declares a valid range for numeric properties | +| `regex` | declares a validation regex for string properties | + +`String` fields may include an optional regular expression, which is used to validate the contents of the field. Careful use of field validators allows Concerto to perform rich data validation, leading to fewer errors and less boilerplate application code. + +The example below validates that a `String` variable starts with `abc`: + +``` + o String myString regex=/abc.*/ +``` + +`Double`, `Long` or `Integer` fields may include an optional range expression, which is used to validate the contents of the field. Both the lower and upper bound are optional, however at least one must be specified. The upper bound must be greater than or equal to the lower bound. + +``` + o Integer intLowerUpper range=[-1,1] // greater than or equal to -1 and less than or equal to 1 + o Integer intLower range=[-1,] // greater than or equal to -1 + o Integer intUpper range=[,1] // less than or equal to 1 + + o Long longLowerUpper range=[-1,1] // greater than or equal to -1 and less than or equal to 1 + o Long longLower range=[-1,] // greater than or equal to -1 + o Long longUpper range=[,1] // less than or equal to 1 + + o Double doubleLowerUpper range=[-1.0,1.0] // greater than or equal to -1 and less than or equal to 1 + o Double doubleLower range=[-1.0,] // greater than or equal to -1 + o Double doubleUpper range=[,1.0] // less than or equal to 1 +``` + +#### Example + +``` +asset Vehicle { + o String model default="F150" + o String make default="FORD" + o Integer year default=2016 range=[1990,] optional // model year must be 1990 or higher + o String V5cID regex=/^[A-z][A-z][0-9]{7}/ +} +``` diff --git a/docs/model-relationships.md b/docs/model-relationships.md new file mode 100644 index 00000000..b9aeb735 --- /dev/null +++ b/docs/model-relationships.md @@ -0,0 +1,36 @@ +--- +id: model-relationships +title: Relationships +--- + +A relationship in Concerto Modeling Language (CML) is a tuple composed of: + +1. The namespace of the type being referenced +2. The type name of the type being referenced +3. The identifier of the instance being referenced + +Hence a relationship could be: `org.example.Vehicle#123456` + +This would be a relationship to the `Vehicle` _type_ declared in the `org.example` _namespace_ with the _identifier_ `123456`. + +> A relationship can be defined to any _identifiable_ type, that is a type that has been declared with either the `identified by` or `identified` properties. + +Relationships are unidirectional and deletes do not cascade, ie. removing the relationship has no impact on the thing that is being pointed to. Removing the thing being pointed to does not invalidate the relationship. + +Relationships must be resolved to retrieve an instance of the object being referenced. The act of resolution may result in null, if the object no longer exists or the information in the relationship is invalid. Resolution of relationships is outside of the scope of Concerto. + +A property of a class may be declared as a relationship using the `-->` syntax instead of the `o` syntax. The `o` syntax declares that the class contains (has-a) property of that type, whereas the `-->` syntax declares a typed pointer to an external identifiable instance. + +In this example, the model declares that an `Order` has-an array of reference to `OrderLines`. Deleting the `Order` has no impact on the `OrderLine`. When the `Order` is serialized the JSON only the IDs of the `OrderLines` are stored within the `Order`, not the `OrderLines` themselves. + +```js +asset OrderLine identified by orderLineId { + o String orderLineId + o String sku +} + +asset Order identified by orderId { + o String orderId + --> OrderLine[] orderlines +} +``` diff --git a/docs/model-vocabulary.md b/docs/model-vocabulary.md new file mode 100644 index 00000000..a21b8703 --- /dev/null +++ b/docs/model-vocabulary.md @@ -0,0 +1,159 @@ +--- +id: model-vocabulary +title: Vocabulary +--- + +The Vocabulary module for Concerto optionally allows human-readable labels (Terms) to be associated with model elements. Terms are stored within a locale specific vocabulary YAML file associated with a Concerto namespace. + +For example, a Concerto model that defines an enumeration with the values `RED`, `GREEN`, `BLUE` can be associated with an English vocabulary with the terms "Red", "Green", "Blue" and a French Vocabulary with terms "Rouge", "Vert", "Bleue". + +The `VocabularyManager` class manages access to a set of Vocabulary files, and includes logic to retrieve the most appropriate term for a requested locale. + +### Example Model + +``` +namespace org.acme + +enum Color { + o RED + o BLUE + o GREEN +} + +asset Vehicle identified by vin { + o String vin + o Color color +} + +asset Truck extends Vehicle { + o Double weight +} +``` + +### Example Vocabulary Files + +#### English - en + +```yaml +locale: en +namespace: org.acme +declarations: + - Color: A color + - Vehicle: A road vehicle + properties: + - vin: Vehicle Identification Number + - model: Model of the vehicle + - Truck: A vehicle capable of carrying cargo + properties: + - weight: The weight of the truck in KG +``` + +#### British English - en-gb + +```yaml +locale: en-gb +namespace: org.acme +declarations: + - Truck: A lorry (a vehicle capable of carrying cargo) + - Color: A colour + - Milkfloat +``` + +#### French - fr + +```yaml +locale: fr +namespace: org.acme +declarations: + - Vehicle: Véhicule + properties: + - vin: Le numéro d'identification du véhicule (NIV) +``` + +#### Simplified Chinese zh-cn + +```yaml +locale: zh-cn +namespace: org.acme +declarations: + - Color: 颜色 + properties: + - RED: 红色 + - GREEN: 绿色 + - BLUE: 蓝色 + - Vehicle: 车辆 + properties: + - vin: 车辆识别代号 + - color: 颜色 +``` + +As you can see in the vocabularies above, a vocabulary can supplement or override terms from a base vocabulary, as is the case of the `en-gb` vocabulary which redefines and adds terms specific to British English over the generic English `en` vocabulary. + +## API Usage + +Use the `VocabularyManager` classs to define new vocabularies, retrieve terms for a locale, or to validate +a vocabulary using a `ModelManager`. + +### Adding a Vocabulary + +Load the YAML file for the Vocabulary and add it to a `VocabularyManager`: + +``` +vocabularyManager = new VocabularyManager(); +const enVocString = fs.readFileSync('./test/org.acme_en.voc', 'utf-8'); +vocabularyManager.addVocabulary(enVocString); +``` + +### Retrieving a Term + +Use the `getTerm` method on the `VocabularyManager` to retrieve a term for +a declaration or property within a namespace: + +``` +const term = vocabularyManager.getTerm('org.acme', 'en-gb', 'Color'); +// term.should.equal('A colour'); +``` + +``` +const term = vocabularyManager.getTerm('org.acme', 'en-gb', 'Vehicle', 'vin'); +// term.should.equal('Vehicle Identification Number'); +``` + +### Resolve a Term using ModelManager Type Hierarchy + +The `resolveTerm` method on the `VocabularyManager` may be used to lookup a term +based on the type hierarchy defined by a `ModelManager`. In the example below, the property +`vin` is not defined on the `Truck` declaration but rather on the `Vehicle` super-type. + +``` +modelManager = new ModelManager(); +const model = fs.readFileSync('./test/org.acme.cto', 'utf-8'); +modelManager.addModelFile(model); +const term = vocabularyManager.resolveTerm(modelManager, 'org.acme', 'en-gb', 'Truck', 'vin'); +// term.should.equal('Vehicle Identification Number'); +``` + +### Validating a Vocabulary Manager + +Use the `validate` method on the `VocabularyManager` to detect missing and redudant vocabulary +terms — comparing the terms in the `VocabularyManager` with the declarations in a `ModelManager`. +The return value from `validate` is an object containing information for the missing and additional terms. + +> Note that allowing vocabularies to evolve independently of their associated namespace provides definition and translation workflow flexibility. + +``` +const result = vocabularyManager.validate(modelManager); +// result.missingVocabularies.length.should.equal(1); +// result.missingVocabularies[0].should.equal('org.accordproject'); +// result.additionalVocabularies.length.should.equal(1); +// result.additionalVocabularies[0].getNamespace().should.equal('com.example'); +// result.vocabularies['org.acme/en'].additionalTerms.should.have.members(['Vehicle.model']); +// result.vocabularies['org.acme/en'].missingTerms.should.have.members(['Color.RED', 'Color.BLUE', 'Color.GREEN', 'Vehicle.color']); +// result.vocabularies['org.acme/en-gb'].additionalTerms.should.have.members(['Milkfloat']); +// result.vocabularies['org.acme/fr'].missingTerms.should.have.members(['Color', 'Vehicle.color', 'Truck']); +// result.vocabularies['org.acme/fr'].additionalTerms.should.have.members([]); +// result.vocabularies['org.acme/zh-cn'].missingTerms.should.have.members(['Truck']); +// result.vocabularies['org.acme/zh-cn'].additionalTerms.should.have.members([]); +``` + +Please refer to the [reference API](ref-concerto-api) for the `concerto-vocabulary` module for detailed API guidance.