Skip to content

Instantly share code, notes, and snippets.

@Glutexo
Created June 10, 2020 10:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Glutexo/ebc4d965b5026fa4dca618c92d74e157 to your computer and use it in GitHub Desktop.
Save Glutexo/ebc4d965b5026fa4dca618c92d74e157 to your computer and use it in GitHub Desktop.
Insights Host Inventory fixed OpenAPI Schema
openapi: 3.0.0
info:
description: REST interface for the Insights Platform Host Inventory application.
version: 1.0.0
title: Insights Host Inventory REST Interface
paths:
/hosts:
get:
operationId: api.host.get_host_list
tags:
- hosts
summary: Read the entire list of hosts
description: Read the entire list of all hosts available to the account.
security:
- ApiKeyAuth: []
parameters:
- in: query
name: display_name
schema:
type: string
description: A part of a searched host’s display name.
required: false
- in: query
name: fqdn
schema:
type: string
description: Filter by a host's FQDN
required: false
- in: query
name: hostname_or_id
schema:
type: string
description: 'Search for a host by display_name, fqdn, id'
required: false
- in: query
name: insights_id
schema:
type: string
format: uuid
description: Search for a host by insights_id
required: false
- $ref: '#/components/parameters/branchId'
- $ref: '#/components/parameters/perPageParam'
- $ref: '#/components/parameters/pageParam'
- $ref: '#/components/parameters/orderByParam'
- $ref: '#/components/parameters/orderHowParam'
- $ref: '#/components/parameters/stalenessParam'
- $ref: '#/components/parameters/tagsParam'
- $ref: '#/components/parameters/registered_with'
- $ref: '#/components/parameters/fieldsParam'
responses:
'200':
description: Successfully read the hosts list.
content:
application/json:
schema:
$ref: '#/components/schemas/HostQueryOutput'
post:
operationId: api.host.add_host_list
tags:
- hosts
summary: Create/update multiple host and add them to the host list
description: >-
Create a new host and add it to the host list or update an existing
hosts. A host is updated if there is already one with the same
canonicals facts and belonging to the same account.
security:
- ApiKeyAuth: []
- BearerAuth: []
requestBody:
description: A list of host objects to be added to the host list
required: true
content:
application/json:
schema:
x-body-name: host_list
type: array
items:
$ref: '#/components/schemas/CreateHostIn'
responses:
'207':
description: Successfully created a host.
content:
application/json:
schema:
$ref: '#/components/schemas/BulkHostOut'
examples:
update:
value:
status: 200
host: Host details
create:
value:
status: 201
host: Host details
error:
value:
status: 400
title: Invalid Request
detail: Could not process request
host: Input host data
'400':
description: Invalid request.
'/hosts/{host_id_list}':
get:
tags:
- hosts
summary: Find hosts by their IDs
description: Find one or more hosts by their ID.
operationId: api.host.get_host_by_id
security:
- ApiKeyAuth: []
parameters:
- $ref: '#/components/parameters/hostIdList'
- $ref: '#/components/parameters/branchId'
- $ref: '#/components/parameters/perPageParam'
- $ref: '#/components/parameters/pageParam'
- $ref: '#/components/parameters/orderByParam'
- $ref: '#/components/parameters/orderHowParam'
responses:
'200':
description: Successfully searched for hosts.
content:
application/json:
schema:
$ref: '#/components/schemas/HostQueryOutput'
'400':
description: Invalid request.
'404':
description: Host not found.
delete:
tags:
- hosts
summary: Delete hosts by IDs
description: Delete hosts by IDs
operationId: api.host.delete_by_id
security:
- ApiKeyAuth: []
parameters:
- $ref: '#/components/parameters/hostIdList'
- $ref: '#/components/parameters/branchId'
responses:
'200':
description: Successfully deleted hosts.
'400':
description: Invalid request.
'404':
description: Host not found.
patch:
tags:
- hosts
summary: Update a host
description: Update a host
operationId: api.host.patch_by_id
security:
- ApiKeyAuth: []
parameters:
- $ref: '#/components/parameters/hostIdList'
- $ref: '#/components/parameters/branchId'
requestBody:
description: A group of fields to be updated on the host
required: true
content:
application/json:
schema:
x-body-name: host_data
$ref: '#/components/schemas/PatchHostIn'
responses:
'200':
description: Successfully updated the host.
'400':
description: Invalid request.
'404':
description: Host not found.
'/hosts/{host_id_list}/facts/{namespace}':
patch:
tags:
- hosts
summary: Merge facts under a namespace
description: Merge one or multiple hosts facts under a namespace.
operationId: api.host.merge_facts
security:
- ApiKeyAuth: []
parameters:
- $ref: '#/components/parameters/hostIdList'
- $ref: '#/components/parameters/factsNamespace'
- $ref: '#/components/parameters/branchId'
requestBody:
description: A dictionary with the new facts to merge with the original ones.
required: true
content:
application/json:
schema:
x-body-name: fact_dict
$ref: '#/components/schemas/Facts'
responses:
'200':
description: Successfully merged facts.
'400':
description: Invalid request.
'404':
description: Host or namespace not found.
put:
tags:
- hosts
summary: Replace facts under a namespace
description: Replace facts under a namespace
security:
- ApiKeyAuth: []
operationId: api.host.replace_facts
parameters:
- $ref: '#/components/parameters/hostIdList'
- $ref: '#/components/parameters/factsNamespace'
- $ref: '#/components/parameters/branchId'
requestBody:
description: A dictionary with the new facts to replace the original ones.
required: true
content:
application/json:
schema:
x-body-name: fact_dict
$ref: '#/components/schemas/Facts'
responses:
'200':
description: Successfully replaced facts.
'400':
description: Invalid request.
'404':
description: Host or namespace not found.
'/hosts/{host_id_list}/system_profile':
get:
tags:
- hosts
summary: Return one or more hosts system profile
description: Find one or more hosts by their ID and return the id and system profile
operationId: api.host.get_host_system_profile_by_id
security:
- ApiKeyAuth: []
parameters:
- $ref: '#/components/parameters/hostIdList'
- $ref: '#/components/parameters/perPageParam'
- $ref: '#/components/parameters/pageParam'
- $ref: '#/components/parameters/orderByParam'
- $ref: '#/components/parameters/orderHowParam'
- $ref: '#/components/parameters/branchId'
responses:
'200':
description: Successfully searched for hosts.
content:
application/json:
schema:
$ref: '#/components/schemas/SystemProfileByHostOut'
'400':
description: Invalid request.
'404':
description: Host not found.
'/hosts/{host_id_list}/tags':
get:
tags:
- hosts
summary: Get the tags on a host
description: Get the tags on a host
operationId: api.host.get_host_tags
security:
- ApiKeyAuth: []
parameters:
- $ref: '#/components/parameters/hostIdList'
- $ref: '#/components/parameters/perPageParam'
- $ref: '#/components/parameters/pageParam'
- $ref: '#/components/parameters/orderByParam'
- $ref: '#/components/parameters/orderHowParam'
- $ref: '#/components/parameters/searchParam'
responses:
'200':
description: Successfully found tags.
content:
application/json:
schema:
$ref: '#/components/schemas/TagsOut'
'400':
description: Invalid request.
'/hosts/{host_id_list}/tags/count':
get:
tags:
- hosts
summary: Get the number of tags on a host
description: Get the number of tags on a host
operationId: api.host.get_host_tag_count
security:
- ApiKeyAuth: []
parameters:
- $ref: '#/components/parameters/hostIdList'
- $ref: '#/components/parameters/perPageParam'
- $ref: '#/components/parameters/pageParam'
- $ref: '#/components/parameters/orderByParam'
- $ref: '#/components/parameters/orderHowParam'
responses:
'200':
description: Successfully found tag count.
content:
application/json:
schema:
$ref: '#/components/schemas/TagCountOut'
'400':
description: Invalid request.
/tags:
get:
tags:
- tags
summary: Get the active host tags for a given account
operationId: api.tag.get_tags
security:
- ApiKeyAuth: []
parameters:
- $ref: '#/components/parameters/tagsParam'
- $ref: '#/components/parameters/tagsOrderBy'
- $ref: '#/components/parameters/tagsOrderHow'
- $ref: '#/components/parameters/perPageParam'
- $ref: '#/components/parameters/pageParam'
- $ref: '#/components/parameters/stalenessParam'
- $ref: '#/components/parameters/searchParam'
- $ref: '#/components/parameters/registered_with'
responses:
'200':
description: Tags
content:
application/json:
schema:
$ref: '#/components/schemas/ActiveTags'
'400':
description: Invalid request.
'404':
$ref: '#/components/responses/PageOutOfBounds'
components:
securitySchemes:
BearerAuth:
type: http
scheme: bearer
x-bearerInfoFunc: app.auth.bearer_token_handler
ApiKeyAuth:
type: apiKey
in: header
name: x-rh-identity
description: >-
Base64-encoded JSON identity header provided by 3Scale. Contains an
account number of the user issuing the request. Format of the JSON:
{"identity": {"account_number": "12345678"}}
x-apikeyInfoFunc: app.auth.authentication_header_handler
parameters:
pageParam:
name: page
in: query
required: false
schema:
type: integer
minimum: 1
default: 1
description: A page number of the items to return.
perPageParam:
name: per_page
in: query
required: false
schema:
type: integer
minimum: 1
maximum: 100
default: 50
description: A number of items to return per page.
hostIdList:
in: path
name: host_id_list
description: A comma separated list of host IDs.
required: true
schema:
type: array
items:
type: string
format: uuid
branchId:
in: query
name: branch_id
schema:
type: string
description: Filter by branch_id
required: false
factsNamespace:
in: path
name: namespace
description: A namespace of the merged facts.
required: true
schema:
type: string
orderByParam:
name: order_by
in: query
required: false
schema:
type: string
enum:
- display_name
- updated
description: Ordering field name
orderHowParam:
name: order_how
in: query
required: false
schema:
type: string
enum:
- ASC
- DESC
description: >-
Direction of the ordering, defaults to ASC for display_name and to DESC for
updated
stalenessParam:
name: staleness
in: query
required: false
schema:
type: array
items:
type: string
enum:
- fresh
- stale
- stale_warning
- unknown
default:
- fresh
- stale
- unknown
description: "Culling states of the hosts. Default: fresh,stale,unknown"
tagsParam:
name: tags
in: query
description: filters out hosts not tagged by the given tags
required: false
schema:
type: array
items:
type: string
pattern: '^([^=/]+/)?[^=/]+(=[^=/]+)?$'
tagsOrderBy:
in: query
name: order_by
required: false
schema:
type: string
default: tag
enum:
- tag
- count
description: Ordering field name
tagsOrderHow:
in: query
name: order_how
required: false
schema:
type: string
default: ASC
enum:
- ASC
- DESC
description: Direction of the ordering. Default to ASC
searchParam:
in: query
name: search
required: false
description: Only include tags that match the given search string. The value is matched against namespace, key and value.
schema:
type: string
registered_with:
in: query
name: registered_with
required: false
description: Filters out any host not registered with the specified service
schema:
type: string
enum:
- insights
fieldsParam:
in: query
name: fields
style: deepObject
allowReserved: true
description: "Include extra fields for each host. If the requested field does not exist, it will return null.
Request by passing a JSON object, or in the URL via `?fields[system_profile]=os_release, arch`. Notice requesting
extra fields will only display the requested fields plus id, account, display_name, fqdn, insights_id and subscription_manager_id.
**Currently extra fields for system_profile or host can be queried.**
See [System profile fields]('#/components/schemas/SystemProfileIn') for a list of possible fields to include for system_profile.
See [Host fields]('#/components/schemas/CreateHostOut') for a list of possible fields to include for host."
schema:
type: object
example:
system_profile: 'os_release, arch'
properties:
system_profile:
type: string
schemas:
TagsOut:
allOf:
- $ref: '#/components/schemas/Paginated'
- properties:
results:
description: The list of tags on the systems
type: object
additionalProperties:
type: array
items:
$ref: '#/components/schemas/StructuredTag'
StructuredTag:
type: object
properties:
namespace:
type: string
nullable: true
key:
type: string
value:
type: string
nullable: true
TagCountOut:
allOf:
- $ref: '#/components/schemas/Paginated'
- properties:
results:
description: The list of tags on the systems
type: object
additionalProperties:
type: integer
BulkHostOut:
type: object
properties:
total:
type: integer
description: Total number of items in the "data" list.
errors:
type: integer
description: Number of items in the "data" list that contain errors.
data:
type: array
items:
$ref: '#/components/schemas/BulkHostOutDetails'
description: 'List of hosts that were created, updated or failed to be processed.'
BulkHostOutDetails:
type: object
properties:
status:
type: integer
description: HTTP status code of the results of the host create/update process
host:
$ref: '#/components/schemas/CreateHostOut'
title:
type: string
description: Short description of why a host failed to be created or updated.
detail:
type: string
description: Details about why a host failed to be created or updated.
Facts:
title: Host facts
description: A set of string facts about a host.
type: object
example:
fact1: value1
fact2: value2
FactSet:
title: Host facts under a namespace
description: A set of string facts belonging to a single namespace.
properties:
namespace:
type: string
minLength: 1
description: A namespace the facts belong to.
facts:
type: object
description: The facts themselves.
example:
fact1: value1
fact2: value2
required:
- namespace
- facts
CreateHostIn:
title: Host data
description: >-
Data of a single host belonging to an account. Represents the hosts
without its Inventory metadata.
type: object
required:
- account
- stale_timestamp
- reporter
properties:
display_name:
description: >-
A host’s human-readable display name, e.g. in a form of a domain
name.
type: string
example: host1.mydomain.com
ansible_host:
description: >-
The ansible host name for remediations
type: string
example: host1.mydomain.com
account:
description: A Red Hat Account number that owns the host.
type: string
example: '000102'
insights_id:
description: >-
An ID defined in /etc/insights-client/machine-id. This field is
considered a canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
rhel_machine_id:
description: >-
A Machine ID of a RHEL host. This field is considered to be a
canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
subscription_manager_id:
description: >-
A Red Hat Subcription Manager ID of a RHEL host. This field is
considered to be a canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
satellite_id:
description: >-
A Red Hat Satellite ID of a RHEL host. This field is considered to
be a canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
bios_uuid:
description: >-
A UUID of the host machine BIOS. This field is considered to be a
canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
ip_addresses:
description: >-
Host’s network IP addresses. This field is considered to be a
canonical fact.
type: array
items:
type: string
example:
- 10.10.0.1
- 10.0.0.2
fqdn:
description: >-
A host’s Fully Qualified Domain Name. This field is considered to
be a canonical fact.
type: string
example: my.host.example.com
mac_addresses:
description: >-
Host’s network interfaces MAC addresses. This field is considered
to be a canonical fact.
type: array
items:
type: string
example:
- 'c2:00:d0:c8:61:01'
external_id:
description: >-
Host’s reference in the external source e.g. AWS EC2, Azure,
OpenStack, etc. This field is considered to be a canonical fact.
type: string
example: i-05d2313e6b9a42b16
facts:
description: A set of facts belonging to the host.
type: array
items:
$ref: '#/components/schemas/FactSet'
system_profile:
$ref: '#/components/schemas/SystemProfileIn'
stale_timestamp:
description: Timestamp from which the host is considered stale.
type: string
format: date-time
reporter:
description: Reporting source of the host. Used when updating the stale_timestamp.
type: string
CreateHostOut:
title: Create Host Out
allOf:
- $ref: '#/components/schemas/BaseHostOut'
- required:
- account
BaseHostOut:
title: Base Host Out
description: >-
Data of a single host belonging to an account. Represents the hosts
without its Inventory metadata.
type: object
properties:
display_name:
description: >-
A host’s human-readable display name, e.g. in a form of a domain
name.
type: string
example: host1.mydomain.com
nullable: true
ansible_host:
description: >-
The ansible host name for remediations
type: string
example: host1.mydomain.com
nullable: true
account:
description: A Red Hat Account number that owns the host.
type: string
example: '000102'
insights_id:
description: >-
An ID defined in /etc/insights-client/machine-id. This field is
considered a canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
nullable: true
rhel_machine_id:
description: >-
A Machine ID of a RHEL host. This field is considered to be a
canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
nullable: true
subscription_manager_id:
description: >-
A Red Hat Subcription Manager ID of a RHEL host. This field is
considered to be a canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
nullable: true
satellite_id:
description: >-
A Red Hat Satellite ID of a RHEL host. This field is considered to
be a canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
nullable: true
bios_uuid:
description: >-
A UUID of the host machine BIOS. This field is considered to be a
canonical fact.
type: string
example: 3f01b55457674041b75e41829bcee1dc
nullable: true
ip_addresses:
description: >-
Host’s network IP addresses. This field is considered to be a
canonical fact.
type: array
items:
type: string
nullable: true
example:
- 10.10.0.1
- 10.0.0.2
fqdn:
description: >-
A host’s Fully Qualified Domain Name. This field is considered to
be a canonical fact.
type: string
example: my.host.example.com
nullable: true
mac_addresses:
description: >-
Host’s network interfaces MAC addresses. This field is considered
to be a canonical fact.
type: array
items:
type: string
nullable: true
example:
- 'c2:00:d0:c8:61:01'
external_id:
description: >-
Host’s reference in the external source e.g. AWS EC2, Azure,
OpenStack, etc. This field is considered to be a canonical fact.
type: string
nullable: true
example: i-05d2313e6b9a42b16
id:
description: >-
A durable and reliable platform-wide host identifier. Applications
should use this identifier to reference hosts.
type: string
example: 3f01b55457674041b75e41829bcee1dc
created:
description: A timestamp when the entry was created.
type: string
format: date-time
updated:
description: A timestamp when the entry was last updated.
type: string
format: date-time
facts:
description: A set of facts belonging to the host.
type: array
items:
$ref: '#/components/schemas/FactSet'
stale_timestamp:
description: Timestamp from which the host is considered stale.
type: string
format: date-time
nullable: true
stale_warning_timestamp:
description: >-
Timestamp from which the host is considered too stale to be listed without an explicit
toggle.
type: string
format: date-time
nullable: true
culled_timestamp:
description: Timestamp from which the host is considered deleted.
type: string
format: date-time
nullable: true
reporter:
description: Reporting source of the host. Used when updating the stale_timestamp.
type: string
nullable: true
HostQueryOutput:
title: A Host Inventory query result
description: >-
A paginated host search query result with host entries and their
Inventory metadata.
allOf:
- $ref: '#/components/schemas/Paginated'
- properties:
results:
oneOf:
- description: Actual host search query result entries.
type: array
items:
$ref: '#/components/schemas/BaseHostOut'
- description: Actual host search query result entries.
type: array
items:
$ref: '#/components/schemas/CreateHostOut'
SystemProfileByHostOut:
title: A host system profile query result
description: Structure of the output of the host system profile query
allOf:
- $ref: '#/components/schemas/Paginated'
- properties:
results:
description: Actual host search query result entries.
type: array
items:
$ref: '#/components/schemas/HostSystemProfileOut'
HostSystemProfileOut:
title: Structure of an individual host system profile output
description: Individual host record that contains only the host id and system profile
properties:
id:
type: string
system_profile:
$ref: '#/components/schemas/SystemProfileIn'
DiskDevice:
title: Disk Device
description: Representation of one mounted device
properties:
device:
example: "/dev/fdd0"
type: string
label:
description: user-defined mount label
type: string
options:
description: mount options
example:
uid: "0"
ro: true
type: object
properties:
name:
type: string
value:
type: string
mount_point:
description: mount point
example: "/mnt/remote_nfs_shares"
type: string
type:
description: mount type
example: "ext3"
type: string
YumRepo:
title: Yum Repository
description: Representation of one yum repository
properties:
id:
type: string
name:
type: string
gpgcheck:
type: boolean
enabled:
type: boolean
baseurl:
type: string
format: uri
DnfModule:
title: DNF Module
description: Representation of one DNF module
properties:
name:
type: string
stream:
type: string
InstalledProduct:
title: Installed Product
description: Representation of one installed product
properties:
name:
type: string
id:
description: the product ID
example: "71"
type: string
status:
description: subscription status for product
example: "Subscribed"
type: string
NetworkInterface:
title: Network Interface
description: Representation of one network interface
properties:
ipv4_addresses:
type: array
items:
type: string
format: ipv4
ipv6_addresses:
type: array
items:
type: string
format: ipv6
mtu:
description: MTU
type: integer
mac_address:
description: MAC address (with or without colons)
example: "00:00:00:00:00:00"
type: string
name:
description: name of interface
type: string
example: eth0
state:
description: interface state (UP, DOWN, UNKNOWN)
type: string
example: "UP"
type:
description: interface type (ether, loopback)
type: string
example: "ether"
SystemProfileIn:
title: System profile fields
description: Representation of the system profile fields
type: object
properties:
number_of_cpus:
type: integer
number_of_sockets:
type: integer
cores_per_socket:
type: integer
system_memory_bytes:
type: integer
format: int64
infrastructure_type:
type: string
infrastructure_vendor:
type: string
network_interfaces:
type: array # techincally a set, ordering is not important
items:
$ref: '#/components/schemas/NetworkInterface'
disk_devices:
type: array # techincally a set, ordering is not important
items:
$ref: '#/components/schemas/DiskDevice'
bios_vendor:
type: string
bios_version:
type: string
bios_release_date:
type: string
cpu_flags:
items:
type: string
type: array
os_release:
type: string
os_kernel_version:
type: string
arch:
type: string
kernel_modules:
type: array
items:
type: string
last_boot_time:
type: string
format: date-time
running_processes:
type: array # techincally a set, ordering is not important
items:
description: a single running process. This will be truncated to 1000 characters when saved.
type: string
subscription_status:
type: string
subscription_auto_attach:
type: string
katello_agent_running:
type: boolean
satellite_managed:
type: boolean
cloud_provider:
type: string
yum_repos:
type: array # technically a set, ordering is not important
items:
$ref: '#/components/schemas/YumRepo'
dnf_modules:
type: array # technically a set, ordering is not important
items:
$ref: '#/components/schemas/DnfModule'
installed_products:
type: array # technically a set, ordering is not important
items:
$ref: '#/components/schemas/InstalledProduct'
insights_client_version:
type: string
insights_egg_version:
type: string
captured_date:
type: string
installed_packages:
type: array # technically a set, ordering is not important
items:
description: a NEVRA string for a single installed package
type: string
example: "krb5-libs-0:-1.16.1-23.fc29.i686"
installed_services:
type: array
items:
type: string
enabled_services:
type: array
items:
type: string
PatchHostIn:
title: Host data
description: >-
Data of a single host to be updated.
type: object
properties:
ansible_host:
description: >-
The ansible host name for remediations
type: string
example: host1.mydomain.com
display_name:
description: >-
A host’s human-readable display name, e.g. in a form of a domain
name.
type: string
example: host1.mydomain.com
ActiveTags:
title: Host data
allOf:
- $ref: '#/components/schemas/Paginated'
- properties:
results:
type: array
items:
title: ActiveTag
description: Information about a host tag
type: object
required:
- tag
- count
properties:
tag:
$ref: '#/components/schemas/StructuredTag'
count:
description: The number of hosts with the given tag. If the value is null this indicates that the count is unknown.
type: integer
nullable: true
example:
total: 3
count: 3
page: 1
per_page: 50
results:
- tag:
namespace: Sat
key: env
value: prod
count: 3
- tag:
namespace: aws
key: region
value: us-east-1
count: 1
- tag:
namespace: insights-client
key: web
value: null
count: -1
Paginated:
type: object
required:
- total
- count
- page
- per_page
- results
properties:
total:
$ref: '#/components/schemas/Total'
count:
$ref: '#/components/schemas/Count'
page:
$ref: '#/components/schemas/Page'
per_page:
$ref: '#/components/schemas/PerPage'
results:
type: array
example:
total: 3
count: 3
page: 1
per_page: 50
results: []
Total:
type: integer
description: Total number of items
Count:
type: integer
description: The number of items on the current page
Page:
type: integer
description: The page number
PerPage:
type: integer
description: The number of items to return per page
responses:
PageOutOfBounds:
description: Requested page is outside of the range of available pages
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment