This is an example of our internal style guide when authoring new objects; while it may not be applicable, use this as an example and basis to build your own! If you didn’t get a chance to look at the object modeling best practices, I would recommend looking HERE.
Use snake_case
Attribute and type names must use snakecase instead of camel case or others. The ‘’ should be used at word boundaries replacing the space.
Do
my_cool_var
my_cool_type
Don’t
myCoolVar
mycoolvar
Use lowercase for object and attribute names
Object and attribute names must all be lowercase. Descriptions can contain capitalization.
Do
my_cool_var
my_cool_type
Don’t
myCoolVar
My_cool_var
My_Cool_Var
Postfix some types
For ID and Date/Timestamp types postfix the attribute name with the type.
Do
my_id # id type
some_date # date type
Don’t
my # id type
some # date type
foo_someid # id type
NOTE: the _id should be lowercase in the attribute name but uppercase (ID) when referenced in the description.
Use required fields correctly
A required field must be specified on each request or generated by the system on create (is_system: true). For non-system fields, this means the caller must specify the field in every request.
If a field is necessary for a majority of objects, but not all, do not mark as required.
For example, if we require all objects have dev_oid, id, and some may have attr foo set:
Do
dev_oid
...
is_required: true
...
id
...
is_required: true
...
foo
... # NOTE: is_required defaults to false
Don’t
dev_oid
...
is_required: true
...
id
...
is_required: true
...
foo
...
is_required: true # NOTE: is_required incorrectly set to true
...
NOTE: avoid accepting null as a valid value. If something may be null, the values should be re-considered.
Inherit from atom or a type that does
All types in the system must inherit from atom directly or indirectly (they inherit from a type who’s parent (directly or indirectly) inherits from atom). This ensures that every object in the system has the bare minimum required fields for things like multi-tenancy, identification, etc.
Do
- name: work
parent: urn:devrev:objects:atom
fields:
...
- name: issue
parent: urn:devrev:objects:work
fields:
...
Don’t
- name: not_work
# NOTE: parent omitted
fields:
...
- name: not_work_child
parent: urn:devrev:objects:not_work
...
Set the devrev_field_type
The devrev_field_type
must be set; please view the schema JSON to see a valid list of field types.
Set the devrev_id_type if the devrev_field_type is ID
When the devrev_field_type
is set to id, the devrev_id_type
must be specified to tell which type of id is expected. This is important to ensure only the correct types of IDs are stored.
Do
- name: atom
fields:
- name: created_by_id
devrev_field_type: id
devrev_id_type:
- user
---
- name: dev_oid
devrev_field_type: id
devrev_id_type:
- dev_org
Don’t
- name: not_atom
fields:
- name: my_id
devrev_field_type: id
# NOTE: devrev_id_type omitted
Must use a DON (not display ID) for any ID values
In the previous item, we dictate that you must set the devrev_id_type
when the devrev_field_type
is set to id. In coordination, the values of these MUST be in the DON format (don:...
) and not the display ID format (TKT-111
). This is imperative as the display ID doesn’t contain details about the dev oid as well as other useful information the DON provides (e.g., the region which will be critical in multi-region scenarios.)
Do
{
"dev_oid": "don:...:dev_org/1"
...
}
Don’t
{
"dev_oid": "DEVO-1"
...
}
NOTE: When referencing an ID in the description, ID must be capitalized (not id or Id)
Set is_system to true for any system-generated fields
For any system set field, ensure that is_system: true.
Do
- name: foo
fields:
- name: created_by_id
devrev_field_type: id
devrev_id_type:
- user
is_required: true
is_system: true
...
- name: bar
devrev_field_type: tokens
is_system: true
# NOTE: is_required isn't always necessary for system fields
Don’t
- name: foo
fields:
- name: system_set_bar
devrev_field_type: tokens
# NOTE: is_system is not set to true
# NOTE: value is actually set by the system
...
Provide a short, but full description
Descriptions should be succinct but also demonstrate the purpose of the attribute
Sort enum values logically and preference overridable_enum
When defining ENUMs, define the values logically (may be alphabetical in some instances and may not). Legacy_enums cannot be changed, so use them sparingly as they cannot be customized.
Do
- name: status
fields:
# NOTE: status would be a combination of the state and stage. For example
# active: pr approval pending, etc.
- name: state
devrev_field_type: overridable_enum # NOTE: overidable_enum used instead of legacy_enum
devrev_enum: # NOTE: order is not alphabetical
- open
- in-progress
- closed
Don’t
- name: status
fields:
# NOTE: status would be a combination of the state and stage. For example
# active: pr approval pending, etc.
- name: state
devrev_field_type: legacy_enum # NOTE: legacy_enum used instead of overridable_enum
devrev_enum: # NOTE: order is not alphabetical or logical
- open
- closed
- in-progress
NOTE: there may be some cases for using
legacy_enum
, however, this should be an uncommon case.
Set gateway summary
field to mark items that should be in summary resolution
Gateway will commonly resolve IDs into their summary objects before returning the response. This process takes an ID and fetches details about the referenced ID. This is necessary as in most cases the ID isn’t too useful and the details about the aforementioned ID may be more useful.
For example, in work was have the applies_to_part_id
attribute which references the part ID the work is associated with. However, the ID of the part isn’t too helpful as I want to know details about the part (e.g., name, owner, description, etc.).
In general, the attributes included in the summary should be the minimal set of attributes required to render a view without the need for subsequent requests.
Do
Set important fields to summary: true
- name: title
devrev_field_type: text
is_required: true
description: Title of the work object
gateway:
api_visibility: public
api_required: true
summary: true # NOTE: `title` included in summary
---
- name: severity
devrev_field_type: legacy_enum
devrev_enum:
- blocker
- high
- medium
- low
is_required: true
description: Severity of the ticket
gateway:
api_visibility: public
is_filterable: true
summary: true # NOTE: `severity` included in summary
Don’t
Don’t set all fields to summary: true
- name: field1
...
gateway:
api_visibility: public
is_filterable: true
summary: true # NOTE: all fields marked as `summary: true`
...
- name: field2
...
gateway:
api_visibility: public
is_filterable: true
summary: true # NOTE: all fields marked as `summary: true`
...
- name: field3
...
gateway:
api_visibility: public
is_filterable: true
summary: true # NOTE: all fields marked as `summary: true`
Override gateway id_resolution
only when necessary
The id_resolution
selector sets the resolution gateway can do for IDs. Valid values are none
,summary
(default),full
.
- Use
none
when you don’t any additional details about the referenced ID (just the ID will be sent in the response) - Use
summary
when summary details will do (defined via thesummary
gateway field). This is the default and should be good in most cases. - Use
full
only when you need the full referenced object’s details (NOTE: use sparingly as this may bloat the response size)
Do
...
- name: event_activity
fields:
- name: source_id
devrev_field_type: id
devrev_id_type:
- change_event
description: The ID correspending to the event
is_required: true
gateway:
description: Timeline's event entry
id_resolution: full # NOTE: id_resolution correctly overridden to full as the full event details are necessary
name: change_event
...
Do
- name: link_id
devrev_field_type: id
devrev_id_type:
- link
is_required: true
is_system: true
gateway:
id_resolution: none # NOTE: id_resolution correctly overridden to none as the link_id details are not necessary
Don’t
---
- name: applies_to_part_id
devrev_field_type: id
devrev_id_type:
- part
is_required: true
description: Details of the part relevant to the work
gateway:
is_filterable: true
id_resolution: full # NOTE: this would cause a signifigant of bloat on this object's response and full should not be used in this case given the size of the part object.
name: applies_to_part
Set gateway name
for any postfixed attribute names where necessary
Do
- name: marketplace_id
devrev_field_type: id
devrev_id_type:
- marketplace
description: The marketplace the item belongs to
gateway:
name: marketplace # NOTE: `marketplace_id` correctly overridden to `marketplace`
Don’t
- name: marketplace_id
devrev_field_type: id
devrev_id_type:
- marketplace
description: The marketplace the item belongs to
gateway:
description: The marketplace the item belongs to # NOTE: redundant, only override when necessary
# NOTE: name not overridden, will appear as `marketplace_id`
Set gateway api_visibility
Sets the api visibility for the field. Valid values are private
, public
, hidden
.
Do
- Use
private
when things should only be internally available via the internal API endpoints - Use
hidden
when things should be omitted from API generation and does not create an API field for the attribute. Use for deprecated fields - Use
public
when the attribute should be visible in the public API
Don’t
- Don’t use
private
for things that should be public and are required by the app/client - Don’t use
public
for fields that should only be accessible internally
When to use composite types and mixins
Use mixins when:
- need a easily reusable common set of attributes that may be used by various types (e.g., MFZ mixin, links mixin, etc.)
Use composite types when:
- need an attribute to contain a nested structure instead of a simple value
NOTE: When you use a mixin the attributes of the mixin are “mixed in” to the objects attributes and do not need to be defined. when a composite type is added to a type the attribute needs to be added with a reference to the composite type.
How to define a composite type
devrev_composite_field_types:
- name: my_composite_type
fields:
- name: key
devrev_field_type: tokens
is_required: true
description: Key used to fetch the object value
ui:
display_name: Key
- name: vals
devrev_field_type: "[]tokens"
is_required: true
description: Values valid for the key
ui:
display_name: Value(s)
description: My composite type
How to reference a composite type
- name: my_type
parent: urn:devrev:objects:atom
fields:
- name: my_cool_composite_type # NOTE: name doesn't need to math the composite type name
devrev_field_type: "[]composite"
is_required: true
description: Example addition of a composite type
ui:
display_name: My composite type
devrev_composite_type: my_composite_type
How to define a mixin
NOTE: define mixins in separate files.
mixin_types:
---
- name: my_status_mixin
fields:
# NOTE: status would be a combination of the state and stage. For example
# active: pr approval pending, etc.
- name: state
devrev_field_type: overridable_enum
devrev_enum:
- open
- in-progress
- closed
is_required: true
is_system: true
description: State of the object based upon the stage
gateway:
is_filterable: true
ui:
display_name: State
is_hidden: true
- name: stage
devrev_field_type: composite
description: Stage of the object
is_required: true
gateway:
api_visibility: public
is_filterable: true
ui:
display_name: Stage
is_bulk_action_enabled: true
is_shown_in_summary: true
is_hidden_during_create: true
devrev_composite_type: stage
description: Mixin for status attributes
How to use a mixin
object_schemas:
- name: work
parent: urn:devrev:objects:atom
mixins:
- name: status
fields_prefix: ""
...
- name: another_example
parent: urn:devrev:objects:atom
mixins:
- name: status
fields_prefix: ""
Example:
{
"bar":1,
"bas":"aaa",
"boo": # NOTE: example of a top level composite type, may be via a mixin
{
"x":1,
"y":3,
"z": # NOTE: example of a nested composite type, within the "boo" composite type
[ # NOTE: nested types can be a known type, structure or [] of structures
{
"a":1,
"b":2
},
{
"a":2,
"b":3
},
]
}
}
REMINDER: composite types may be nested.
For example, in MFZ a group
has an attribute for conditional_roles
which is a composite type that includes an array of conditions
which is another composite type. The only way to model this nesting is with composite types.
Do
- name: group
parent: urn:devrev:objects:atom
fields:
- name: conditional_roles
devrev_field_type: "[]composite"
is_required: true
description: Conditional roles for the group
ui:
display_name: Conditional roles
devrev_composite_type: conditional_role
---
devrev_composite_field_types:
- name: conditional_role
fields:
- name: conditions
devrev_field_type: "[]composite"
is_required: true
description: Conditions definition which must be met for role
ui:
display_name: Conditions
devrev_composite_type: condition
---
- name: condition
fields:
- name: key
devrev_field_type: tokens
is_required: true
description: Key used to fetch the object value
ui:
display_name: Key
- name: vals
devrev_field_type: "[]tokens"
is_required: true
description: Values valid for the key
ui:
display_name: Value(s)
- name: operator
devrev_field_type: legacy_enum
devrev_enum:
- eq
- in
- not_eq
- not_in
description: The operator used to compare the object vals
ui:
display_name: Operator
description: Condition
Don’t
# NOTE: It isn't possible to structure nested types without composite types
Do
Use a mixin to add a common set of attributes across object types. If the field_prefix
is set to ""
the attributes will be added as defined in the mixin. If field_prefix
is set to something other than ""
, for example, "foo_"
the mixins attributes will be added as foo_[attr_name]
.
NOTE: to keep consistent with the snake*case requirement, the field_prefix must end with a
*
to give the structureprefix\_[attr_name]
object_schemas:
- name: work
parent: urn:devrev:objects:atom
mixins:
- name: link
fields_prefix: ""
- name: reaction
fields_prefix: ""
- name: status
fields_prefix: ""
...
- name: another_example
parent: urn:devrev:objects:atom
mixins:
- name: status
fields_prefix: ""
mixin_types:
...
- name: status
fields:
# NOTE: status would be a combination of the state and stage. For example
# active: pr approval pending, etc.
- name: state
devrev_field_type: overridable_enum
devrev_enum:
- open
- in-progress
- closed
is_required: true
is_system: true
description: State of the object based upon the stage
gateway:
is_filterable: true
ui:
display_name: State
is_hidden: true
- name: stage
devrev_field_type: composite
description: Stage of the object
is_required: true
gateway:
api_visibility: public
is_filterable: true
ui:
display_name: Stage
is_bulk_action_enabled: true
is_shown_in_summary: true
is_hidden_during_create: true
devrev_composite_type: stage
description: Mixin for status attributes
Don’t
Duplicate a common set of attributes by defining in each type. Notice how the state and stage attributes are defined in each type instead of a) using a composite type and b) using a mixin.
object_schemas:
- name: work
parent: urn:devrev:objects:atom
fields:
# NOTE: status would be a combination of the state and stage. For example
# active: pr approval pending, etc.
- name: state
devrev_field_type: overridable_enum
devrev_enum:
- open
- in-progress
- closed
is_required: true
is_system: true
description: State of the object based upon the stage
gateway:
is_filterable: true
ui:
display_name: State
is_hidden: true
- name: stage
devrev_field_type: composite
description: Stage of the object
is_required: true
gateway:
api_visibility: public
is_filterable: true
ui:
display_name: Stage
is_bulk_action_enabled: true
is_shown_in_summary: true
is_hidden_during_create: true
devrev_composite_type: stage
...
- name: another_example
parent: urn:devrev:objects:atom
fields:
# NOTE: status would be a combination of the state and stage. For example
# active: pr approval pending, etc.
- name: state
devrev_field_type: overridable_enum
devrev_enum:
- open
- in-progress
- closed
is_required: true
is_system: true
description: State of the object based upon the stage
gateway:
is_filterable: true
ui:
display_name: State
is_hidden: true
- name: stage
devrev_field_type: composite
description: Stage of the object
is_required: true
gateway:
api_visibility: public
is_filterable: true
ui:
display_name: Stage
is_bulk_action_enabled: true
is_shown_in_summary: true
is_hidden_during_create: true
devrev_composite_type: stage
...