<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yuliya Bagriy</title>
    <description>The latest articles on DEV Community by Yuliya Bagriy (@aviskase).</description>
    <link>https://dev.to/aviskase</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F298803%2Fc1409b08-0be0-4ccd-8481-d85ca349c6fe.png</url>
      <title>DEV Community: Yuliya Bagriy</title>
      <link>https://dev.to/aviskase</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/aviskase"/>
    <language>en</language>
    <item>
      <title>Using openapi-cli: custom rules</title>
      <dc:creator>Yuliya Bagriy</dc:creator>
      <pubDate>Mon, 06 Sep 2021 20:35:43 +0000</pubDate>
      <link>https://dev.to/aviskase/using-openapi-cli-custom-rules-49le</link>
      <guid>https://dev.to/aviskase/using-openapi-cli-custom-rules-49le</guid>
      <description>&lt;p&gt;If you’re planning to &lt;a href="https://www.aviskase.com/articles/2020/12/07/crash-course-into-api-related-terminology/#description-doc-validation-vs-linting-vs-preprocessing"&gt;lint OpenAPI description documents&lt;/a&gt; (you should!), always check whether a linter supports adding custom rules. And I mean not just &lt;a href="https://www.aviskase.com/articles/2019/12/26/lunchlearn-linting-openapi-description-docs/"&gt;changing severity or disabling predefined rules&lt;/a&gt;, but actually adding new ones specific to your API standards.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;openapi-cli&lt;/code&gt;, as any respectable OpenAPI linter, &lt;a href="https://redoc.ly/docs/cli/custom-rules/"&gt;allows that&lt;/a&gt;. The process is very similar to &lt;a href="https://www.aviskase.com/articles/2021/08/16/using-openapi-cli-custom-preprocessing/"&gt;adding preprocessors&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: enforcing case style
&lt;/h2&gt;

&lt;p&gt;Let’s look at our previous enumeration example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
    &lt;span class="na"&gt;nullable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;x-aviskase-enum&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;OK&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DESTROYED&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;BURIED&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Typically, there are guidelines for case style of enumeration values. Imagine that our rule is: all values in enumerations and extensible enumerations MUST be &lt;code&gt;CAPITAL_CASE&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding a rule
&lt;/h3&gt;

&lt;p&gt;First, here is our rule file &lt;code&gt;./plugins/rules/uppercase-schema-enums.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//@ts-check&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;UppercaseSchemaEnums&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/** @type { import('@redocly/openapi-core/src/visitors').Oas3Rule } */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;UppercaseSchemaEnums&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;enumProperties&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;enum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enumLikeProperties&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[])];&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nx"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;enumProperties&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`All enum values should be uppercase`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Export a rule function &lt;code&gt;UppercaseSchemaEnums&lt;/code&gt; that can be configured with enumeration-like properties in addition to the standard &lt;code&gt;enum&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;All rules (like preprocessors) should return a &lt;em&gt;visitor&lt;/em&gt;. We use &lt;code&gt;Schema&lt;/code&gt; visitor with two methods:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;skip&lt;/code&gt; method to ensure that we check enumerations only for properties with &lt;code&gt;type: string&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;normal &lt;code&gt;enter&lt;/code&gt; method to check the rule&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;To report a problem we use the second argument of &lt;code&gt;enter&lt;/code&gt; method: context. It’s &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment"&gt;destructured&lt;/a&gt; to &lt;code&gt;report&lt;/code&gt; and &lt;code&gt;location&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;report&lt;/code&gt; is a function to report a problem (duh!)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;location&lt;/code&gt; has several properties and methods, in our example we use &lt;code&gt;location.child()&lt;/code&gt; to ask reporter to pinpoint the problem to the node’s child location. Thus, the problem will be pointed to &lt;code&gt;state.x-aviskase-enum&lt;/code&gt; instead of simply &lt;code&gt;state&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Enabling the rule via a custom plugin
&lt;/h3&gt;

&lt;p&gt;Similarly to the preprocessor plugin, we keep all custom rules bundled as a plugin in &lt;code&gt;./plugins/custom-rules.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//@ts-check&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UppercaseSchemaEnums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./rules/uppercase-schema-enums&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-rules&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/** @type { import('@redocly/openapi-core/src/config/config').CustomRulesConfig } */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;oas3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;uppercase-schema-enums&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UppercaseSchemaEnums&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, enable the plugin and configure the rule in the &lt;code&gt;.redocly.yaml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./plugins/custom-preprocessors.js'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./plugins/custom-rules.js'&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;custom-rules/uppercase-schema-enums&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;error&lt;/span&gt;
      &lt;span class="na"&gt;enumLikeProperties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;x-aviskase-enum&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final result is in &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/45a99c7f3cfa8e1725ebccc601280f948c637910"&gt;commit 45a99c7&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: checking &lt;code&gt;description&lt;/code&gt; property style
&lt;/h2&gt;

&lt;p&gt;Let’s add one more housekeeping rule: all &lt;code&gt;description&lt;/code&gt; properties should start with a capital letter and end with a punctuation mark.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//@ts-check&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DescriptionStyle&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/** @type { import('@redocly/openapi-core/src/visitors').Oas3Rule } */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;DescriptionStyle&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;Info&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;Server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
    &lt;span class="na"&gt;ServerVariable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;PathItem&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;Operation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; 
    &lt;span class="na"&gt;ExternalDocs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;Parameter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;RequestBody&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;Example&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;SecurityScheme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;checkStyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;attribute&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;skip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="nx"&gt;enter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;loc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;location&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;child&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;attribute&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; description should not be empty string.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;firstChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;firstChar&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;firstChar&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;toLocaleUpperCase&lt;/span&gt;&lt;span class="p"&gt;()){&lt;/span&gt;
        &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; description should start with capital letter.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastChar&lt;/span&gt;&lt;span class="p"&gt;)))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;suggest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`"&amp;lt;...&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;."`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`The &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; description should end with punctuation.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;loc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;suggest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;suggest&lt;/span&gt;&lt;span class="p"&gt;]});&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;description&lt;/code&gt; property can be present &lt;a href="https://spec.openapis.org/oas/v3.0.3.html"&gt;in several objects&lt;/a&gt;. Thus, we target several visitors at once.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;report&lt;/code&gt; can also show suggestions: here we show a possible fix for the punctuation problem.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don’t forget to enable it in &lt;code&gt;.redocly.yaml&lt;/code&gt;. Here is what we have after adding the rule and fixing &lt;em&gt;most&lt;/em&gt; of the errors: &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/33e6a46e39c5237746448d8fcc9e3b8924b7d176"&gt;commit 33e6a46&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But there are still two errors to fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;validating /openapi/gates.yaml...
[1] openapi/components/schemas/Stargate.yaml:10:5 at #/properties/state/description

The Schema description should end with punctuation.

Did you mean: "&amp;lt;...&amp;gt;, `BURIED`." ?

 8 | - nullable: true
 9 | state:
10 | type: string
11 | nullable: true
 … | &amp;lt; 4 more lines &amp;gt;
16 | environment:
17 | type: string
18 | description: Last known place where gate is situated.

Error was generated by the custom-rules/description-style rule.

[2] openapi/components/schemas/Stargate.yaml:18:18 at #/properties/environment/description

The Schema description should end with punctuation.

Did you mean: "&amp;lt;...&amp;gt;DE_OBJECT`." ?

