Skip to content

Commit

Permalink
Merge pull request #15 from liblaber/snippets
Browse files Browse the repository at this point in the history
Adding docs and a snippets example
  • Loading branch information
Jim Bennett authored May 20, 2024
2 parents 54bb276 + 89fb0ec commit 7c0dd5f
Show file tree
Hide file tree
Showing 51 changed files with 19,110 additions and 14 deletions.
3 changes: 2 additions & 1 deletion .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
"tamasfe.even-better-toml",
"streetsidesoftware.code-spell-checker",
"remcohaszing.schemastore",
"ms-dotnettools.csdevkit"
"ms-dotnettools.csdevkit",
"esbenp.prettier-vscode"
]
}
},
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -184,3 +184,4 @@ sdk-examples/python/pics
sdk-examples/rest/pics
llama-store.sln
.mono/
docs/static/snippets/
79 changes: 78 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

![Linting: pylint, Code style: black](https://github.com/liblaber/llama-store/workflows/Lint/badge.svg) ![Test with pytest](https://github.com/liblaber/llama-store/workflows/Run%20pytest/badge.svg)![Check spec](https://github.com/liblaber/llama-store/workflows/Update%20OpenAPI%20specs/badge.svg)

![A llama on a stage by a microphone drawn as a bad cartoon](/img/llama-del-rey.webp)
![A llama at the checkout of a store](/img/llama-store.webp)

This is a sample API project to demonstrate how [liblab](https://liblab.com) can generate better SDKs for your APIs. This API has a well-formed OpenAPI spec that generates high quality SDKs.

Expand Down Expand Up @@ -368,3 +368,80 @@ The OpenAPI spec for this API is in the [`spec.json`](/spec.json) and [`spec.yam
cd scripts
./create-specs.sh
```

## Generate API documentation

As a part of the SDK generation using the provided liblab config file, the SDKs for some languages will include [generated code snippets](https://developers.liblab.com/sdk-docs/sdk-docs-overview/#sdk-snippets).

These snippets are designed to be included in your own developer documentation. You can see an example of this in the [`docs`](./docs) folder. This is a [Docusaurus](https://docusaurus.io) site that uses the [Docusaurus OpenAPI plugin](https://github.com/PaloAltoNetworks/docusaurus-openapi-docs) to generate API docs, with an updated theme component showing how to include SDK snippets generated by liblab.

### Generate the documentation

To generate the documentation, you need to do the following:

1. Generate the SDKs using liblab
1. Copy the generated snippets from the SDK folders, to the `docs/snippets` folder, renaming each one to match the language it is for. For example, copy `output/csharp/Documentation/snippets.json` to `docs/snippets/csharp.json`.
1. Head to the docs folder, and generate the API docs. This uses the Docusaurus OpenAPI plugin:

```bash title="Terminal"
cd docs
npm install
npm run docusaurus clean-api-docs all
npm run docusaurus gen-api-docs all
```

There is a helper script that does all of this for you. Run the following command from the root of this repo:

```bash title="Terminal"
./scripts/create-sdk-docs.sh
```

### Launch the documentation

To launch the documentation, you need to do the following:

1. Navigate to the `docs` folder:

```bash title="Terminal"
cd docs
```

1. Start the Docusaurus site:

```bash title="Terminal"
npm run start
```

You can then open the documentation in your browser at [http://localhost:3000](http://localhost:3000). You will be greated by this readme rendered as docs, with the API reference on the side. Expand the API reference to see the full API documentation for each endpoint, with SDK snippets.

![A screenshot of the llama store docs showing the Create API token endpoint and example C# code](/img/llama-store-docs.webp)

### How SDK snippets work

To show the SDK snippets, the Docusaurus OpenAPI theme was [swizzled](https://docusaurus.io/docs/swizzling) to wrap the `APIExplorer/CodeSnippets` component. In the default theme, this component shows examples of calling the API directly in different programming languages.

The swizzled version loads the SDK snippets created with your SDK, and shows those instead. You can see this component at [`docs/src/theme/APIExplorer/CodeSnippets/index.tsx`](./docs/src/theme/APIExplorer/CodeSnippets/index.tsx).

The SDK snippets are in a JSON file that lists the snippets by endpoint and method:

```json title=python.json
{
"endpoints": {
"/llama": {
"get": "from llama_store import LlamaStore, Environment\n\nsdk = LlamaStore(\n access_token=\"YOUR_ACCESS_TOKEN\",\n base_url=Environment.DEFAULT.value\n)\n\nresult = sdk.llama.get_llamas()\n\nprint(result)\n",
"post": "from llama_store import LlamaStore, Environment\nfrom llama_store.models import LlamaCreate\n\nsdk = LlamaStore(\n access_token=\"YOUR_ACCESS_TOKEN\",\n base_url=Environment.DEFAULT.value\n)\n\nrequest_body = LlamaCreate(\n name=\"libby the llama\",\n age=5,\n color=\"brown\",\n rating=1\n)\n\nresult = sdk.llama.create_llama(request_body=request_body)\n\nprint(result)\n"
},
"/llama/{llama_id}": {
"get": "from llama_store import LlamaStore, Environment\n\nsdk = LlamaStore(\n access_token=\"YOUR_ACCESS_TOKEN\",\n base_url=Environment.DEFAULT.value\n)\n\nresult = sdk.llama.get_llama_by_id(llama_id=\"1\")\n\nprint(result)\n",
"put": "from llama_store import LlamaStore, Environment\nfrom llama_store.models import LlamaCreate\n\nsdk = LlamaStore(\n access_token=\"YOUR_ACCESS_TOKEN\",\n base_url=Environment.DEFAULT.value\n)\n\nrequest_body = LlamaCreate(\n name=\"libby the llama\",\n age=5,\n color=\"brown\",\n rating=1\n)\n\nresult = sdk.llama.update_llama(\n request_body=request_body,\n llama_id=\"1\"\n)\n\nprint(result)\n",
"delete": "from llama_store import LlamaStore, Environment\n\nsdk = LlamaStore(\n access_token=\"YOUR_ACCESS_TOKEN\",\n base_url=Environment.DEFAULT.value\n)\n\nresult = sdk.llama.delete_llama(llama_id=\"1\")\n\nprint(result)\n"
},
...
}
...
}
```

Inside the component, the JSON file is loaded, and using the current API endpoint and method, the relevant snippet is extracted from the JSON and shown in an `ApiCodeBlock` - a component from the theme that shows formatted code.

Check out the component source code for more details on how this works.
20 changes: 20 additions & 0 deletions docs/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Dependencies
/node_modules

# Production
/build

# Generated files
.docusaurus
.cache-loader

# Misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
41 changes: 41 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Website

This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.

### Installation

```
$ yarn
```

### Local Development

```
$ yarn start
```

This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.

### Build

```
$ yarn build
```

This command generates static content into the `build` directory and can be served using any static contents hosting service.

### Deployment

Using SSH:

```
$ USE_SSH=true yarn deploy
```

Not using SSH:

```
$ GIT_USER=<Your GitHub username> yarn deploy
```

If you are using GitHub pages for hosting, this command is a convenient way to build the website and push to the `gh-pages` branch.
3 changes: 3 additions & 0 deletions docs/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = {
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
};
60 changes: 60 additions & 0 deletions docs/docs/API/create-api-token.api.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
---
id: create-api-token
title: "Create Api Token"
description: "Create an API token for a user. These tokens expire after 30 minutes."
sidebar_label: "Create Api Token"
hide_title: true
hide_table_of_contents: true
api: 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
sidebar_class_name: "post api-method"
info_path: API/llama-store-api
custom_edit_url: null
---

import ApiTabs from "@theme/ApiTabs";
import DiscriminatorTabs from "@theme/DiscriminatorTabs";
import MethodEndpoint from "@theme/ApiExplorer/MethodEndpoint";
import SecuritySchemes from "@theme/ApiExplorer/SecuritySchemes";
import MimeTabs from "@theme/MimeTabs";
import ParamsItem from "@theme/ParamsItem";
import ResponseSamples from "@theme/ResponseSamples";
import SchemaItem from "@theme/SchemaItem";
import SchemaTabs from "@theme/SchemaTabs";
import Markdown from "@theme/Markdown";
import OperationTabs from "@theme/OperationTabs";
import TabItem from "@theme/TabItem";

<h1 className={"openapi__heading"}>Create Api Token</h1>

<MethodEndpoint method={"post"} path={"/token"}></MethodEndpoint>



Create an API token for a user. These tokens expire after 30 minutes.

Once you have this token, you need to pass it to other endpoints in the Authorization header as a Bearer token.

## Request

<MimeTabs className={"openapi-tabs__mime"}><TabItem label={"application/json"} value={"application/json-schema"}><details style={{}} className={"openapi-markdown__details mime"} data-collapsed={false} open={true}><summary style={{}} className={"openapi-markdown__details-summary-mime"}><h3 className={"openapi-markdown__details-summary-header-body"}>Body</h3><strong className={"openapi-schema__required"}>required</strong></summary><div style={{"textAlign":"left","marginLeft":"1rem"}}></div><ul style={{"marginLeft":"1rem"}}><SchemaItem collapsible={false} name={"email"} required={true} schemaName={"Email (string)"} qualifierMessage={"**Possible values:** `>= 5 characters` and `<= 254 characters`, Value must match regular expression `.+\\@.+\\..+`"} schema={{"type":"string","maxLength":254,"minLength":5,"pattern":".+\\@.+\\..+","title":"Email","description":"The email address of the user. This must be unique across all users.","examples":["[email protected]"]}}></SchemaItem><SchemaItem collapsible={false} name={"password"} required={true} schemaName={"Password (string)"} qualifierMessage={"**Possible values:** `>= 8 characters` and `<= 254 characters`"} schema={{"type":"string","maxLength":254,"minLength":8,"title":"Password","description":"The password of the user. This must be at least 8 characters long, and contain at least one letter, one number, and one special character.","examples":["Password123!"]}}></SchemaItem></ul></details></TabItem></MimeTabs><div><div><ApiTabs label={undefined} id={undefined}><TabItem label={"201"} value={"201"}><div>

A new API token for the user

</div><div><MimeTabs className={"openapi-tabs__mime"} schemaType={"response"}><TabItem label={"application/json"} value={"application/json"}><SchemaTabs className={"openapi-tabs__schema"}><TabItem label={"Schema"} value={"Schema"}><details style={{}} className={"openapi-markdown__details response"} data-collapsed={false} open={true}><summary style={{}} className={"openapi-markdown__details-summary-response"}><strong>Schema</strong></summary><div style={{"textAlign":"left","marginLeft":"1rem"}}></div><ul style={{"marginLeft":"1rem"}}><SchemaItem collapsible={false} name={"accessToken"} required={true} schemaName={"Access Token (string)"} qualifierMessage={undefined} schema={{"type":"string","title":"Access Token","description":"The bearer token to use with the API. Pass this in the Authorization header as a bearer token.","examples":["Authorization: Bearer 1234567890abcdef"]}}></SchemaItem><SchemaItem collapsible={false} name={"tokenType"} required={false} schemaName={"Token Type (string)"} qualifierMessage={undefined} schema={{"type":"string","title":"Token Type","description":"The type of token. This will always be bearer.","default":"bearer","examples":["bearer"]}}></SchemaItem></ul></details></TabItem><TabItem label={"Example (from schema)"} value={"Example (from schema)"}><ResponseSamples responseExample={"{\n \"accessToken\": \"string\",\n \"tokenType\": \"bearer\"\n}"} language={"json"}></ResponseSamples></TabItem></SchemaTabs></TabItem></MimeTabs></div></TabItem><TabItem label={"404"} value={"404"}><div>

User not found or the password is invalid

</div><div></div></TabItem><TabItem label={"422"} value={"422"}><div>

Validation Error

</div><div><MimeTabs className={"openapi-tabs__mime"} schemaType={"response"}><TabItem label={"application/json"} value={"application/json"}><SchemaTabs className={"openapi-tabs__schema"}><TabItem label={"Schema"} value={"Schema"}><details style={{}} className={"openapi-markdown__details response"} data-collapsed={false} open={true}><summary style={{}} className={"openapi-markdown__details-summary-response"}><strong>Schema</strong></summary><div style={{"textAlign":"left","marginLeft":"1rem"}}></div><ul style={{"marginLeft":"1rem"}}><SchemaItem collapsible={true} className={"schemaItem"}><details style={{}} className={"openapi-markdown__details"}><summary style={{}}><span className={"openapi-schema__container"}><strong className={"openapi-schema__property"}>detail</strong><span className={"openapi-schema__name"}> object[]</span></span></summary><div style={{"marginLeft":"1rem"}}><li><div style={{"fontSize":"var(--ifm-code-font-size)","opacity":"0.6","marginLeft":"-.5rem","paddingBottom":".5rem"}}>Array [</div></li><SchemaItem collapsible={true} className={"schemaItem"}><details style={{}} className={"openapi-markdown__details"}><summary style={{}}><span className={"openapi-schema__container"}><strong className={"openapi-schema__property"}>loc</strong><span className={"openapi-schema__name"}> object[]</span><span className={"openapi-schema__divider"}></span><span className={"openapi-schema__required"}>required</span></span></summary><div style={{"marginLeft":"1rem"}}><li><div style={{"fontSize":"var(--ifm-code-font-size)","opacity":"0.6","marginLeft":"-.5rem","paddingBottom":".5rem"}}>Array [</div></li><div><span className={"badge badge--info"}>anyOf</span><SchemaTabs><TabItem label={"MOD1"} value={"0-item-properties"}><div style={{"marginTop":".5rem","marginBottom":".5rem"}}>

string

</div></TabItem><TabItem label={"MOD2"} value={"1-item-properties"}><div style={{"marginTop":".5rem","marginBottom":".5rem"}}>

integer

</div></TabItem></SchemaTabs></div><li><div style={{"fontSize":"var(--ifm-code-font-size)","opacity":"0.6","marginLeft":"-.5rem"}}>]</div></li></div></details></SchemaItem><SchemaItem collapsible={false} name={"msg"} required={true} schemaName={"Message (string)"} qualifierMessage={undefined} schema={{"type":"string","title":"Message"}}></SchemaItem><SchemaItem collapsible={false} name={"type"} required={true} schemaName={"Error Type (string)"} qualifierMessage={undefined} schema={{"type":"string","title":"Error Type"}}></SchemaItem><li><div style={{"fontSize":"var(--ifm-code-font-size)","opacity":"0.6","marginLeft":"-.5rem"}}>]</div></li></div></details></SchemaItem></ul></details></TabItem><TabItem label={"Example (from schema)"} value={"Example (from schema)"}><ResponseSamples responseExample={"{\n \"detail\": [\n {\n \"loc\": [\n \"string\",\n 0\n ],\n \"msg\": \"string\",\n \"type\": \"string\"\n }\n ]\n}"} language={"json"}></ResponseSamples></TabItem></SchemaTabs></TabItem></MimeTabs></div></TabItem></ApiTabs></div></div>

Loading

0 comments on commit 7c0dd5f

Please sign in to comment.