Object Customization

This functionality is in Beta. It is not recommended for use in production applications.

DevRev allows you to customize its core objects such as issue and ticket to fit your organization’s unique workflows and reporting needs. By using the customization framework, you can extend these objects with custom fields that reflect your processes.

This guide provides an overview of the customization framework and walks you through the process of tracking bugs in your organization. By the end of this guide, you’ll be able to:

  1. Customize DevRev objects such as issue and ticket.
  2. Create custom stages and stage transition diagrams for your objects.
  3. Create dependent fields for your objects.

Concepts

Schema fragment

DevRev objects are customized using schema fragments. A fragment is a building block that defines a specific set of custom fields. When creating or updating an object record, multiple schema fragments can be combined to determine the full set of custom fields available for that record. The term fragment is used because each schema fragment contributes a portion of the overall object schema.

Tenant custom field

Tenant custom fields allow extending the DevRev objects by adding new fields. These custom fields are applied to all records of the specified object type within the organization. For example, a release notes tenant custom field for issue is applicable to all issue records in the organization.

Tenant custom fields are defined in a schema fragment of type tenant_fragment.

Subtype

Subtypes are kinds of DevRev object types. They inherit all fields from the parent type and can include additional specific fields. For example, a bug subtype of issue would have all issue fields plus bug-specific fields like RCA.

Subtypes are defined using a schema fragment of type custom_type_fragment.

Customizing a DevRev object

Let’s say you want to track bugs across various environments in your organization.

First, create a schema fragment defining the fields for the bug subtype. Make sure to replace <TOKEN> with your API token.

1curl --location 'https://api.devrev.ai/schemas.custom.set' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "type": "custom_type_fragment",
7 "description": "Attributes for tracking a bug",
8 "leaf_type": "issue",
9 "subtype": "bug",
10 "subtype_display_name": "Bug",
11 "fields": [
12 {
13 "name": "regression",
14 "field_type": "bool",
15 "ui": {
16 "display_name": "Regression",
17 }
18 },
19 {
20 "name": "impacted_environments",
21 "field_type": "array",
22 "base_type": "enum",
23 "allowed_values": [ "Dev", "QA", "Prod" ],
24 "is_filterable": true,
25 "ui": {
26 "display_name": "Impacted Environments",
27 }
28 },
29 {
30 "name": "rca",
31 "field_type": "rich_text",
32 "ui": {
33 "display_name": "RCA",
34 }
35 },
36 ]
37}'

Let’s say a bug has been identified in the Prod environment. The person who reported the anomaly creates a bug-flavored issue object to track it and assigns a relevant owner.

1curl --location 'https://api.devrev.ai/works.create' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "type": "issue",
7 "title": "API failure in Prod",
8 "owned_by": "<OWNER_ID>",
9
10 ... // other required fields
11
12 "custom_schema_spec": {
13 "subtype": "bug"
14 },
15 "custom_fields": {
16 "ctype__regression": true,
17 "ctype__impacted_environments": [ "Prod" ]
18 }
19}'

After resolving the bug, the developer can update the issue object with release notes. Adding release notes provides a clear record of what was deployed to resolve the bug which can be valuable for future reference and communication with stakeholders.

To add release notes for the completed work, you can create a tenant custom field for the issue.

Since release notes are relevant to all issues, a tenant custom field is used instead of a subtype-specific field.

1curl --location 'https://api.devrev.ai/schemas.custom.set' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "type": "tenant_fragment",
7 "description": "Tenant attributes for issues",
8 "leaf_type": "issue",
9 "fields": [
10 {
11 "name": "release_notes",
12 "field_type": "rich_text",
13 "ui": {
14 "display_name": "Release Notes",
15 }
16 }
17 ]
18}'

Populate the release notes in the issue object created above:

1curl --location 'https://api.devrev.ai/works.update' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "id": "don:core:dvrv-us-1:devo/test:issue/1",
7 "type": "issue",
8 "custom_schema_spec": {
9 "tenant_fragment": true
10 },
11 "custom_fields": {
12 "tnt__release_notes": "<RELEASE_NOTES>"
13 }
14}'

