YAML by example - yaml

I am trying to design the configuration file format for my app and have chosen YAML. However, this (obviously) means I need to be able to define, parse and validate proper YAML syntax!
In the config file, there must be a collection/sequence called widgets. This sequence needs to contain a set of typed widgets (possible types are fizz, buzz and foobaz). Each widget also has a name and various other properties (depending on the type). For example:
myappconfig.yaml
================
widgets:
- buzz:
name: Red Buzz
isSilly: true
- buzz:
name: Blue Buzz
isSilly: false
- foobaz:
name: Abracadabra
rating: 3000
specialty: Such YAML much amaze
My simple question is: Have I created a proper/valid YAML file above? Meaning, based on my constraints, am I understanding YAML syntax correctly in my implementation?

You can check the syntax of your YAML, e.g. on this website.
Your YAML is parsed as this:
{'widgets': [{'buzz': {'name': 'Red Buzz', 'isSilly': True}}, {'buzz': {'name': 'Blue Buzz', 'isSilly': False}}, {'foobaz': {'rating': 3000, 'name': 'Abracadabra', 'specialty': 'Such YAML much amaze'}}]}
which looks like what you seem to be after.

If the widget name should be unique, you can use it for keys:
widgets:
RedBuzz:
type: buzz
isSilly: true
BlueBuzz:
type: buzz
isSilly: false
Abracadabra:
type: foobaz
rating: 3000
specialty: Such YAML much amaze
This one uses grouping by widget type:
widgets:
buzz:
- RedBuzz:
isSilly: true
- Blue Buzz:
isSilly: false
foobaz:
- Abracadabra:
rating: 3000
specialty: Such YAML much amaze
Also if you're feeling nerdy you can use widgets types merge like this:
buzz_widget: &buzz_widget
type: buzz
foobaz_widget: &foobaz_widget
type: foobaz
widgets:
RedBuzz:
isSilly: true
<<: *buzz_widget
BlueBuzz:
isSilly: false
<<: *buzz_widget
Abracadabra:
rating: 3000
specialty: Such YAML much amaze
<<: *foobaz_widget
As for me, using nested key-values structure (for non-primitive elements) is preferable. Being parsed into a map, you can always access it's values as a collection. At the same time you have unique constraint and constant access time (by key). If your keys should not be unique, then you should try restructuring it before giving back to sequence.

Related

Open API Spec V2.0 - Default value of a field of type Enum

I have a request body for an API specification in Swagger V2.0, which looks like follows.
"/uri/path":
...
parameters:
- in: body
...
schema:
$ref: '#/definitions/StatusObject'
definitions:
StatusObject:
status:
$ref: '#/definitions/StatusEnum'
StatusEnum:
type: string
enum: ['ALPHA', 'BRAVO', 'UNKNOWN']
Now, I want StatusObject.status to have the value UNKNOWN by default, if it is not set from the client end. I tried to achieve this as follows, with no luck.
"/uri/path":
...
parameters:
- in: body
...
schema:
$ref: '#/definitions/StatusObject'
definitions:
StatusObject:
status:
$ref: '#/definitions/StatusEnum'
default: 'UNKNOWN'
StatusEnum:
type: string
enum: ['ALPHA', 'BRAVO', 'UNKNOWN']
I also have tried with '#/definitions/StatusEnum.UNKNOWN' which again didn't work. Combed through the documentation as well but couldn't find anything further. What am I missing?
Response to marked duplicate
What I am trying to achieve is to set a default value for this property status. This works when the enum is defined in line as follows.
"/uri/path":
...
parameters:
- in: body
...
schema:
$ref: '#/definitions/StatusObject'
definitions:
StatusObject:
status:
type: string
enum:
- 'ALPHA'
- 'BRAVO'
- 'UNKNOWN'
default: 'UNKNOWN'
But, this won't work for me, as I'd like to reuse the enum, which otherwise I'll have to repeat at multiple places.
Since this is just a workaround and I am not sure if I can confirm if this is an answer, I won't mark this as accepted answer. That way, I think it will be still open for someone who figured out the right way, or a better way to achieve the expectation.
Apparently, the problem is with $ref. It's already known that the siblings of $ref are ignored in OpenAPI V2.0. So, enforcing any further constraints once you use $ref won't be possible.
For my specific use case, since I want to reuse my enum definition, I used YAML Anchors as defined in V2.0 docs. Even though the enum definition is repeated in each POJO it's not that much of a headache to manage, at least for the time being. The implementation I came up is as follows.
"/uri/path":
...
parameters:
- in: body
...
schema:
$ref: '#/definitions/StatusObject'
definitions:
StatusObject:
status:
enum: *STATUS_ENUM # Referencing the anchor
default: 'UNKNOWN'
StatusEnum:
type: string
enum: &STATUS_ENUM # This is the anchor
- 'ALPHA'
- 'BRAVO'
- 'UNKNOWN'
It must also be noted that, the enum values in this case cannot be defined using array syntax (i.e. ['ALPHA', 'BRAVO', 'UNKNOWN']) as it'll break the YAML syntax rules when you try to use YAML anchors alongside that.

Reusing a subset of an enum in OpenAPI (Swagger)