16 | environment:
17 | type: string
18 | description: Last known place where gate is situated.
19 | nullable: true
20 | x-aviskase-enum:

Error was generated by the custom-rules/description-style rule.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm, looks confusing. Thanks to &lt;code&gt;suggest&lt;/code&gt; we can at least see that something was added to the original description… Oh, right, that was our preprocessor! That’s why I suggested to use it in the first place: if we were to modify descriptions via decorators, the rule wouldn’t be able to catch this problem.&lt;/p&gt;

&lt;p&gt;Finally, to make linter passing, we need to fix the preprocessor plugin code &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/81a43321ae607fd20afd5abe4922fc86fa0fcce0"&gt;commit 81a4332&lt;/a&gt;.&lt;/p&gt;






&lt;p&gt;Rules are cooler than preprocessors and decorators: they support nested visitors. I’ll cover this concept in the next article. Until then, try to come up with an example where the uppercase rule in its current form might have undesired behavior.&lt;/p&gt;

</description>
      <category>api</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Using openapi-cli: custom preprocessing</title>
      <dc:creator>Yuliya Bagriy</dc:creator>
      <pubDate>Tue, 17 Aug 2021 03:44:37 +0000</pubDate>
      <link>https://dev.to/aviskase/using-openapi-cli-custom-preprocessing-1ha7</link>
      <guid>https://dev.to/aviskase/using-openapi-cli-custom-preprocessing-1ha7</guid>
      <description>&lt;p&gt;The key feature of &lt;code&gt;openapi-cli&lt;/code&gt; is its extensibility. There are three ways to extend it: &lt;a href="https://redoc.ly/docs/cli/custom-rules/"&gt;preprocessors, rules, and decorators&lt;/a&gt;. In comparison, &lt;a href="https://meta.stoplight.io/docs/spectral"&gt;Spectral&lt;/a&gt; supports only custom rules.&lt;/p&gt;

&lt;p&gt;In this tutorial, we’ll start with &lt;strong&gt;preprocessors&lt;/strong&gt;. They are used to transform OpenAPI description document &lt;em&gt;before&lt;/em&gt; validation and linting. Mind you, documentation says they should be avoided, because custom preprocessors tend to be error prone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: extensible enumerations
&lt;/h2&gt;

&lt;p&gt;In our Stargate Network API there is a &lt;a href="https://github.com/aviskase/openapi-cli-examples/blob/bb95d479cc184221b38ff5d5371767f0b3f32f74/openapi/components/schemas/Stargate.yaml"&gt;&lt;code&gt;Stargate.yaml&lt;/code&gt; schema&lt;/a&gt; with two properties defined with enumerations: &lt;code&gt;state&lt;/code&gt; and &lt;code&gt;environment&lt;/code&gt;. We might think that we captured all potential values, but we can’t be sure.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://opensource.zalando.com/restful-api-guidelines/#112"&gt;Zalando API guidelines&lt;/a&gt; and &lt;a href="https://livebook.manning.com/book/the-design-of-web-apis/chapter-9/table9.1"&gt;The Design of Web APIs by Arnaud Lauret&lt;/a&gt; warn us that enumerations can cause API compatibility breaks, especially with outputs.&lt;/p&gt;

&lt;p&gt;Imagine the scenario. We generated SDK for the initial version of the API, and the generator was smart enough to use enumerations in the selected programming language. Some time later we push a new API version where &lt;code&gt;environment&lt;/code&gt; can also be &lt;code&gt;STAR&lt;/code&gt;. Customers, who are still on the first version of the SDK, won’t be able to process API responses containing this value, because most probably their clients will crash with something-something-uprocessable-entity exceptions.&lt;/p&gt;

&lt;p&gt;Thus, similarly to Zalando, we will use a custom &lt;a href="https://spec.openapis.org/oas/v3.0.3.html#specification-extensions"&gt;vendor extension&lt;/a&gt; to document possible values. I’m calling it &lt;code&gt;x-aviskase-enum&lt;/code&gt; just in case not to introduce conflicts with other extensions. &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/fa78766d7ea2cd245740373efb951bffe7b2facf"&gt;Commit fa78766&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Surfacing values in the documentation
&lt;/h2&gt;

&lt;p&gt;Oops, now there is no way to see known values in the generated reference docs. The manual fix is to add these values to the &lt;code&gt;description&lt;/code&gt;. But this is too cumbersome, especially if you’ll ever want to change presentation format.&lt;/p&gt;

&lt;p&gt;Preprocessor for the rescue! What we want it to do:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find all properties with &lt;code&gt;x-aviskase-enum&lt;/code&gt; property.&lt;/li&gt;
&lt;li&gt;Grab the list of values, format them nicely, and add it to the &lt;code&gt;description&lt;/code&gt;. Make sure not to overwrite existing description if it’s already present!&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Adding a preprocessor
&lt;/h3&gt;

&lt;p&gt;Let’s create a preprocessor first &lt;code&gt;./plugins/preprocessors/add-enum-to-description.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//@ts-check&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;AddEnumToDescription&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/** @type { import('@redocly/openapi-core/src/visitors').Oas3Preprocessor } */&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;AddEnumToDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vendorExtension&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vendorExtension&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;x-enum&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Schema&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;vendorExtension&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;appendToDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;vendorExtension&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;appendToDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;additionalDescription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`Possible values: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;\n\n&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;additionalDescription&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;additionalDescription&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What’s happening here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We export a preprocessor function &lt;code&gt;AddEnumToDescription&lt;/code&gt; that accepts configuration &lt;code&gt;options&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;vendorExtension&lt;/code&gt; option is used to define extension name for extensible enumeration. If not provided, it equals to &lt;code&gt;x-enum&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;All &lt;code&gt;openapi-cli&lt;/code&gt; preprocessors should return a &lt;em&gt;visitor&lt;/em&gt;. In our case, we specifically indicate to take into account only schema nodes.&lt;/li&gt;
&lt;li&gt;If a schema has defined extensible enumeration property, we update its description with formatted list of values.&lt;/li&gt;
&lt;/ul&gt;






&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Per documentation, for type support you can use &lt;code&gt;import('@redocly/openapi-cli').Oas3Preprocessor}&lt;/code&gt;. It never worked for me, so I use a “full path” &lt;code&gt;import('@redocly/openapi-core/src/visitors').Oas3Preprocessor&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Enabling our preprocessor via custom plugin
&lt;/h3&gt;

&lt;p&gt;Let’s keep our preprocessor (and the future ones) bundled inside one plugin via &lt;code&gt;./plugins/custom-preprocessors.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//@ts-check&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;AddEnumToDescription&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./preprocessors/add-enum-to-description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;custom-preprocessors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="cm"&gt;/** @type { import('@redocly/openapi-core/src/config/config').PreprocessorsConfig } */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;preprocessors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;oas3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;add-x-enum-to-description&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AddEnumToDescription&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;preprocessors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we defined plugin’s &lt;code&gt;id&lt;/code&gt; and a mapping between preprocessor’s name (&lt;code&gt;add-x-enum-to-description&lt;/code&gt;) and its implementation.&lt;/p&gt;