The final issue object now looks as follows:

1{
2 "id": "don:core:dvrv-us-1:devo/test:issue/1",
3 "type": "issue",
4 "title": "API failure in Prod",
5 "display_id": "ISS-1",
6 "created_by": {...},
7 "created_date": "2024-10-11T06:48:57.759Z",
8 "modified_date": "2024-10-11T06:55:29.183Z",
9 "stage": "resolved",
10 "custom_fields": {
11 "ctype__regression": true,
12 "ctype__impacted_environments": [ "Prod" ],
13 "tnt__release_notes": "<RELEASE_NOTES>"
14 },
15 "subtype": "bug",
16 "custom_schema_fragments": [
17 "don:core:dvrv-us-1:devo/test:custom_type_fragment/1"
18 "don:core:dvrv-us-1:devo/test:tenant_fragment/1",
19 ]
20}

The following observations can be made from the above example:

  • The custom fields defined by different fragments are held in different namespaces in an object.
    • Subtype fields are of the form ctype__<field_name>.
    • Tenant fields are of the form tnt__<field_name>.
  • References to each fragment are stored with the object.
  • When updating an object, the custom_schema_spec can specify only the fragments being modified. Here, only the tenant fragment is specified as only the release notes field is being updated.

Supported custom field types

The following custom field types are supported -

TypeExample
int42
double3.14
booltrue
tokens"apple"
text"Hello, world!"
rich_text"**Hello**, world!"
enum"apple"
timestamp"2020-10-20T00:00:00Z" (RFC3339)
date"2020-10-20" (YYYY-MM-DD)
id"don:core:dvrv-us-1:devo/test:issue/1"

The list variants of all the supported custom field types are also supported. For example, []int, []tokens, etc.

Schema fragment versioning

Schema fragments are immutable. When evolving a fragment:

  1. A new fragment is created and chained to the older one.
  2. The older fragment remains intact.
  3. Objects referencing the older fragment are unaffected (more on this later).

The same API endpoint schemas.custom.set is used to create and update fragments. The API internally figures out how to version and chain the fragments.

Let’s say you want to add a new boolean field customer_impact to the bug subtype and delete the regression field.

The API call to add the new field and delete the old field is shown below:

1curl --location 'https://api.devrev.ai/schemas.custom.set' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "type": "custom_type_fragment",
7 "description": "Attributes for tracking a bug",
8 "leaf_type": "issue",
9 "subtype": "bug",
10 "fields": [
11 {
12 "name": "impacted_environments",
13 "field_type": "array",
14 "base_type": "enum",
15 "allowed_values": [ "Dev", "QA", "Prod" ],
16 "is_filterable": true,
17 "ui": {
18 "display_name": "Impacted Environments",
19 }
20 },
21 {
22 "name": "rca",
23 "field_type": "rich_text",
24 "ui": {
25 "display_name": "RCA",
26 }
27 },
28 {
29 "name": "customer_impact",
30 "field_type": "bool",
31 "ui": {
32 "display_name": "Customer Impact",
33 }
34 }
35 ],
36 "deleted_fields": [ "regression" ]
37}'

Note that:

  • The API payload reflects the entire state of the new fragment version.
  • The deleted_fields array specifies the field names that are being deleted. If not provided, the API call fails due to the lack of an explicit field deletion confirmation. This prevents accidental field deletions.

The above API call internally performs the following steps:

  1. Creates a new fragment with the specified payload.
  2. Updates the new fragment to point to the previous fragment. The old_fragment_ref system field in the new fragment points to the previous fragment.
  3. Updates the old fragment to point to the new fragment. The new_fragment_ref system field in the old fragment points to the new fragment.

The diagram below shows the relationship between the fragments and how the versioning scheme preserves the referential integrity.

Fragment Versioning

Object upgrades

A natural question arises at this point: what happens to the objects referencing the old fragment version?

The object get and list APIs automatically upgrade the object in-memory to the latest fragment version when queried. The necessary field adjustments are done in this process. In the example above, when the object referencing the old fragment is read, the regression field is dropped in the response.

