Docs / Write contracts

Several files

How the files and folders of one publish become one contract.

bidirekt publish takes any number of files and folders, and all the files of one publish are one contract. A folder stands for every .yaml and .yml file under it, subfolders included. The order does not matter, and the shell expands globs such as contracts/*.yaml. Reports name a file you passed by the path you typed, and a file found in a folder by the folder and its path inside it, such as contracts/rest/pets.yaml.

  • One schema namespace. A schema declared in one file can be named from any other. The same name declared twice is a duplicate. A file that names schemas declared elsewhere is valid only when published together with that file.
  • A provider declares each resource once. Files may split the endpoints, the methods or even the statuses of one method between them, but the same resource in two files is a duplicate. A file reached twice, such as a file passed on its own and again through its folder, is read once and is not a duplicate. A request has no status, so the request of an endpoint and method belongs in exactly one file, even when its responses are spread over several.
  • A consumer merges by union. Each module of a consumer may declare the resources it reads in its own file. Fragments of the same resource merge: in a response, a property is optional only if every fragment allows it, and nullable only if every fragment accepts null; in a request, it is required only if every fragment sends it, and nullable if any fragment may send null. A property that one fragment leaves out together with its parent is not made optional: only the parent is. Two fragments that give one property different types are rejected.

A provider that keeps the 201 of POST /pets in one file and its 400 in another writes request in only one of them:

yaml
# pets.yaml
provides:
  rest:
    /pets:
      post:
        request: NewPet
        responses:
          201: Pet
yaml
# pets_errors.yaml
provides:
  rest:
    /pets:
      post:
        responses:
          400: Error

Writing request: NewPet in pets_errors.yaml too declares the request of POST /pets twice:

publish error
contract validation failed
  - pets_errors.yaml: duplicate resource "provides POST /pets request", also declared in pets.yaml

Two modules of the consumer petstore_web read the same GET /pets/*, each its own subset:

yaml
# web_list.yaml
consumes:
  petstore_api:
    rest:
      /pets/*:
        get:
          responses:
            200: PetRow

schemas:
  PetRow:
    type: object
    properties:
      petId:
        type: integer
      name:
        type: string
yaml
# web_card.yaml
consumes:
  petstore_api:
    rest:
      /pets/*:
        get:
          responses:
            200: PetCard

schemas:
  PetCard:
    type: object
    properties:
      petId:
        type: integer
      photoUrl:
        type: string
        optional: true

Published together, petstore_web reads petId and name as required and photoUrl as optional, so a provider that returns only petId and name is compatible. Remove optional: true from web_card.yaml and photoUrl becomes required, because one reader needs it.