&lt;p&gt;Then we need to add this custom plugin to the &lt;code&gt;.redocly.yaml&lt;/code&gt; and to enable the preprocessor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;./plugins/custom-preprocessors.js'&lt;/span&gt;
  &lt;span class="na"&gt;preprocessors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;custom-preprocessors/add-x-enum-to-description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;vendorExtension&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;x-aviskase-enum&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The final result is in &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/665492641fa119292d2ef7aa2c6bd4a87266ff9b"&gt;commit 6654926&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it looks in the docs
&lt;/h2&gt;

&lt;p&gt;Now, when we render a reference documentation, we can see all values with simple markdown formatting.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--N_qdn0-U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aviskase.com/articles/2021/08/16/using-openapi-cli-custom-preprocessing/x_enum_render.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--N_qdn0-U--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aviskase.com/articles/2021/08/16/using-openapi-cli-custom-preprocessing/x_enum_render.png" alt="Redoc with modified descriptions for extensible enumerations"&gt;&lt;/a&gt;&lt;/p&gt;



&lt;p&gt;By the way, can you spot operations where we could have left normal &lt;code&gt;enum&lt;/code&gt; and why?&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a preprocessor here?
&lt;/h2&gt;

&lt;p&gt;As mentioned before, preprocessors might be brittle. We could have used decorator instead. I’ll show you the reason in the next article, but here is a sneak peek: we want to be able to &lt;a href="https://www.aviskase.com/articles/2021/09/06/using-openapi-cli-custom-rules/"&gt;&lt;em&gt;lint&lt;/em&gt; modified descriptions&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>api</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Using openapi-cli during API design: part two</title>
      <dc:creator>Yuliya Bagriy</dc:creator>
      <pubDate>Tue, 23 Mar 2021 04:30:57 +0000</pubDate>
      <link>https://dev.to/aviskase/using-openapi-cli-during-api-design-part-two-352i</link>
      <guid>https://dev.to/aviskase/using-openapi-cli-during-api-design-part-two-352i</guid>
      <description>&lt;p&gt;&lt;a href="https://www.aviskase.com/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/"&gt;Part one&lt;/a&gt; showed the basics of using &lt;code&gt;openapi-cli&lt;/code&gt; for a single-definition project. Let’s see how it works for multi-definition projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multi-what?
&lt;/h2&gt;

&lt;p&gt;By multi-definition project I mean a project with several OpenAPI description documents. For example, it can be useful if you want to keep contracts for all services in one place. Then, you can reuse common schemas between definitions.&lt;/p&gt;

