This is the step by step cookbook for creating the OAS.
Please read the following conceptual documents before working on the OAS:
- Concept of Microservice
- Introduction to HTTP Requests
- API-First Approach
- OpenAPI Specification
- OpenAPI Guide from Basic Structure to Using $ref
Furthermore, it is recommended to study an existing OAS (e.g. RegistryOffice) in deep detail while reading Structure of the OAS in parallel.
Assure that there is a copy of the latest template of the OAS in the local develop branch of your application's repository.
Rename the file, by replacing "ApplicationPattern" by your application's name.
Use CTRL+h for replacing '*-1-0-0' by the abbreviation of your application's name and release number e.g. 'ro-2-0-1'.
Update the title: and version: values by the content of the HttpServer in your ServiceList.
Save the OAS file.
Commit it.
Push it to the remote develop branch of your application's repository.
If not yet existing, create an Issue for elaborating the OAS.
Note all your ideas, plans and questions that are not yet documented in ServiceList, ForwardingList etc. into the Issue.
Create a local feature branch for elaborating the OAS from that Issue.
Please, be careful while altering the existing code blocks in the Basic Service, Basic OaM and Common Components sections of the OAS.
Most of them will require just a little adjustment at their IDs.
Some code blocks might be obsolete (e.g. ElasticSearchClient) in your specific application and need to be deleted.
Several individual code blocks need to be added.
It is recommended to copy similar, already existing code blocks and to customize them.
(Existing code blocks could be taken from the ApplicationPattern, alternatively and probably more appropriate, from existing applications like the RegistryOffice or other microservices of the TinyApplicationController. Please, don't forget to CTRL+h the UUIDs after pasting.)
Add the OperationNames from the individual OperationServers section of your ServiceList beneath the /v1/bequeath-your-data-and-die: into the Individual Service Section of the OAS. The ordering should be kept in synch.
Copy the entire parameters: block either from the /v1/bequeath-your-data-and-die: or any other service in the ApplicationPattern and paste it beneath each of the new paths statements.
All services are exclusively supporting the post method.
Pick the next path and make some extra notes about:
- Are attributes required to be send as an input to the OperationServer?
- If yes, note the attributes that are required.
- Are attributes expected to be responded as an output by the OperationServer?
- If yes, note the attributes that will be responded.
Check existing specifications for a path definition that is similar.
Obviously, it is unlikely that there is a path that has the exact same input and output attributes, but you should at least chose a path that comprises requestBody: and responses: blocks, if you need those in the new path.
Make a copy of the chosen post: block beneath the parameters: block.
Delete from the the copied block whatever you no longer need.operationId:, summary:, tags: and security: statements to be updated in accordance with the respective chapters in Structure of the OAS.
Transfer the attributes' names from your notes into the required: block.
Remark: OpenAPI Specification allows defining the structure of objects in the Common Components section at the end of the OAS and to just put references to these definitions into the schema: blocks of the individual RequestBodies or ResponseBodies.
In principle, the ApplicationPattern processes are supporting this, but appliance should be limited to comprehensive objects that get referenced for at least two or three times.
Anyway, it is recommended to formulate the schemas of all RequestBodies and ResponseBodies first and to search for redundancies afterwards.
Reading the OAS is much easier when following this order during specification work.So please, take your time and chose as descriptive as possible attributes' names.
Delete the already existing, but obsolete attributes' names from the required: block.Add a couple of empty lines beneath the properties: statement.
Pick the next attribute from the required: block and decide about its datatype.
Search the already existing attributes for one that is as similar as possible.
Copy that one and paste it beneath the properties: statement.Adapt the attribute's name.
Adapt the datatype in the type: statement, if required.Add pattern:, enum:, minimum:, maximum: etc. statements for detailing the format of the expected data on the ingress, if appropriate.
Help for formulating proper regular expressions could be found here or here.
A validator for testing regular expressions could be found here.
It is recommended not to attempt optimizing the length of the regular expression to a minimum number of symbols, but to optimize for easy-to-read explanations of the format.Describe the meaning of the attribute in connection with the operation in the first line of the description: > statement.
Example: 'IP address of the device that shall be messed up
(Please regard that there is a single quote at the beginning of the line, but none at its end.)If the sent attribute shall serve for altering internal data of the application, the second line of the description: > statement is needed for describing a reference.
One of the terms find in, find or create, update and update or create has to be chosen in accordance with the definitions in Structure of the OAS.
The path towards the resource, which is either to be identified or to be altered, has to be added.
Maybe, the same resource has already been referenced in an existing definition (CTRL+f for the name of the attribute from the internal data tree) and the path can be copied from there (please, regard that the same attribute name might occur several times inside the internal data tree).
If you would need to formulate the entire path manually, Structure of OaM paths might help.
After describing the path, some included UUID or local-ID might need to be adapted.
Looking into the CONFIGfile might support finding the right identifier.
Often, the input is adding another instance to a list, or identifying an instance from a list.
The UUID cannot address a concrete instance in these cases.
The variant segments of the UUID need to be replaced by "*" for describing the list of objects that are affected by the operation.
(Please take care that the second line ends with a single quote.)Repeat these steps until you described all the attributes from the required: block in the properties: block.
Delete the already existing, but obsolete attribute descriptions from the properties: block.Change into the example: block and delete the already existing, but obsolete attribute names.
Copy the attribute names from the required: block into the example: block.
Remove the hyphens in front of the attributes' names.
Add colons behind the attributes' names.
If the schema definition would be sub-structured into further objects, the example would need to be sub-structured by the additionally required attributes, too.
Add reasonable values that are complying the restrictions made on the format.Be aware:
If you would add an example, which does not comply with the structure of the objects or the formats of the attributes, the mock server that gets created from the OaS will not be able to provide a proper ResponseBody and validation of the test cases will not be possible.If there are no attributes to be returned as an output:
- Double check that there is a 204: block in your responses:.
- Update the description:.
- Update the UUID in the description: > of the life-cycle-state: parameter.
If there are attributes to be returned:
- Double check that there is a 200: block in your responses:.
- Update the description: by a explanation of what just happened as the request has been successfully executed.
Add a couple of empty lines beneath the properties: statement.
Transfer the attributes' names from your notes into the properties: block (required: statement not required on the output).
Please, regard that you and potential users of your application have to understand the purpose of the OperationServer from reading its path (operation name) and the attributes' names in the API description.
So please, take your time and chose as descriptive as possible attributes' names.Pick the next attribute from the properties: block and decide about its datatype.
Search the already existing attributes for one that is as similar as possible.
Copy its definition (datatype, description etc.) and paste it beneath the attribute's name.Adapt the datatype in the type: statement, if required.
Detailing the format of the provided data is not required (enumerations might be an exception as they ease consequent processing of the data).
Describe the meaning of the attribute in relation with the operation in the first line of the description: > statement.
Example: 'IP address of the device that has just been successfully messed up
(Please regard that there is a single quote at the beginning of the line, but none at its end.)If the provided attribute is retrieved from internal data of the application, the second line of the description: > statement is needed for referencing the exact source.
The term from has to be chosen.
The path towards the resource that is holding the data that is to be provide has to be added.
Maybe, the same resource has already been referenced in an existing definition (CTRL+f for the name of the attribute in the internal data tree) and the path can be copied from there.
If you would need to formulate it manually, Structure of OaM paths might help.
After describing the path, some included UUID or local-ID might need to be adapted.
Looking into the CONFIGfile might support finding the right identifier.
Often, the output is providing data from an object instance in a list.
As the object instance is identified by the key attribute's value, which is handed over in the RequestBody, the UUID in the specification cannot identify a concrete instance.
Consequently, the variable segments of the UUID need to be replaced by "*" for describing a list of objects that come into question.
(Please take care that the second line ends with a single quote.)Repeat these steps until you described all the attributes the properties: block.
Delete the remaining, but obsolete attribute descriptions from the properties: block.Change into the example: block and delete the already existing, but obsolete attribute names.
Copy the attribute names from the properties: block into the example: block.
Add colons behind the attributes' names.
If the schema definition would be sub-structured into further objects, the example would need to be substructured, too.
Add reasonable values that are complying the restrictions that are made on the ingress of the data.Update the UUID in the description: > of the life-cycle-state: parameter in the headers: block.
Repeat adding post: blocks until all the paths in your OAS are covered.
It is recommended to first describe all RequestBodies and all ResponseBodies before adding the first callback to the OAS.
Finding the right location for adding a callback is the first step in its description.
The simplest procedure is to open the ForwardingList and to assign all forwardings one after another to one of the existing paths.
The callback definition, which is implementing a forwarding from the ForwardingList, is usually allocated inside the definition of the path, which is stated to be the management request for updating the OperationClient.
If no OperationServer for updating the OperationClient would be stated in the description of the forwarding, the OperationServer for updating the FcPorts, or for deleting the FcPorts, or for deleting the OperationClients would have to be chosen (in this ordering) instead.
If there would be no management requests stated in the description of a forwarding in the ForwardingList, the path of the OperationServer that is initiating sending the callback shall be chosen for allocating the callback definition.
As soon as the correct path could be identified, a callbacks: statement is to be added after the responses: block.
The forwarding-name from within the ForwardingList has to be copied beneath the callbacks: statement.
If several forwarding-names have to be added to the same path, the callbacks: statement shall not be repeated.
After all forwardings from the ForwardingList have been transferred into the OAS, the individual callbacks have to be defined in detail.
The URL statement is a bit hard to describe, but it supports the ApplicationImplementer to create a correct request from application internal data.
Maybe it is a good proceeding to
- copy a URL statement from an existing callback definition
- temporarily add line breaks after every "]",
- adapt the UUIDs of protocol, IP address, domain name, port, and operation name references and
- remove the line breaks again.
It is recommended to wait until the original path definitions are really stable, before copying the entire method block (most likely post:) from the original path definition into the callback definition.
The entire block is to be marked for correcting the indents.
operationId:, tags: and security: statements are to be removed.
All statements that are filtering the to be sent requests for specific values of the attributes (e.g. patterns or enumerations at String attributes, minimum or maximum values at Integer attributes) are to be removed, too.
In the RequestBody, change all descriptions to "from " and replace the existing reference by the reference to the location where to get the value to be sent.
In the ResponseBody, change all descriptions to find in/update or find or create/update or create and replace the existing references by references to the locations where to store the answered values.
In the headers:, the description at the life-cycle-state: shall be replaced by the simple formulation 'Life cycle state of the consumed service'.
Repeat describing the callbacks in detail until all forwardings from the ForwardingList have been covered.
No changes required in the Basic Service Section.
If you would have defined additional Profiles in the ProfileList, you would now have to add a definition of these Profiles into the /core-model-1-4:control-construct: and the /core-model-1-4:control-construct/profile-collection/profile={uuid}:.
Click into the path statement of the /core-model-1-4:control-construct: and CTRL+f for "oneOf:".
Collapse all entries inside the oneOf: block.
Mark the StringProfile definition and CTRL+c.
CTRL+v the same at the end of the list of profile definitions.
Update the description: statement of the copied block.
Carefully CTRL+h "string-profile" by the name of the Profile, which you would like to add, inside the copied block.
Change into the enum: statement and create a copy of the last entry.
The second last entry has to be changed from your Profile's name back to "string-profile-1-0".
In the last entry, "STRING_PROFILE" has to be replaced by your Profile's name.In the next step, the attributes of the Profile have to be described.
The /core-model-1-4:control-construct: and the /core-model-1-4:control-construct/profile-collection/profile={uuid}: provide GET method only.
It is not needed to filter data on the egress.
In contrary to current definitions a required: statement shall not be added in the capability: and configuration: blocks.Add a couple of empty lines beneath the properties: statement inside the capability: block.
Transfer the names and datatypes of the read-only capability attributes from your ProfileList.
Delete the obsolete attribute definitions.Jump into the enum: statement of the profile-name: attribute of your new Profile definition.
Mark the additional entry that is identifying your new Profile and CTRL+c.
CTRL+v the same into the enum: statements of the profile-name: attributes of all existing Profile definitions.
Repeat these steps until all the additional Profiles you defined in the ProfileList have been transferred into the OAS.
After defining all the additional Profiles in /core-model-1-4:control-construct:, mark the new blocks and CTRL+c.
Click into the /core-model-1-4:control-construct/profile-collection/profile={uuid}: and CTRL+f for "oneOf:".
Collapse all entries inside the oneOf: block.
CTRL+v the new Profile definition at the end of the list of existing Profile definitions.
Mark the new blocks and adapt the indents.
Jump into the enum: statement of the profile-name: attribute of one of your new Profile definitions.
Mark the additional entries that are identifying your new Profiles and CTRL+c.
CTRL+v the same into the enum: statements of the profile-name: attributes of the existing Profile definitions.
Apart from updating the paths for reading the entire ControlConstruct and the list of Profile instances, new paths for reading and writing the individual attributes of new Profiles have to be created.
If Profile contains at least a single Capability (read-only) attribute:
- CTRL+c the entire block of an existing OaM path of a Capability attribute.
- CTRL+v this block beneath the /core-model-1-4:control-construct/profile-collection/profile={uuid}: in the Individual OaM Section.
- Just change the attribute's name at the end of the path statement.
If there is also a Configuration (read/write) attribute in the new Profile:
- CTRL+c the entire block of an existing OaM path of a Configuration attribute.
- CTRL+v this block beneath the Capability block from above.
Click into the path statement of the Capability block and carefully CTRL+h the ProfileName and ReleaseNumber of the existing Profile by the ProfileName and "-1-0" of your new Profile until you reached the end of the new Configuration block.
To be done in both blocks:
- Update the pattern: and the example: statements of the parameters: blocks by the new ProfileName.
- Update the tags: statements in all method blocks by the new ProfileName.
If required, replicate the half-way adapted Capability block and adapt the attribute's name at the end of the path statement until all required Capability attributes of the new Profile are covered.
If required, replicate the half-way adapted Configuration block and adapt the attribute's name at the end of the path statement until all required Configuration attributes of the new Profile are covered.
To be done in all method blocks concerning the new Profile:
- Update the operationId: statements.
- Describe the purpose of the request in the summary statements.
- Describe the effect of a successful request in the description: statements of the responses with HTTP response code 200 or 204.
- (If required, delete the required block from old fashioned responses with HTTP response code 200 or 204.)
- Update the attribute's name in the properties: statements.
- Adapt the datatypes, if required.
- (If required, rearrange the example: statement so that it is one level below the schema: statement. Please note that the attributes need to be stated including their names and values at this position).
- Update the example: statement with reasonable values.
To be done in the PUT method blocks only:
- Add detailed definitions of the format of the ingress data (e.g. enum:, pattern:, minimum:, maximum: and so on).
- Add detailed definitions of the structure of the ingress data (e.g. required:, oneOf: and so on).
Delete obsolete paths from the OAS.
This could apply e.g. on Profiles or the ElasticSearchClient.
The UUIDs need to be adapted to the abbreviation and release number of the new application.
No changes required in the Common Components Section. The UUIDs need to be adapted to the abbreviation and release number of the new application.
Check, whether all individual OperationServers have been transferred from the ServiceList into the OAS.
Check, whether all Profiles from ProfileList
- are included in the /core-model-1-4:control-construct:
- are included in the /core-model-1-4:control-construct/profile-collection/profile={uuid}:
- have been covered by dedicated paths for their individual attributes (assure the Configuration attributes being covered by PUT methods) Check, whether all the forwardings from the ForwardingList have been transferred into callbacks.
- Commit to your local feature branch.
- Push your local feature branch to the remote repository.
- Create a Pull-Request.
- Please, regard the test results of the YAML linting in the Pull-Request. Correct the syntax of the OAS, if errors are indicated (warnings need not to be regarded), and commit and push again until the OAS in the remote repository is successfully validated.
- Select one or two Reviewers from the team of ApplicationOwners.
- Assign the Pull-Request to yourself.