1curl --location 'https://api.devrev.ai/works.get' \
2--header 'Accept: application/json' \
3--header 'Authorization: <TOKEN>' \
4--data '{
5 "id": "don:core:dvrv-us-1:devo/test:issue/2"
6}'
7
8{
9 "work": {
10 "id": "don:core:dvrv-us-1:devo/test:issue/2",
11 "type": "issue",
12 "title": "Critical Service Outage",
13 "display_id": "ISS-x",
14 "created_by": {...},
15 "created_date": "2024-10-12T08:30:15.123Z",
16 "custom_fields": {
17 "ctype__impacted_environments": [ "Prod" ]
18 },
19 "subtype": "bug",
20 "custom_schema_fragments": [
21 "don:core:dvrv-us-1:devo/test:custom_type_fragment/2"
22 "don:core:dvrv-us-1:devo/test:tenant_fragment/1",
23 ]
24 }
25}

The object now references the latest fragment version (custom_type_fragment/2). While the optional release notes field is absent, the tenant fragment remains attached, allowing for future tenant-specific field additions.

Deprecating a custom schema fragment

Custom schema fragments can be deprecated to avoid creating work items using them. The following POST request payload to schemas.custom.set can be used:

1{
2 "type": "custom_type_fragment",
3 "description": "Attributes for tracking a bug",
4 "leaf_type": "issue",
5 "subtype": "bug",
6 "fields": [
7 ...
8 ],
9 "deprecated": true,
10}

Listing custom schema fragments

The following API call can be used to list all the custom schema fragments in your organization:

1curl --location 'https://api.devrev.ai/schemas.custom.list' \
2--header 'Accept: application/json' \
3--header 'Authorization: <TOKEN>'

Deprecated fragments aren’t listed in the response.

UI hints

UI hints allow customizing the UI/UX of custom fields. So far, ui.display_name has been used to set the display name of a field. Let’s look at the other supported UI hints:

  • display_name: The display name of the field.
  • is_hidden: Whether the field is hidden.
  • placeholder: The placeholder text for the field.
  • is_sortable: Whether the field is sortable. Requires is_filterable to be true.
  • is_groupable: Whether the field is groupable. Requires is_filterable to be true.

is_filterable is not a UI hint but a top level field property.

Stock field overrides

The fields available in native DevRev objects are called stock fields. For example, priority is a stock field of issue.

Let’s say you want to do the following modifications to the priority field in your organization:

  1. Update the UI display name from Priority to Urgency Level.
  2. Update the allowed values from P0, P1, P2, and P3 to Low, Medium, High, and Blocker.

Since the modification is applicable to all issues, you can create a tenant schema fragment with the following payload:

1curl --location 'https://api.devrev.ai/internal/schemas.custom.set' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "type": "tenant_fragment",
7 "leaf_type": "issue",
8 "description": "Stock field overrides demo",
9 "fields": [],
10 "stock_field_overrides": [
11 {
12 "name": "priority_v2",
13 "uenum_values": [
14 {
15 "id": 1,
16 "label": "Low",
17 "ordinal": 1
18 },
19 {
20 "id": 2,
21 "label": "Medium",
22 "ordinal": 2
23 },
24 {
25 "id": 3,
26 "label": "High",
27 "ordinal": 3
28 },
29 {
30 "id": 4,
31 "label": "Blocker",
32 "ordinal": 4
33 }
34 ],
35 "ui": {
36 "display_name": "Urgency Level",
37 }
38 }
39 ]
40}'

A few observations can be made from the above payload:

  • The stock_field_overrides array contains the overrides for the stock fields.
  • The name field in the override specifies the stock field to be overridden.
  • The uenum_values array contains the new allowed values for the stock field.
  • Each allowed value in the uenum_values array must have a unique id. Since labels can change, the id is used to identify the value.
  • The ordinal field is used to determine the sort order of the values.
  • The ui.display_name field updates the display name of the stock field.

If you want the overrides to be scoped to a subtype, you can add them to the subtype instead.

