Voucherify builds and maintains REST API documentation and SDKs to make it easier for software developers to understand and integrate Voucherify into their e-commerce platforms.
This document describes all deliverables and their development process.
- Voucherify's Documentation and OpenAPI Contribution
Voucherify's dev experience is built around three main items:
- Guides documentation that describes key concepts, integration with Voucherify development, and common recipes.
- API Reference documentation describing REST API endpoints, including available parameters and responses.
- SDKs: clients for all major programming languages, e.g., JavaScript, Java, Ruby, Python, .NET, PHP. The source code of SDKs is hosted on the GitHub platform and available for the developers in language-specific package repositories like NPM, RubyGems, or Apache Maven.
The Guides and API Reference pages are hosted on readme.io, which is a platform for creating and hosting developer documentation. However, the source of the documentation content is stored in the Voucherify Open API GitHub repository.
The guides are stored purely as Markdown files in the guides folder. They can be uploaded to the readme.io platform via readme CLI.
The API Reference pages are built by readme.io. The platform combines the OpenAPI file that describes Voucherify API endpoints, parameters, and responses with the Markdown files from the reference folder.
API Endpoint Pages like GET voucher describe a REST API endpoint, including details like path, HTTP method, path parameters, body parameters, response schema, and response statuses. On the right side of those pages, there is a Playground Widget
that developers can use to make test API calls. Readme.io builds those pages using information about the REST API from the uploaded OpenAPI file and displays UI, so users can explore all the details.
For each endpoint page, there is a corresponding dummy Markdown file like VOUCHERS-Get-Voucher.md. This page allows editing the appearance of the page displayed in readme.io, in particular:
- The Markdown attributes section at the beginning of the file wrapped by
---
describes the page title, type, slug, order, and visibility. [block:html]
section that adds custom styles to the page that hides unnecessary UI elements like Playground language selector or expandable readme object exploration widget. It can be also used to display the "Beta" tag next to the title.
Reamde.io platform compares the operationId
endpoint details attribute from the OpenAPI file with the slug
from Markdown attributes to combine them and display the final version of the API endpoint page.
All API endpoint pages are grouped by sections like Vouchers
, Campaigns
, or Promotions
. Those sections are built by readme.io based on the tags
endpoint details attribute from the OpenAPI file. The attribute must be used in the parentDocSlug
attribute of the dummy Markdown file.
Data model pages like Voucher Object describe the schema of the main building blocks used in specific sections. There are two types of data model pages:
- Using readme.io expandable object exploration widget, like on the Validation Object page,
- Displaying the schema of the object with all attributes in a table like on the Voucher Object page.
We believe that presenting object details in a table is more intuitive for developers. Unfortunately, readme.io does not have the feature to display building block objects defined in OpenAPI in a table format, so we have built a custom JS script (build-update-md-tables-from-openapi
). The script generates Markdown tables automatically from the OpenAPI file and puts them into Markdown files in the reference-docs
folder, e.g.: https://github.com/voucherifyio/voucherify-openapi/blob/master/docs/reference-docs/VOUCHERS-Voucher-Object.md. Once the Markdown files are created, they are uploaded to readme.io with the readme.io CLI.
Pages from the introduction section, like Introduction – What is Voucherify API?, are Markdown pages uploaded to readme.io with readme.io CLI. Their content can be found along with other Markdown files inside the docs/reference-docs
.
To label an API endpoint as a Beta version in readme.io, add the following details in its Markdown file:
- Add
[Beta]
postfix to the page title (title
markdown attribute) - Add the following style to the
[block:html]
section within the<style></style>
tags:
h1::after {\n content: \"BETA\";\n background-color: rgb(237, 117, 71);\n color: rgb(255, 255, 255);\n border-radius: 2rem;padding: 8px 13px 8px;\n white-space: nowrap;font-size:12px;\n}
Note that OpenAPI files slightly differ depending on where we use them.
- [production/readOnly-openAPI.json] - specification version 3.0.1 for all external viewers.
- [reference/OpenAPI.json] - Specification version 3.0.1 with
"type": "null"
usages. - [tmp/referenceToUpload/OpenAPI.json] - Used for readme.io specification version 3.0.1, but it is marked as 3.1.0 to skip the validation check by readme.io. It uses
"type": "null"
. - [tmp/reference/{language}/OpenAPI.json] - Used to generate an SDK.
If you want to contribute to the OpenAPI file, you MUST do it in the reference/OpenAPI.json file, because all other OpenAPI files are generated from this file!
To update the [production/readOnly-openAPI.json] file, run the npm run build-production-openapi
The [tmp/referenceToUpload/OpenAPI.json] file is generated while running the npm run create-clean-project -- (parameters)
command.
The [tmp/reference/{language}/OpenAPI.json] files are generated while running npm run prepare-open-api-for-sdk -- --language=(language)
command. The available languages are ruby
and python
.
The OpenAPI Specification (OAS) is used to create the Voucherify API documentation. The Voucherify OpenAPI file is located in the Voucherify OpenAPI GitHub repository.
We use Stoplight to edit the OpenAPI file as it gives a readable UI that helps to edit the very large json file. Everyone can create a free account on the Stoplight platform.
How to edit the OpenAPI file:
- Upload
./reference/OpenAPI.json
file to the Stoplight platform. - Make changes to the OpenAPI.json file with the Stoplight UI.
- Export the modified OpenAPI content and update the OpenAPI.json file in the repository.
- Ensure that the OpenAPI.json file has only expected changes.
[!WARNING] Each OpenAPI change should be tested by reviewing the documentation on readme.io after the full documentation update process.
The documentation of the events that are used in Voucherify webhooks is generated from an OpenAPIWebhooks.json file.
If you want to contribute to this documentation, follow the guidelines for the Voucherify OpenAPI documentation.
Tips:
- To add a new event, add it to the
"paths"
resources in the OpenAPIWebhooks.json file. Events use thePOST
method. - To add a new event category, add an object to the
"tags"
section. Specify the name and description. The name and the description should be the same, starting with theEvents
word. - If you want to add a page to the Events section, add a Markdown file to the
docs/custom-webhook-sites
folder.- Note: these files require a header wrapped with
---
to describe the page title, type, slug, order, and visibility.
- Note: these files require a header wrapped with
- If an event requires an additional description, add a Markdown file to the
docs/webhook-introductions
folder. The file should be named in the following format:events-{event category tag name}-{event > post > summary > value}
. Example:events-voucher-enabled.md
.- Note: these files do not require a header.
When building new models, follow the following name convention:
- Use the PascalCase.
- If a model is used as a specific API endpoint description (0-level model), follow the pattern:
{Client?}{PathNameResult}{Action}{Differentiator?}{Request|Response}{Body|Query}
, where:- (optional)
Client
: Use for all client schemas. PathNameResult
:location.pathname
WITHOUTv1
andpath parameters
written in PascalCase. Examples:/v1/rewards/{rewardId}/assignments
=>RewardsAssignments
/v1/rewards/{rewardId}/assignments/{assignmentId}
=>RewardsAssignments
/v1/rewards/{rewardId}/assignments/{assignmentId}/redemptions
=>RewardsAssignmentsRedemptions
/client/v1/rewards/{rewardId}/assignments/{assignmentId}/redemptions
=>ClientRewardsAssignmentsRedemptions
Action
: Either taken from an HTTP method, e.g.List
,Get
,Update
,Delete
,Create
or what the endpoint does, e.g.Track
,Validate
,Import
,Export
Get
(single record),List
(multiple record)Update
(single record),UpdateInBulk
(multiple record),Delete
(single record),Create
(single record),CreateInBulk
(multiple record)
- (optional)
Differentiator
: Sub-model title when a 0-level model contains onlyoneOf
. The title of those models must be like the schema name but inTitle Case
and the description must follow the pattern:{Response/Request} {Body/Query} schema for **{Method}** {Path} {OPTIONALLY: and **{Method}** {Path}}
. Examples:Base [PublicationsCreateBaseResponseBody]
(common part of other child models)Vouchers [PublicationsCreateVouchersResponseBody]
Voucher [PublicationsCreateVoucherResponseBody]
Request
orResponse
Body
orQuery
- (optional)
Example of a model that needs a Differentiator
:
"PublicationsCreateResponseBody": {
"title": "Publications Create Response Body",
"type": "object",
"description": "Response body schema for **POST** `v1/publication` and **GET** `v1/publications/create`.",
"oneOf": [
{
"$ref": "#/components/schemas/PublicationsCreateVoucherResponseBody"
},
{
"$ref": "#/components/schemas/PublicationsCreateVouchersResponseBody"
}
]
},
- If a model is used by more than one API endpoint (general model), use simple domain language, e.g.
Customer
,Category
,Discount
,DiscountUnit
. - If a part of a model is used by more than one schema, save this part under a new schema and use it with an
allOf
operator.
If you see a schema with a wrong name, don't hesitate to correct it!
type
- should beobject
orarray
mostly but in some cases, it could be optional.title
- should be the same as the name of the model.description
- should point to the API endpoint that uses this model e.g.{type} body schema for **{method}** {path}
.properties / oneOf / allOf
- should contain all attributes that are used in the API endpoint or$ref
to another schema.
{
"RedemptionsGetResponseBody": {
"type": "object",
"title": "Redemptions Get Response Body",
"description": "Response body schema for **GET** `v1/redemptions/{redemptionId}`",
"oneOf": [
{
"$ref": "#/components/schemas/Redemption"
},
{
"$ref": "#/components/schemas/RedemptionRollback"
}
]
}
}
For example:
- The general voucher model, used in many different API endpoints, should have the name
Voucher
(currently, it has a name:Voucher
) - for path
GET /v1/vouchers
(list vouchers), we have a1_res_vouchers_GET
0-level model that should be namedVouchersListResponseBody
. - for path
GET /v1/vouchers
(list vouchers), we have a1_res_vouchers_GET
0-level model which has a sub-modelVoucher_list_vouchers
that should be namedVouchersListResponseBody
. - General model
Voucher
is used in many paths (GET /v1/vouchers/{code}
,POST /v1/vouchers/qualification
,GET /v1/publications/create
); therefore, it should be renamed toVoucher
.
[!NOTE] Most likely, the general model will be the same as used in the GET method. For example,
CategoriesGetResponseBody
is equal by reference toCategory
. This model most likely will not be used inPUT
requests, because the response in aPUT
request always returns value inupdated_at
, so you will need to create a duplicated model just for the update response.
Contribute with the following good practices in mind:
- For literal unions, use
enum
, - For type unions, use
oneOf
, - For attributes that may contain
null
, add"nullable": true
, - If an attribute is always
null
, set"type": "null"
, - For dates, use
"type": "string", "format": "date-time"
or"type": "string", "format": "date"
, - For the object type
object
, add therequired
attribute which should contain a list of required attributes in the object, - A
nullable
cannot be next to a$ref
. Runnpm run fix-schemas-with-refs
to fix it. writeOnly
andreadOnly
flags should not be used, because they cause errors in generating SDKs
- Install
git
,nodejs
, andnpm
. - Clone the repository locally:
git clone https://github.com/voucherifyio/voucherify-openapi
. - Ensure you have a readme.io account with access to the
Voucherify
project. Ask your line manager for help. - Install
rdme
tool (readme.io CLI). Follow the readme.io installation manual. - Authenticate
rdme
tool by running therdme login
command. You can check if it works with therdme whoami
command. If correct, you should receive theYou are currently logged in as [email protected] to the voucherify project.
response. - Copy
.env.example
to.env
and in this file, add your personal API Key created in readme.io:dashboard
>configuration
>API Keys
.
There are two ways of adding images to Markdown files:
- With a
[block:image]
component, see example in Introduction.md, - With a link declaration, for example
![Welcome Diagram](https://files.readme.io/6070078-welcome-diagram.png "Welcome Diagram")
.
At first, always point to the assets img folder, for example: ![Recent Changes](../../assets/img/guides_getting_started_quickstart_recent_changes_4.png "Recent Changes")
. This path declaration will be automatically updated to a url link during npm run create-clean-project
command. However, if you don't want to update project data, you can run npm run readme-upload-missing-images
instead.
In order to add a new category, go to scripts/create-clean-version.ts
and edit following fragment:
const listOfGuideCategories = [
"Getting started",
"Development",
"Building blocks",
"Campaigns Recipes",
"Discounts Recipes",
"Distributions Recipes",
"More",
];
const listOfReferenceCategories = ["Introduction"];
Note that the order of each category title determines the order on the Voucherify documentation page.
In order to change the order of the categories, change the order of category titles in the script fragment presented above.
In order to remove a category, remove its title from the list. Make sure to remove any Markdown files associated with this categorySl ug
as well.
[!NOTE]
categorySlug
is created from the category title by converting the title tokebab-case
/dash-case
(all lower case). Example:Campaigns Recipes
->campaigns-recipes
.
- For each change / pull request, create your copy of the current documentation, where you can test changes.
- Create your own branch from
master
. - Create a draft pull request.
- Create your own branch from
- Make changes in the repository following the patterns and good practices.
- Run
npm run create-clean-version -- --vt={your name}-{pull request number}
to create a new project version with your tag number.- then follow instructions from the console or add
--ua
or--uploadAll
option to the script
- then follow instructions from the console or add
- If you need to make a change, run
npm run create-clean-version -- --vt={your name}-{pull request number}-{!!!any number!!!}
- then follow instructions from the console or add
--ua
or--uploadAll
option to the script - on the readme dashboard delete version that is not good.
- then follow instructions from the console or add
- If changes are fine, then:
- Add a note in the changelog.
- run
git commit; git push
. - Publish the pull request.
- Ensure the changelog was updated.
- Merge the master branch to your branch by running
git merge master
. - Run
npm run create-clean-version -- --vt={your name}-{pull request number}-master
to ensure the version is up-to-date.- then follow instructions from the console or add
--ua
or--uploadAll
option to the script
- then follow instructions from the console or add
- Test for the last time the changes on readme. You can use the version prepared by the contributor.
- Merge the pull request with the
master
branch. - In readme.io, change the current documentation version from
v2018-08-01
tov2018-08-01-deprecated-mm-dd-yyyy
. - Change the name of your new release version from
2018-08-01-{your name}-{pull request number}
tov2018-08-01
.
[!NOTE] You can use GitHub desktop to perform git actions.
[!NOTE] Readme.io cache pages for 15 minutes, for only logged-out users. If you are logged in, then you will always receive the most recent content.