github.com/snyk/vervet/v6@v6.2.4/README.md (about)

     1  # vervet
     2  
     3  Vervet is an HTTP API version lifecycle management tool, allowing APIs to be designed, developed, versioned and released from [resources](https://github.com/snyk/sweater-comb/blob/main/docs/principles/api_program.md#resources) independently and concurrently.
     4  
     5  In a large organization, there might be many teams involved in delivering a large API -- such as at [Snyk](https://snyk.io) where Vervet was developed.
     6  
     7  Within a single small team, there is still often a need to simultaneously try new things in parts of an API while maintaining stability.
     8  
     9  While Vervet was developed in the context of a RESTful API, Vervet can be used with any HTTP API expressed in OpenAPI 3 -- even if it does not adhere to strict REST principles.
    10  
    11  ### [API Versioning](https://github.com/snyk/sweater-comb/blob/main/docs/principles/version.md)
    12  
    13  To summarize the API versioning supported by Vervet:
    14  
    15  #### What is versioned?
    16  
    17  Resource versions are defined in OpenAPI 3, as if each resource were a standalone service.
    18  
    19  #### How are resource version specs organized?
    20  
    21  Resources are organized in a standard directory structure by release date, using OpenAPI extensions to define lifecycle concepts like stability.
    22  
    23  #### How does versioning work?
    24  
    25  - Resources are versioned independently by date and stability, with a well-defined deprecation and sunsetting policy.
    26  - Additive, non-breaking changes can be made to released versions. Breaking changes trigger a new version.
    27  - New versions deprecate and sunset prior versions, on a timeline determined by the stability level.
    28  
    29  [Read more about API versioning](https://github.com/snyk/sweater-comb/blob/main/docs/principles/version.md).
    30  
    31  ## Features
    32  
    33  A brief tour of Vervet's features.
    34  
    35  ### Building a service OpenAPI from resources
    36  
    37  Vervet collects the OpenAPI specification of each resource version and merges them into a series of OpenAPI specifications that describe the entire application, at each distinct release version in its underlying parts.
    38  
    39  Given a directory structure of resource versions, each defined by an OpenAPI specification as if it were an independent service:
    40  
    41      tree resources
    42  
    43  ```
    44  resources
    45  ├── _examples
    46  │   └── hello-world
    47  │       ├── 2021-06-01
    48  │       │   └── spec.yaml
    49  │       ├── 2021-06-07
    50  │       │   └── spec.yaml
    51  │       └── 2021-06-13
    52  │           └── spec.yaml
    53  └── projects
    54      └── 2021-06-04
    55          └── spec.yaml
    56  ```
    57  
    58  and a Vervet project configuration that instructs how to put them together:
    59  
    60      cat .vervet.yaml
    61  
    62  ```yaml
    63  apis:
    64    my-api:
    65      resources:
    66        - path: "resources"
    67      output:
    68        path: "versions"
    69  ```
    70  
    71  `vervet build` aggregates these resources' individual OpenAPI specifications to describe the entire service API _at each distinct version date and stability level_ from its component parts.
    72  
    73      tree versions
    74  
    75  ```
    76  versions/
    77  ├── 2021-06-01
    78  │   ├── spec.json
    79  │   └── spec.yaml
    80  ├── 2021-06-01~beta
    81  │   ├── spec.json
    82  │   └── spec.yaml
    83  ├── 2021-06-01~experimental
    84  │   ├── spec.json
    85  │   └── spec.yaml
    86  ├── 2021-06-04
    87  │   ├── spec.json
    88  │   └── spec.yaml
    89  ├── 2021-06-04~beta
    90  │   ├── spec.json
    91  │   └── spec.yaml
    92  ├── 2021-06-04~experimental
    93  │   ├── spec.json
    94  │   └── spec.yaml
    95  ├── 2021-06-07
    96  │   ├── spec.json
    97  │   └── spec.yaml
    98  ├── 2021-06-07~beta
    99  │   ├── spec.json
   100  │   └── spec.yaml
   101  ├── 2021-06-07~experimental
   102  │   ├── spec.json
   103  │   └── spec.yaml
   104  ├── 2021-06-13
   105  │   ├── spec.json
   106  │   └── spec.yaml
   107  ├── 2021-06-13~beta
   108  │   ├── spec.json
   109  │   └── spec.yaml
   110  └── 2021-06-13~experimental
   111      ├── spec.json
   112      └── spec.yaml
   113  ```
   114  
   115  ### Code generation
   116  
   117  Since Vervet models the composition, construction and versioning of an API, it is well positioned to coordinate code and artifact generation through the use of templates.
   118  
   119  Generators may be defined in a YAML file, such as `generators.yaml`:
   120  
   121  ```yaml
   122  generators:
   123    version-readme:
   124      scope: version
   125      filename: "{{ .Path }}/README"
   126      template: "{{ .Here }}/templates/README.tmpl" # Located relative to the location of generators.yaml
   127  ```
   128  
   129  The context of `README.tmpl` has full access to the resource version metadata and OpenAPI document object model.
   130  
   131  ```yaml
   132  Generated by vervet. DO NOT EDIT!
   133  
   134  # My API
   135  
   136  Files in this directory were generated by `@snyk/vervet`
   137  for resource `{{ .ResourceVersion.Name }}` at version `{{ .ResourceVersion.Version.String }}`.
   138  ```
   139  
   140  In a project with a `.vervet.yaml` configuration, execute the generators with
   141  
   142      vervet generate -g generators.yaml
   143  
   144  The simple generator above produces a README in each resource version directory.
   145  
   146      tree resources
   147  
   148  ```
   149  resources
   150  └── thing
   151      └── 2021-10-21
   152          ├── README
   153          └── spec.yaml
   154  ```
   155  
   156  Generators are defined using [Go templates](https://pkg.go.dev/text/template).
   157  
   158  Template syntax may also be used to express a directory structure of many files. A more advanced example, an Express controller generated from each operation in a resource version OpenAPI spec:
   159  
   160  ```yaml
   161  generators:
   162    version-controller:
   163      scope: version
   164      # `files:` generates a collection of files -- which itself is expressed as a
   165      # YAML template.  Keys in this YAML are the paths of the files to generate,
   166      # whose values are the file contents.
   167      files: |-
   168        {{- $path := .Path -}}
   169        {{- $resource := .ResourceVersion -}}
   170        {{- $version := .ResourceVersion.Version -}}
   171        {{- range $path, $pathItem := .ResourceVersion.Document.Paths -}}
   172        {{- range $method, $operation := $pathItem -}}
   173        {{- $operationId := $operation.operationId -}}
   174        {{/* Construct a context object using the 'map' function */}}
   175        {{- $ctx := map "Context" . "OperationId" $operationId }}
   176        {{ $path }}/{{ $operationId }}.ts: |-
   177          {{/*
   178               Evaluate the template by including it with the necessary context.
   179               The generator's template (controller.ts.tmpl) is included as
   180               "contents" from within the `files:` template.
   181             */}}
   182          {{ include "contents" $ctx | indent 2 }}
   183        {{ end }}
   184        {{- end -}}
   185      template: "{{ .Here }}/templates/controller.ts.tmpl"
   186  ```
   187  
   188  In this case, a template is being applied per `operationId` in the `spec.yaml` generated in the prior step. `version-controller` produces a collection of files, a controller module per resource, per version, per operation.
   189  
   190  Finally, a note on scoping. Generators can be scoped to either a `version` or a `resource`.
   191  
   192  `scope: version` generator templates execute with [VersionScope](https://pkg.go.dev/github.com/snyk/vervet/v6/internal/generator#VersionScope). This maps 1:1 with a single resource version OpenAPI specification.
   193  
   194  `scope: resource` generator templates execute with [ResourceScope](https://pkg.go.dev/github.com/snyk/vervet/v6/internal/generator#ResourceScope). This is a collection of resource versions, useful for building resource routers.
   195  
   196  ## Installation
   197  
   198  ### NPM
   199  
   200  Within a project:
   201  
   202      npm install @snyk/vervet
   203  
   204  Or installed globally:
   205  
   206      npm install -g @snyk/vervet
   207  
   208  NPM packaging adapted from https://github.com/manifoldco/torus-cli.
   209  
   210  ### Go
   211  
   212  Go >= 1.16 required.
   213  
   214      go install github.com/snyk/vervet/v6/cmd/vervet@latest
   215  
   216  Building from source locally:
   217  
   218      go build ./cmd/vervet
   219  
   220  or
   221  
   222      make build
   223  
   224  ## Development
   225  
   226  Vervet uses a reference set of OpenAPI documents in `testdata/resources` in
   227  tests. CLI tests compare runtime compiled output with pre-compiled, expected
   228  output in `testdata/output` to detect regressions.
   229  
   230  When introducing changes that intentionally change the content of compiled
   231  output:
   232  
   233  - Run `go generate ./testdata` to update the contents of `testdata/output`
   234  - Verify that the compiled output is correct
   235  - Commit the changes to `testdata/output` in your proposed branch
   236  
   237  ## Releasing a new version
   238  
   239  A new version of `vervet` will automatically be generated for Github and `npm` when new features
   240  are introduced, i.e. when commits are merged that are marked with `feat:`.
   241  
   242  ## Deprecating a version
   243  After removing the endpoint version code and specs, you may see this issue:
   244  ```
   245  ENOENT: no such file or directory, open '.../spec.yaml'
   246  ```
   247  To solve this:
   248  1. Temporarily ignore the endpoint version code in `.vervet.yaml`
   249  2. Remove the endpoint versions from `catalog-info.yaml`
   250  3. Remove the old OpenAPI specs.
   251  
   252  [Example PR](https://github.com/snyk/registry/pull/33489/files)