&lt;p&gt;Another use case is keeping parts of your APIs hidden from the final description document (internal and external APIs). This approach will mostly work only if separation is on path-level (&lt;a href="https://redoc.ly/docs/resources/hide-apis/"&gt;see more here&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;In our small Stargate API example we are splitting gates and addresses APIs into two description docs (&lt;a href="https://github.com/aviskase/openapi-cli-examples/commit/18f1bdb2de458859b93c1c99a6400567a2cb9442"&gt;commit&lt;/a&gt;). Next step is to change &lt;a href="https://redoc.ly/docs/cli/configuration/#apidefinitions"&gt;configuration file&lt;/a&gt;, so that &lt;code&gt;openapi-cli&lt;/code&gt; would know that we have two definitions (&lt;a href="https://github.com/aviskase/openapi-cli-examples/commit/47c4a4947b9827509a02c52550f036a139dccf36"&gt;commit&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;gates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openapi/gates.yaml&lt;/span&gt;
  &lt;span class="na"&gt;addresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;openapi/addresses.yaml&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But now we need to fix our scripts in the &lt;code&gt;package.json&lt;/code&gt; so that we could work with both definitions!&lt;/p&gt;

&lt;h2&gt;
  
  
  Preview
&lt;/h2&gt;

&lt;p&gt;If you simply run &lt;code&gt;openapi preview-docs&lt;/code&gt; as we did before, it will start server only for the first definition in config file (in our case: gates). To preview a specific definition, you need to pass a definition label from configuration file: &lt;code&gt;openapi preview-docs addresses&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But what if we want to preview both definitions? The simple fix will be adding separate scripts to run each definition on different ports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preview:gates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"openapi preview-docs -p 8080 gates"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preview:addresses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"openapi preview-docs -p 8081 addresses"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, run both commands (either setting them to background processes or in the different terminals).&lt;/p&gt;

&lt;h2&gt;
  
  
  Linting
&lt;/h2&gt;

&lt;p&gt;Nothing to change here! &lt;code&gt;openapi lint&lt;/code&gt; without any definition labels will check all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundling
&lt;/h2&gt;

&lt;p&gt;Remember how we had to use &lt;code&gt;-o dist/openapi&lt;/code&gt; to properly generate bundled desciption doc for single-definition project? Now we should simply leave it as &lt;code&gt;-o dist&lt;/code&gt;. This will generate &lt;code&gt;dist/gates.yaml&lt;/code&gt; and &lt;code&gt;dist/addresses.yaml&lt;/code&gt; based on definitions labels.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating reference docs
&lt;/h2&gt;

&lt;p&gt;To generate static reference docs we should split &lt;code&gt;npm run docs&lt;/code&gt; into separate scripts to target each definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"docs:gates"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node plugins/prepareOptions.js &amp;amp;&amp;amp; redoc-cli bundle dist/gates.yaml -o dist/redoc-gates.html --options .redoc.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"docs:addresses"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node plugins/prepareOptions.js &amp;amp;&amp;amp; redoc-cli bundle dist/addresses.yaml -o dist/redoc-addresses.html --options .redoc.json"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"docs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npm run docs:gates &amp;amp;&amp;amp; npm run docs:addresses"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no need to run &lt;code&gt;prepareOptions.js&lt;/code&gt; twice, but I’m keeping it in case I’d want to generate only one reference doc.&lt;/p&gt;




&lt;p&gt;The final working &lt;code&gt;package.json&lt;/code&gt; is &lt;a href="https://github.com/aviskase/openapi-cli-examples/commit/48599bdfa3322e2976329e62ea2a094be4be8bd4"&gt;here&lt;/a&gt;. As you can see, we have to manually manage some building processes, but the most important pieces (linting &amp;amp; bundling) are quite transparent.&lt;/p&gt;

&lt;p&gt;In the next article we’re gonna finally see juicy linting magic!&lt;/p&gt;

</description>
      <category>api</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Using openapi-cli during API design: part one</title>
      <dc:creator>Yuliya Bagriy</dc:creator>
      <pubDate>Mon, 01 Feb 2021 01:57:57 +0000</pubDate>
      <link>https://dev.to/aviskase/using-openapi-cli-during-api-design-part-one-3n63</link>
      <guid>https://dev.to/aviskase/using-openapi-cli-during-api-design-part-one-3n63</guid>
      <description>&lt;p&gt;Obviously, API design is much more than writing OpenAPI description doc. First of all, should it even be OpenAPI-based? If yes, using &lt;code&gt;openapi-cli&lt;/code&gt; will make your life a bit easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example API
&lt;/h2&gt;

&lt;p&gt;Let’s imagine you’ve been asked to create an API for &lt;a href="https://stargate.fandom.com/wiki/Stargate_Network"&gt;Stargate Network&lt;/a&gt; and you already did some &lt;a href="https://tyk.io/api-design-methodologies/"&gt;preliminary analysis&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Stargate network consists of points in space identifiable via special addresses. When you dial an address (like a phone number), a wormhole is established between your gate and a gate at the address. Gates are physical objects, so they can be destroyed or moved to another address.&lt;/p&gt;

&lt;p&gt;First, resources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Address

&lt;ul&gt;
&lt;li&gt;Id&lt;/li&gt;
&lt;li&gt;Is the address accessible? Can we dial it?&lt;/li&gt;
&lt;li&gt;last known position in human terms (some addresses aren’t fixed points in space)&lt;/li&gt;
&lt;li&gt;Symbols used to dial it

&lt;ul&gt;
&lt;li&gt;in galaxy and out of galaxy addresses&lt;/li&gt;
&lt;li&gt;unique address if it assigned to the gate&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Available gates at this address&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Stargate

&lt;ul&gt;
&lt;li&gt;Id&lt;/li&gt;
&lt;li&gt;Environment: is it located above the surface, underwater, on the surface, or inside the ship?&lt;/li&gt;
&lt;li&gt;State: is the gate functional, destroyed, or buried?&lt;/li&gt;
&lt;li&gt;Address id&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, operations. Nothing fancy, the bare minimum:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List all addresses&lt;/li&gt;
&lt;li&gt;Get address info&lt;/li&gt;
&lt;li&gt;Add new gate info&lt;/li&gt;
&lt;li&gt;Get gate info&lt;/li&gt;
&lt;li&gt;Update gate info&lt;/li&gt;
&lt;/ul&gt;






&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; Don’t forget, not all APIs should be OpenAPI-based. If I needed to design a real Stargate system, I would go with a combo of something like GraphQL for lookup and event-based APIs for anything else (perhaps described with AsyncAPI).&lt;/p&gt;




&lt;p&gt;Let’s create a place to store our OpenAPI description doc: YAML-based, multi-file git repo. You can read more about &lt;a href="https://redoc.ly/docs/resources/openapi-decisions/"&gt;why’s in the Redocly docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/aviskase/openapi-cli-examples"&gt;The example repo is on the GitHub&lt;/a&gt;. At each step of the tutorial you’ll see links to commits as &lt;code&gt;Commit &amp;lt;commit_hash&amp;gt;.&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Repository structure
&lt;/h2&gt;

&lt;p&gt;We’re gonna use &lt;a href="https://github.com/Redocly/create-openapi-repo"&gt;&lt;code&gt;create-openapi-repo&lt;/code&gt;&lt;/a&gt; to generate the initial structure and then tweak it a bit. Run &lt;code&gt;npx create-openapi-repo&lt;/code&gt; inside a directory where you want to generate API structure. One caveat to keep in mind: this tool will attempt to initialize a git repository and make a commit. &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/e5b40a5d3474eec48e236d170c15ee6faf37f631"&gt;Commit e5b40a5&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mHp1WRTK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aviskase.com/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/initRepo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mHp1WRTK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aviskase.com/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/initRepo.png" alt="Output of create-openapi-repo tool"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s explore the generated files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;docs&lt;/code&gt; directory contains HTML template for reference doc. I’m deleting it, because I don’t need any custom template at the moment.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.gitignore&lt;/code&gt; set up to ignore our beloved &lt;code&gt;node_modules&lt;/code&gt; and &lt;code&gt;dist&lt;/code&gt; directory used for generating the final OpenAPI description doc.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.redocly.yaml&lt;/code&gt; is a configuration file for &lt;code&gt;openapi-cli&lt;/code&gt;. Because I removed &lt;code&gt;docs&lt;/code&gt; directory, I also need to remove &lt;code&gt;referenceDocs.htmlTemplate&lt;/code&gt; option here.&lt;/li&gt;
&lt;li&gt;Don’t forget to update the &lt;code&gt;LICENSE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package.json&lt;/code&gt; set up with the most common actions.&lt;/li&gt;
&lt;li&gt;Take your time to read &lt;code&gt;README.md&lt;/code&gt;. It explains what actions are available and provides a sample contribution guidelines. In the real project, you probably don’t want to have &lt;em&gt;all guidelines&lt;/em&gt; there in one file.&lt;/li&gt;
&lt;li&gt;And last, but not least, &lt;code&gt;openapi&lt;/code&gt; directory. This is where your API definitions live. Almost all subdirectories have a relevant &lt;code&gt;README.md&lt;/code&gt; explaining why these particular files were created as well as showing you other alternatives. I don’t want to copy-paste them here, so, go ahead and explore ;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let’s rewind the time. Stargate Network API contract is defined, &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/c2b8d1172129e00903d3d1bd4cbb90f2bf03b51f"&gt;commit c2b8d11&lt;/a&gt;. For more information about keeping your structure &lt;em&gt;DRY&lt;/em&gt; read &lt;a href="https://mux.com/blog/an-adventure-in-openapi-v3-api-code-generation/"&gt;this&lt;/a&gt; and &lt;a href="https://stoplight.io/blog/keeping-openapi-dry-and-portable/"&gt;this&lt;/a&gt;. If you’re interested in reading/watching about it from me, just ping!&lt;/p&gt;






&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The definitions are not state of art, even for this shallow design. Some problems with it were added consciously to simplify the example, others for fixing in later articles.&lt;/p&gt;

&lt;p&gt;And I’m not even talking about how many details are omitted lore-wise!&lt;/p&gt;




&lt;h2&gt;
  
  
  Preview
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;package.json&lt;/code&gt; file there is already an action for previewing the docs: &lt;code&gt;openapi preview-docs&lt;/code&gt;. To execute it, run in the terminal &lt;code&gt;npm run start&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This starts a server with the generated Redoc reference documentation (&lt;a href="https://redoc.ly/docs/cli/commands/#preview-docs"&gt;docs&lt;/a&gt;). It watches changes from the disk; usually I keep it running while writing definitions and use it like a UI of &lt;a href="https://stoplight.io/studio/"&gt;Stoplight Studio&lt;/a&gt; or &lt;a href="https://insomnia.rest/products/designer/"&gt;Insomnia Designer&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As you may (or may not) know, you can customize how Redoc looks like. This is done with&lt;code&gt;referenceDocs&lt;/code&gt; section in &lt;code&gt;.redocly.yaml&lt;/code&gt; (&lt;a href="https://redoc.ly/docs/cli/configuration/reference-docs/"&gt;docs&lt;/a&gt;). Beware, some options are available only for premium edition.&lt;/p&gt;

&lt;p&gt;Options can be divided into two categories: those that affect a theme (colors, fonts) and those for disabling or enabling certain features. For example, &lt;code&gt;requiredPropsFirst: true&lt;/code&gt; will display required properties before others:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--HVeWQvPg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aviskase.com/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/requiredProps.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--HVeWQvPg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.aviskase.com/articles/2021/01/31/using-openapi-cli-during-api-design-part-one/requiredProps.png" alt="Comparison of property display with requiredPropsFirst equal to false or true"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/c873f1593825256a58991e71cc9ec968d892c3b5"&gt;at commit c873f15&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Linting
&lt;/h2&gt;

&lt;p&gt;From time to time you should lint with &lt;code&gt;openapi lint&lt;/code&gt; (&lt;a href="https://redoc.ly/docs/cli/commands/#lint"&gt;docs&lt;/a&gt;). Let’s execute &lt;code&gt;npm run test&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ npm run test

&amp;gt; stargate-network@1.0.0 test /home/aviskase/Projects/openapi-cli-examples
&amp;gt; openapi lint

validating /home/aviskase/Projects/openapi-cli-examples/openapi/openapi.yaml...
[1] openapi/openapi.yaml:1:1 at #/

Servers must be present.

 1 | openapi: 3.0.3
 2 | info:
 … | &amp;lt; 30 more lines &amp;gt;
33 | $ref: components/securitySchemes/api_key.yaml
34 |

Error was generated by the no-empty-servers rule.

/home/aviskase/Projects/openapi-cli-examples/openapi/openapi.yaml: validated in 27ms

❌ Validation failed with 1 error.
run with `--generate-ignore-file` to add all problems to ignore file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Oops! We have an error! Wait a second, &lt;a href="https://redoc.ly/docs/cli/built-in-rules/#no-empty-servers"&gt;&lt;code&gt;no-empty-servers&lt;/code&gt;&lt;/a&gt; rule complains that we must have &lt;code&gt;servers&lt;/code&gt; defined. But &lt;a href="http://spec.openapis.org/oas/v3.0.3.html#openapi-object"&gt;OpenAPI specification doesn’t state this as a required field&lt;/a&gt;, so our description doc is valid. Where does this error come from?&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;.redocly.yaml&lt;/code&gt; was generated, it included &lt;code&gt;lint&lt;/code&gt; section:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;lint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;recommended&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;no-unused-components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;warning&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;no-empty-servers&lt;/code&gt; comes from extending recommended set of rules. If it’s too strict, you may want to change to &lt;code&gt;minimal&lt;/code&gt;. Or change it to &lt;code&gt;all&lt;/code&gt; to get even more errors.&lt;/p&gt;

&lt;p&gt;I prefer keeping recommended rules and explicitly disabling unneeded ones. &lt;a href="https://redoc.ly/docs/cli/configuration/lint/"&gt;Per docs&lt;/a&gt; you need to set &lt;code&gt;no-empty-servers: off&lt;/code&gt; in &lt;code&gt;rules&lt;/code&gt; section, &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/ee2e3ffb2009deefb69d75dd14febbbb90192148"&gt;commit ee2e3ff&lt;/a&gt;. Or you can change severity to &lt;code&gt;warning&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can read more about other built-in rules &lt;a href="https://redoc.ly/docs/cli/built-in-rules/"&gt;in the docs&lt;/a&gt;. In the later article I’ll show how to add your own custom rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bundling
&lt;/h2&gt;

&lt;p&gt;Most of the tools require single-file OpenAPI doc. That’s where bundling comes in. The &lt;code&gt;package.json&lt;/code&gt; is preconfigured with the script to run &lt;code&gt;openapi bundle -o dist&lt;/code&gt; which will save a generated file to the &lt;code&gt;dist&lt;/code&gt; directory. Usually it’s enough, but in some cases you may want to run with &lt;code&gt;--derefenreced&lt;/code&gt; flag to produce a file with internally inlined definitions (&lt;a href="https://redoc.ly/docs/cli/commands/#bundle"&gt;docs&lt;/a&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ npm run build

&amp;gt; stargate-network@1.0.0 build /home/aviskase/Projects/openapi-cli-examples
&amp;gt; openapi bundle -o dist

bundling /home/aviskase/Projects/openapi-cli-examples/openapi/openapi.yaml...
📦 Created a bundle for /home/aviskase/Projects/openapi-cli-examples/openapi/openapi.yaml at dist.yaml 28ms.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hmm, what just happened? Instead of &lt;code&gt;./dist/&amp;lt;filename&amp;gt;.yaml&lt;/code&gt; we’ve got &lt;code&gt;./dist.yaml&lt;/code&gt;. Why?&lt;/p&gt;

&lt;p&gt;Well, this is kinda bug or feature situation. If you &lt;a href="https://github.com/Redocly/openapi-cli/blob/8eeab11ca73902a0e7dcee2375981119f65eb4eb/packages/cli/src/utils.ts#L234"&gt;check the code&lt;/a&gt;, this looks like an intended behavior, but nevertheless confusing. In short, if you have &lt;strong&gt;only one&lt;/strong&gt; API definition, &lt;code&gt;-o&lt;/code&gt; seems to be expecting file path. If you have &lt;strong&gt;multiple&lt;/strong&gt; API definitions (more about that in the upcoming part two), this option can point to directory.&lt;/p&gt;

&lt;p&gt;In our example we have one API definition, so to fix we should change the script either to &lt;code&gt;-o dist/openapi&lt;/code&gt; or to &lt;code&gt;-o dist/openapi.yaml&lt;/code&gt;. Both will work the same, as long as &lt;code&gt;--ext&lt;/code&gt; equals to &lt;code&gt;yaml&lt;/code&gt; by default. If you want to generate file in JSON format, you can use &lt;code&gt;-o dist/openapi --ext json&lt;/code&gt; or &lt;code&gt;-o dist/openapi.json&lt;/code&gt;. If you want both formats, you need to run commands separately, see &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/a4e1e234dc5fd11741c3f09564a8c5ee0932c098"&gt;at commit a4e1e23&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating reference doc
&lt;/h2&gt;

&lt;p&gt;One of the features that &lt;a href="https://github.com/Redocly/openapi-cli/issues/133"&gt;&lt;code&gt;openapi-cli&lt;/code&gt; doesn’t have&lt;/a&gt;, but I need all the time is to generate static Redoc html file. Yes, you’d want to invest in better solutions and processes at some point, but at smaller scale it’s ok to send a simple HTML file when someone from non-dev team wants to see the latest or prototype API thingy.&lt;/p&gt;

&lt;p&gt;First, we need to install &lt;code&gt;redoc-cli&lt;/code&gt; via &lt;code&gt;npm i redoc-cli&lt;/code&gt;. Then, add a new script to &lt;code&gt;package.json&lt;/code&gt; to generate an HTML file inside &lt;code&gt;dist&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"docs": "redoc-cli bundle dist/openapi.yaml -o dist/redoc.html"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After running &lt;code&gt;npm run docs&lt;/code&gt; and opening the generated file you’ll notice that all our custom options are gone. You see, &lt;code&gt;redoc-cli&lt;/code&gt; doesn’t support &lt;code&gt;.redocly.yaml&lt;/code&gt; configuration file. To fix this, add &lt;a href="https://github.com/aviskase/openapi-cli-examples/blob/ad6dacc3464a5800c408ce53aaa5f23bb701600a/plugins/prepareOptions.js"&gt;&lt;code&gt;prepareOptions.js&lt;/code&gt;&lt;/a&gt; which reads &lt;code&gt;referenceDocs&lt;/code&gt; from &lt;code&gt;.redocly.yaml&lt;/code&gt; and saves it to &lt;code&gt;.redoc.json&lt;/code&gt; Next, modify &lt;code&gt;docs&lt;/code&gt; script to run preparation script and use &lt;code&gt;.redoc.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"docs": "node plugins/prepareOptions.js &amp;amp;&amp;amp; redoc-cli bundle dist/openapi.yaml -o dist/redoc.html --options .redoc.json"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/ad6dacc3464a5800c408ce53aaa5f23bb701600a"&gt;at commit ad6dacc&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  IMHO
&lt;/h2&gt;

&lt;p&gt;Personally, I prefer different names for scripts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;preview&lt;/code&gt; instead of &lt;code&gt;start&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lint&lt;/code&gt; instead of &lt;code&gt;test&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bundle&lt;/code&gt; instead of &lt;code&gt;build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;build&lt;/code&gt; for &lt;code&gt;npm run lint &amp;amp;&amp;amp; npm run bundle &amp;amp;&amp;amp; npm run docs&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, all I do is jump between &lt;code&gt;npm run preview&lt;/code&gt; and &lt;code&gt;npm run build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See &lt;a href="https://github.com/aviskase/openapi-cli-examples/tree/51c078e4913cae9f1a323e2a8be625287e343dd2"&gt;at commit 51c078e&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;That’s it for today. In the &lt;a href="https://www.aviskase.com/articles/2021/03/23/using-openapi-cli-during-api-design-part-two/"&gt;part two&lt;/a&gt;, I’ll show how to manage multiple definitions per repository.&lt;/p&gt;

</description>
      <category>api</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Using openapi-cli for API exploration</title>
      <dc:creator>Yuliya Bagriy</dc:creator>
      <pubDate>Sun, 10 Jan 2021 19:28:31 +0000</pubDate>
      <link>https://dev.to/aviskase/using-openapi-cli-for-api-exploration-414d</link>
      <guid>https://dev.to/aviskase/using-openapi-cli-for-api-exploration-414d</guid>
      <description>&lt;p&gt;&lt;a href="https://www.aviskase.com/articles/2020/12/20/using-openapi-cli-intro/"&gt;As promised&lt;/a&gt;, let’s dive into the usage of &lt;code&gt;openapi-cli&lt;/code&gt;. The first topic is semi-non-technical: API exploration. You might be interested if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you’re a tech writer&lt;/li&gt;
&lt;li&gt;you’re a tester&lt;/li&gt;
&lt;li&gt;you don’t know what the heck API exploration is&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is API exploration
&lt;/h2&gt;

&lt;p&gt;I use this term akin to &lt;em&gt;exploratory testing&lt;/em&gt;. You can search for works of Cem Kaner, James Bach, Michael Bolton, Elisabeth Hendrickson, James Whittaker, but beware, that’s a nice little serpentarium of a field.&lt;/p&gt;

&lt;p&gt;In the simplest words, it is a process of learning something about API I don’t know or have a limited information about. That’s it. The main contexts for this activity are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Necessity&lt;/strong&gt;. I want to use some API.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Curiosity&lt;/strong&gt;. Just looking at how other APIs are done.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assessment&lt;/strong&gt;. I need to check APIs for certain characteristics.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are many ways to perform exploration, countless tools and sources to use. In this article I’ll be looking only at OpenAPI description documents, because that’s where &lt;code&gt;openapi-cli&lt;/code&gt; comes handy.&lt;/p&gt;






&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; When exploring unknown APIs, I run a globally installed package: &lt;code&gt;npm i -g @redocly/openapi-cli&lt;/code&gt;. Then it is accessible in the terminal as &lt;code&gt;openapi &amp;lt;args&amp;gt;&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;openapi stats&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Gathers some basic statistics for an OpenAPI description document. Can be in “stylish” format (as in the example below) or in JSON.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ openapi stats http://localhost:8888/api/v1/swagger.json
Document: http://localhost:8888/api/v1/swagger.json stats:

🚗 References: 12 
📦 External Documents: 0 
📈 Schemas: 12 
👉 Parameters: 9 
🔗 Links: 0 
➡️ Path Items: 4 
👷 Operations: 7 
🔖 Tags: 1 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing useful for me at the moment. The only use case that comes to mind is when you have a bunch of documents for an &lt;strong&gt;assessment&lt;/strong&gt; and you’re not sure where to start. Then, you can check stats for each doc and proceed with the smallest one.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;openapi preview-docs&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This command is the primary reason I have &lt;code&gt;openapi-cli&lt;/code&gt; installed as a global package: I use it all the time. The command accepts path or link to the existing OpenAPI description document and starts a local HTTP server with the generated Redoc reference doc.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;➜ openapi preview-docs http://localhost/api/v1/swagger.json
Using Redoc community edition.
Login with openapi-cli login or use an enterprise license key to preview with the premium docs.

🔎 Preview server running at http://127.0.0.1:8080

👀 Watching http://localhost/api/v1/swagger.json and all related resources for changes

Bundling...

Created a bundle for http://localhost/api/v1/swagger.json successfully
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why you’d want that?&lt;/p&gt;

&lt;p&gt;First, sometimes there is just an OpenAPI description document and nothing else. No documentation whatsoever. Rare, but I do stumble upon this with internal APIs.&lt;/p&gt;

&lt;p&gt;The second reason is when there are some reference docs, but they don’t fit you.&lt;/p&gt;

&lt;p&gt;For example, it’s a SwaggerUI, and you don’t like it, or the description doc has advanced features like &lt;code&gt;oneOf&lt;/code&gt; or &lt;code&gt;discriminator&lt;/code&gt;. In such case, SwaggerUI cannot generate fancy form-based presentation, so you have to read bare model definitions.&lt;/p&gt;

&lt;p&gt;Or perhaps you have special needs. Lorna Jane Mitchell in her presentation on &lt;a href="https://lornajane.net/resource/delightful-sdks-with-openapi"&gt;Delightful SDKs&lt;/a&gt; mentions that she often generates her own local reference docs because official documentations aren’t &lt;em&gt;accessible&lt;/em&gt;. AFAIK, she uses Redoc and made several pull requests specifically for better accessibility support.&lt;/p&gt;






&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You can generate reference docs with &lt;a href="https://www.npmjs.com/package/redoc-cli"&gt;&lt;code&gt;redoc-cli&lt;/code&gt;&lt;/a&gt;, but I prefer using &lt;code&gt;openapi-cli&lt;/code&gt; as a one-stop shop. The difference is when you want to generate static HTML with your theme: right now you have to use a small hack &lt;a href="https://github.com/Redocly/openapi-cli/issues/133"&gt;from this issue&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;openapi split&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Reading a big OpenAPI description doc is cumbersome. Especially when you need to jump back and forth between references.&lt;/p&gt;

&lt;p&gt;Split for the rescue! It’s an opposite of the &lt;a href="https://www.aviskase.com/articles/2020/12/07/crash-course-into-api-related-terminology/#ref"&gt;bundling operation&lt;/a&gt; and separates references into several files. Though, it’s a bit smarter; for example, it splits paths into separate files too.&lt;/p&gt;



&lt;p&gt;Warning! This command doesn’t support OpenAPI 2.&lt;/p&gt;

&lt;p&gt;Let’s split a description doc into &lt;code&gt;out&lt;/code&gt; directory: &lt;code&gt;openapi split --outDir out openapi.json&lt;/code&gt;. Here is a sample result:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/out 
➜ tree
.
├── components
│ ├── parameters
│ │ ├── pageNumber.yaml
│ │ ├── pageSize.yaml
│ │ └── resourceId.yaml
│ ├── requestBodies
│ │ ├── User.yaml
│ ├── responses
│ │ ├── Error.yaml
│ │ ├── Forbidden.yaml
│ │ ├── Ok.yaml
│ │ └── Unauthorized.yaml
│ └── schemas
│ ├── Id.yaml
│ ├── ResourceId.yaml
│ ├── UserCollection.yaml
│ └── User.yaml
├── openapi.yaml
└── paths
    ├── users@{id}.yaml
    └── users.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another reason to use split is when you’re not just exploring API, but you plan to work on it. For example, when you’re switching from annotations-driven to handcrafted approach; &lt;em&gt;split creates a good starting point for refactoring&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;openapi lint&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You may want to do &lt;a href="https://www.aviskase.com/articles/2020/12/07/crash-course-into-api-related-terminology/#description-doc-validation-vs-linting-vs-preprocessing"&gt;linting&lt;/a&gt; in &lt;strong&gt;assessment&lt;/strong&gt; or &lt;strong&gt;curiosity&lt;/strong&gt; contexts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Assessment:

&lt;ul&gt;
&lt;li&gt;Is this old API at least valid per OpenAPI standard or it’s complete garbage?&lt;/li&gt;
&lt;li&gt;We came up with new API guidelines. Do our existing APIs conform to it? How many don’t?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Curiosity:

&lt;ul&gt;
&lt;li&gt;I wonder how many APIs out there follow this rule? Is it common and should we apply it in our API?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you can see, the biggest benefits of &lt;code&gt;openapi lint&lt;/code&gt; come when you use custom rules and configurations. This topic I’ll be covering in later articles, stay tuned.&lt;/p&gt;




&lt;p&gt;If you want to read more about OpenAPI-based API exploration, I recommend &lt;a href="https://apihandyman.io/api-toolbox-jq-and-openapi-part-1-using-jq-to-extract-data-from-openapi-files/"&gt;Arnaud Lauret’s series of articles about &lt;code&gt;jq&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>api</category>
      <category>openapi</category>
      <category>exploratorytesting</category>
    </item>
    <item>
      <title>Using openapi-cli: intro</title>
      <dc:creator>Yuliya Bagriy</dc:creator>
      <pubDate>Mon, 21 Dec 2020 03:26:21 +0000</pubDate>
      <link>https://dev.to/aviskase/using-openapi-cli-intro-3khh</link>
      <guid>https://dev.to/aviskase/using-openapi-cli-intro-3khh</guid>
      <description>&lt;p&gt;Before we jump into the usage of this mysterious tool with way too generic name, let me give you an introduction.&lt;/p&gt;

&lt;p&gt;There are many tools for supporting API design via OpenAPI description documents. You can check &lt;a href="https://openapi.tools/"&gt;the list here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The most common tasks are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dereferencing&lt;/li&gt;
&lt;li&gt;linting&lt;/li&gt;
&lt;li&gt;reference documentation preview and/or generation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I talked about dereferencing &lt;a href="https://www.aviskase.com/articles/2020/12/07/crash-course-into-api-related-terminology/#ref"&gt;not long ago&lt;/a&gt;. In short, it’s needed to produce a final OpenAPI description doc from more DRY and granular schemas. You can &lt;a href="https://stoplight.io/blog/keeping-openapi-dry-and-portable/"&gt;read more about the process here&lt;/a&gt;. One of the tools that can help you is &lt;a href="https://github.com/APIDevTools/json-schema-ref-parser"&gt;&lt;code&gt;json-schema-ref-parser&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.aviskase.com/articles/2020/12/07/crash-course-into-api-related-terminology/#description-doc-validation-vs-linting-vs-preprocessing"&gt;Linting&lt;/a&gt; is used to check OpenAPI description doc for errors and style guide violations. &lt;a href="https://stoplight.io/spectral"&gt;Spectral&lt;/a&gt; comes to mind; Arnaud Lauret has a great talk about &lt;a href="https://apihandyman.io/the-augmented-api-design-reviewer/"&gt;using it for design reviews&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://swagger.io/tools/swagger-ui/"&gt;SwaggerUI&lt;/a&gt; is the most common choice for reference documentation generation. If you need something more aesthetically pleasing you can try &lt;a href="https://redoc.ly/redoc"&gt;Redoc&lt;/a&gt; or &lt;a href="https://mrin9.github.io/RapiDoc/"&gt;RapiDoc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But there is a tool that combines all of these and more: welcome to &lt;code&gt;openapi-cli&lt;/code&gt; (&lt;a href="https://github.com/Redocly/openapi-cli"&gt;GitHub&lt;/a&gt;, &lt;a href="https://redoc.ly/openapi-cli"&gt;docs&lt;/a&gt;). It supports both OpenAPI 2 (fka Swagger) and OpenAPI 3; support for 3.1 is coming soon. At the time of the writing, the tool’s version 1 is still in beta phase, so keep it in mind. But I’m fairly confident in using it anyway. Why? Two reasons.&lt;/p&gt;

&lt;p&gt;First of all, it’s developed by the same team as the famous Redoc, so you already know how reference docs look like =) Redoc &lt;em&gt;is&lt;/em&gt; famous. You cannot read a book or attend a conference without seeing it mentioned by someone. A lot of people use it, so it would make sense for a &lt;code&gt;openapi-cli&lt;/code&gt; to be developed further.&lt;/p&gt;

&lt;p&gt;The second reason is stated on the team’s site: &lt;em&gt;“built because we needed it”&lt;/em&gt;. All tools developed by Redocly are used in the other company of its founder, Adam Altman, &lt;a href="https://www.rebilly.com/"&gt;Rebilly&lt;/a&gt;. These tools weren’t created in vacuum; they are constantly dogfooded by the developers whose &lt;em&gt;primary business is making good APIs&lt;/em&gt;. By the way, check their &lt;a href="https://github.com/Rebilly/api-definitions"&gt;api definitions&lt;/a&gt; for an inspiration on how to structure description doc development.&lt;/p&gt;

&lt;p&gt;So, in this series of articles I’m gonna write about my experience of using &lt;code&gt;openapi-cli&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We gonna go through themes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;using &lt;code&gt;openapi-cli&lt;/code&gt; for API exploration&lt;/li&gt;
&lt;li&gt;basic API doc structuring and preparation (dereferencing, previewing, linting, handing off results)&lt;/li&gt;
&lt;li&gt;advanced topic: preprocessing&lt;/li&gt;
&lt;li&gt;advanced topic: custom linting rules&lt;/li&gt;
&lt;li&gt;advanced topic: decorators&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And while I’m writing next posts (hehe), install and play with it!&lt;/p&gt;

&lt;p&gt;P.S. It’s a pity that Redocly team isn’t very active about advertising themselves. While everyone knows and praises Redoc, &lt;code&gt;openapi-cli&lt;/code&gt; isn’t that well-known. Maybe the team wants to finish the beta first? Anyway, I was using it for almost a year, and I think it’s time to break the silence and give it some public love!&lt;/p&gt;

</description>
      <category>api</category>
      <category>openapi</category>
    </item>
    <item>
      <title>Crash course into API-related terminology</title>
      <dc:creator>Yuliya Bagriy</dc:creator>
      <pubDate>Tue, 08 Dec 2020 01:59:28 +0000</pubDate>
      <link>https://dev.to/aviskase/crash-course-into-api-related-terminology-159b</link>
      <guid>https://dev.to/aviskase/crash-course-into-api-related-terminology-159b</guid>
      <description>&lt;p&gt;Anyone trying to dive into the world of APIs is doomed to be confused by conflicting terminology. In this post I’m gonna touch upon the main definitions and processes. I already tried it once during &lt;a href="https://www.aviskase.com/articles/2020/03/08/hmms-february/"&gt;lunch&amp;amp;learn session at work&lt;/a&gt;, but the time has come to revisit &lt;a href="https://github.com/aviskase/trucs/blob/master/api_salad.pdf"&gt;that old presentation&lt;/a&gt; in the written form.&lt;/p&gt;

&lt;p&gt;One last note: the basis for my definitions goes from &lt;a href="https://github.com/openapi-contrib/glossary"&gt;OpenAPI Glossary&lt;/a&gt; started by &lt;a href="https://philsturgeon.com/"&gt;Phil Sturgeon&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Main definitions
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Specification&lt;/strong&gt; is some standard or RFC or whatever describing a particular format or protocol.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Schema&lt;/strong&gt; (aka: data model) is a metadata describing the data type, formats, and validation rules. Schema can be defined for content body, header, path or query parameters, etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hypermedia controls&lt;/strong&gt; is an easier way to say &lt;strong&gt;HATEOAS&lt;/strong&gt;. What’s that? It’s a special affordance that allows a client to traverse API without hardcoding links. The simplest example would be including pagination links in the response data. E.g., a client asked to get collection of items — the server responded with first N items and a link to the next page of results — the client can use the link to get the next page without constructing a link on their own.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"links"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"href"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/items?cursor=ae12fb2"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Description&lt;/strong&gt; (aka: definition, contract) is a metadata about API, its endpoints, resources, operations, headers, parameters, and etc.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API description document&lt;/strong&gt; is a file that contains API description. It is usually written with a particular format from specifications like OpenAPI, WSDL, RAML.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API documentation&lt;/strong&gt; is a collection of information pieces that allows clients to successfully use an API. It includes, &lt;em&gt;but not limited to,&lt;/em&gt; an API reference documentation (which is often generated from description document). Other important pieces are: tutorials, concept explanations, registration helpers, SDKs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Specifications (some of them)
&lt;/h2&gt;

&lt;p&gt;There are &lt;em&gt;tons&lt;/em&gt; of API-related specifications. I’ll cover only some.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.openapis.org/"&gt;OpenAPI&lt;/a&gt;&lt;/strong&gt; is used to describe both the service model and the request/response body &lt;em&gt;kinda&lt;/em&gt; based on JSON Schema. It was called Swagger before being donated to Linux Foundation. You probably noticed that this created a sore spot: people still call it Swagger, perhaps, because “OpenAPI” is a bit mouthful. Anyway, know that Swagger is just a part of the SwaggerHub branding of tooling like Swagger Editor or SwaggerUI.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; You may stumble upon people using the word “swagger” even in weirder situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;as a synonym for “API”: “We developed swagger for creating users!”, “Use this swagger to export files!”&lt;/li&gt;
&lt;li&gt;as a synonym for response body: “We make a request and the swagger we got back…”&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;&lt;a href="https://json-schema.org/"&gt;JSON Schema&lt;/a&gt;&lt;/strong&gt; is for describing an instance of JSON data. OpenAPI was using an extended subset of it, but this was fixed in OAS 3.1, and now they are fully compatible. Why does it matter? Well, JSON Schema have additional tooling for client-side validations or HATEOAS support.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://jsonapi.org/"&gt;JSON: API&lt;/a&gt;&lt;/strong&gt; is an anti-bikeshedding spefication for building APIs. Imagine not inventing your own guidelines for naming and other patterns! A notable feature is ingrained support for HATEOAS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.asyncapi.com/"&gt;AsyncAPI&lt;/a&gt;&lt;/strong&gt; is like OpenAPI but for event-driven architectures. Think Kafka, AMQP, WebSocket.&lt;/p&gt;

&lt;p&gt;Notable mentions: &lt;a href="https://grpc.io/"&gt;gRPC&lt;/a&gt;, &lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt;, &lt;a href="https://www.odata.org/"&gt;OData&lt;/a&gt;, &lt;a href="http://stateless.co/hal_specification.html"&gt;HAL&lt;/a&gt;, &lt;a href="https://github.com/kevinswiber/siren"&gt;Siren&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Processes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  API-first vs. API design-first vs. API code-first
&lt;/h3&gt;

&lt;p&gt;Read &lt;a href="https://stoplight.io/blog/is-api-planning-the-same-thing-as-api-design/"&gt;this article for more details&lt;/a&gt;. Here is a TL;DR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;API-first&lt;/strong&gt; is about how you approach your API as a product: not an afterthought on top of already created system, but thought through right from the start. The good example came recently from &lt;a href="https://obsidian.md/"&gt;Obsidian’s devs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For the developer the biggest issue is migrating an existing app that wasn’t designed with plugin/API in mind. Having experienced this first hand with our previous product, we designed Obsidian from the ground up with a plugin API from the first version.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;API design-first&lt;/strong&gt; means prototyping and testing a design before any coding is started. &lt;strong&gt;API code-first&lt;/strong&gt; (or implementation-first) means having some code written even at the the design stage. Often though, a more restricted definition is used and it’s about the source of truth: description doc or code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design-first: “we write description doc first and then implement”.&lt;/li&gt;
&lt;li&gt;Code-first: “we annotate code and generate a description doc from these annotations”.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Beware, as I said, this is a more restricted definition! The article I’ve linked uses a broader one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Description doc validation vs. linting vs. preprocessing
&lt;/h3&gt;

&lt;p&gt;API description doc is often written in machine-readable format, thus, we can manipulate it and check for errors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Validation&lt;/strong&gt; checks that description doc conforms to some specification.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Linting&lt;/strong&gt; checks custom rules like style. For example: check that all paths are lowercase.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Preprocessing&lt;/strong&gt; allows transforming the doc based on additional rules before using it in code or documentation generation. For example, hiding some endpoints from the published version or injecting descriptions from markdown files.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;$ref&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;When working with OpenAPI or JSON Schema you will stumble upon &lt;code&gt;$ref&lt;/code&gt;’s a great deal. These are pointers to JSON structures in different locations: inside the same file or to some other file:&lt;/p&gt;

&lt;p&gt;For example, this reference points to the schema from the other file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
    &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;../components/schemas/Users/User.yaml&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;References help with structuring schemas, separating them into reusable pieces.&lt;/p&gt;

&lt;p&gt;The process of looking for value of &lt;code&gt;$ref&lt;/code&gt; is called &lt;strong&gt;resolution&lt;/strong&gt; (or lookup). Not all tools and libraries play well with multi-file resolutions, so you may want to do one of these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bundling&lt;/strong&gt; (aka: external inlining) replaces &lt;code&gt;$ref&lt;/code&gt;’s to external files with internal references. This creates one file that is easier to share.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dereferencing&lt;/strong&gt; (aka: internal inlining) replaces &lt;code&gt;$ref&lt;/code&gt;’s to external or internal data with actual data. The resulting file will have no &lt;code&gt;$ref&lt;/code&gt;’s, but will be noticeably bigger.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This post was triggered by discussion with &lt;a href="https://unremarkabletester.com/"&gt;Areti Panou&lt;/a&gt; in &lt;a href="https://www.angryweasel.com/ABTesting/"&gt;AB Testing podcast’s slack&lt;/a&gt;. I hope it will help you!&lt;/p&gt;

</description>
      <category>openapi</category>
      <category>api</category>
      <category>apidesign</category>
    </item>
  </channel>
</rss>