I'm trying to achieve an OpenAPI definition where I define a shared, complete list of allowed values as an enum and then use subgroups of the values in different places to show which values are allowed in each case.
Using the example from the enum spec on Swagger.io, if I have a definition like this:
paths:
/products:
get:
parameters:
- in: query
name: color
required: true
schema:
$ref: '#/components/schemas/Color'
responses:
'200':
description: OK
components:
schemas:
Color:
type: string
enum:
- black
- white
- red
- green
- blue
then is it possible to define e.g. two different paths that take a color as a parameter, but one of them only accepts black or white whereas the other accepts all colors?
There's no good way to reuse a part of an enum. The best way is to define separate enums.
A possible workaround is to use oneOf to "combine" partial enums into the full enum as suggested here. However, oneOf enum schemas probably won't work as enums in Swagger UI and code generators.
components:
schemas:
BlackOrWhite:
type: string
enum:
- black
- white
Color:
oneOf:
- $ref: '#/components/schemas/BlackOrWhite'
- type: string
enum:
- red
- green
- blue
Tricks with YAML &anchors and merge keys << will NOT work because YAML only supports merge keys in mappings (objects) but not in sequences (arrays).
# This will NOT work in YAML
components:
schemas:
BlackOrWhite:
type: string
enum: &BLACK_WHITE
- black
- white
Color:
type: string
enum:
<< : *BLACK_WHITE
- red
- green
- blue

raml2html not generating arrays declarations properly

I am using the raml2html tool to generate html documentation for my ReST apis.
I have defined all my types in a raml file memberTypes.raml that I include in the main service.raml.
Following is a sample from the service.raml file.
#%RAML 1.0
title: update member object
uses:
memberTypes: /memberTypes.raml
types:
Member:
properties:
termsAndConditions:
type: memberTypes.TermsAndConditions[]
description: The terms and conditions that the member has accepted.
required: false
person:
type: memberTypes.Person
description: The personal details of the member.
required: true
And following is a sample for the 2 properties above in memberTypes.raml
Person:
properties:
personName:
type: NameType
description: The name of the member.
required: true
dateOfBirth:
type: DateOfBirth
required: true
TermsAndConditions:
properties:
version:
type: string
description: The version of the terms and conditions.
acceptedDate:
type: date-only
description: The date on which the corresponding terms and conditions were accepted. Format is YYYY-MM-DD, ISO-8601 Calendar date format.
Now, below is the what I get in the html
The array is shown as array of memberTypes.TermsAndConditions. The questions is how do i get rid of the memberTypes? i simply want the type to be array of TermsAndConditions.
How do I achieve this? is there a command line option to raml2html tool that will do the trick?
I don't think there is command line option to do that, since the template / theme of raml2html defines how things are shown in HTML. You can edit the default theme. If I recall correctly that is defined in item.nunjucks file.
The usual use case for that is "array of objects" or "array of strings" but there is also definitions how to show non standard type types.

Front matter defaults for collection's documents in subfolders

I would like to organize collection's documents in subfolders and assign them different categories through front matter defaults. I have the following structure:
_kb
- category1
- article1.md
- category2
- article2.md
In my _config.yml file I have:
# Collections
collections:
kb:
output: true
permalink: /kb/:name/
# Defaults
defaults:
-
scope:
path: "_kb/category1/"
type: "kb"
values:
category: "category1"
-
scope:
path: "_kb/category2/"
type: "kb"
values:
category: "category2"
But this doesn't work. Any idea how I can assign different categories to documents in different subfolders through front-matter defaults?
I don't think what you are looking for is possible.
Reading the documentation, it should be possible. But I have done some experimentation with Jekyll 2.4 and the "path" appears to be the result of the permalink conversion.
With your example setup I added this as a set of defaults
-
scope:
path: "kb/article1/"
type: "kb"
values:
category: "category6"
The generated file got "category6" assigned to it.

YAML: Store array as variable

I'm just starting to learn YAML, and I'm not really finding a best practice for something that I'm trying to accomplish. Basically, I have an array of objects in my YAML file, and for production, I'd like to add 1 more entry to this array. So I basically want something like this (it's pseudo code because I know it's not valid YAML):
development:
array: &ARRAY
- name: item1
value: value1
- name: item2
value: value2
production:
<<: *ARRAY
array:
- name: item3
value: value3
Currently, I'm parsing my YAML files with Ruby, so I decided to handle this logic in Ruby. I'm doing something like this:
yaml_contents = YAML::load(yaml_string)
prod_values = yaml_contents['production']
prod_values['array'].push({:name => 'item3', :value => 'value3'})
However, that can make my loading script very hairy. Is there a better way of designing this?
I believe this question is related.
The << syntax is for merging maps (i.e. Hashes), not sequences. You could do something like this:
development: &ARRAY
- name: item1
value: value1
- name: item2
value: value2
production:
- *ARRAY
- name: item3
value: value3
When you load this the production array will have a nested array, so you would need to use flatten:
yaml_contents = YAML::load(yaml_string)
prod_values = yaml_contents['production'].flatten
If your actual data could involve nested hashes and you only want to flatten any arrays that appear as aliases in the Yaml you could write your own Psych visitor (probably a sub class of Psych::Visitors::ToRuby) and merge them in as you create the object graph, but I suspect simply calling flatten will be enough in this case.

Resources