Stage customization

Stages represent the different phases an object can be in during its lifecycle. For example, an issue might go through the following lifecycle:

Issue Lifecycle

Customizing the stages allows you to tailor the object lifecycle to your organization’s specific requirements.

A state is a group of stages. For example, the open state groups the triage, backlog, and prioritized stages. By default, DevRev creates open, in_progress, and closed states in your organization.

Let’s say you want to add a new stage Needs RCA to the bug subtype.

Adding a custom stage

1curl --location 'https://api.devrev.ai/stages.custom.create' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "name": "Needs RCA",
7 "state": "closed",
8 "ordinal": 1000
9}'

A stage can be referenced by any object type. For example, both issue and ticket object types can use the in_development stage. It’s incorrect to say that the stage is bound to an issue or ticket.

Stage diagram

A stage diagram determines the allowed transitions between stages for a given object. For example, triage stage can transition to backlog stage but not vice versa.

Let’s create a stage diagram for the bug subtype:

Stage Diagram

1curl --location 'https://api.devrev.ai/stage-diagrams.create' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "leaf_type": "issue",
7 "subtype": "bug",
8 "stages": [
9 {
10 "stage_id": "don:core:dvrv-us-1:devo/test:stage/1",
11 "is_start": true,
12 "transitions": [
13 {
14 "target_stage_id": "don:core:dvrv-us-1:devo/test:stage/2",
15 }
16 ]
17 },
18 {
19 "stage_id": "don:core:dvrv-us-1:devo/test:stage/2",
20 "transitions": [
21 {
22 "target_stage_id": "don:core:dvrv-us-1:devo/test:stage/3"
23 }
24 ]
25 },
26 {
27 "stage_id": "don:core:dvrv-us-1:devo/test:stage/3",
28 "transitions": [
29 {
30 "target_stage_id": "don:core:dvrv-us-1:devo/test:stage/4"
31 }
32 ]
33 },
34 {
35 "stage_id": "don:core:dvrv-us-1:devo/test:stage/4",
36 }
37 ]
38}'

It is important to specify the start stage for the diagram. This is the default stage that gets assigned to the newly created objects.

Using a stage diagram

The stage diagram created above can be referenced in the bug subtype as follows:

1curl --location 'https://api.devrev.ai/schemas.custom.set' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "type": "custom_type_fragment",
7 "leaf_type": "issue",
8 "subtype": "bug",
9 "stage_diagram_id": "don:core:dvrv-us-1:devo/test:stages_diagram/1",
10 "fields": [
11 ... // no changes
12 ]
13}'

All objects of the bug subtype now adhere to the stage diagram created above.

Dependent fields

Let’s say that the developers in your organization tend to forget to add an RCA when resolving the bugs. You can make the RCA field required when a bug object is moved to the completed stage by adding a dependent field constraint.

1curl --location 'https://api.devrev.ai/schemas.custom.set' \
2--header 'Content-Type: application/json' \
3--header 'Accept: application/json' \
4--header 'Authorization: <TOKEN>' \
5--data '{
6 "type": "custom_type_fragment",
7 "leaf_type": "issue",
8 "subtype": "bug",
9 "fields": [
10 ... // no changes
11 ],
12 "conditions": [
13 {
14 "expression": "stage == 'don:core:dvrv-us-1:devo/test:stage/5'",
15 "effects": [
16 {
17 "fields": [ "custom_fields.rca" ],
18 "require": true
19 }
20 ]
21 }
22 ]
23}'

Any attempt to update a bug object to the completed stage without populating the RCA field is rejected.

The supported operators are ==, !=, >, >=, <, <=. The expression is a boolean expression that must return a boolean value.

The effects array contains the list of effects of the condition. The following effects are supported:

  • require: Whether the field must be set for the condition to be met.
  • show: Whether the field must be shown for the condition to be met.
  • allowed_values: The conditional allowed values for the enum type field.

don:core:dvrv-us-1:devo/test:stage/5 is the ID of the completed stage. The stage display name is not used in the expression because it is liable to change.