OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openproject/docs/development/concepts/resource-schemas/README.md

146 lines
7.3 KiB

---
sidebar_navigation:
title: Schemas
description: An introduction to resource schemas and how they are tied to editable resources
robots: index, follow
keywords: concept, schemas, resource schemas
---
# Resource schemas
In OpenProject, editable resources such as work packages or projects can be highly customized by the user. A resource can have an arbitrary number of additional custom fields. In the frontend, the associated schema to a resource needs to be loaded in many cases when rendering attributes of that resource, such as in an [inline-editable field](../inline-editing).
## Key takeaways
Schema objects are the dictionary for the frontend application to identify the available properties of a resource.
*Schemas contain:*
- a (possibly) localized name
- The value type of the defined attributes
- Constraints for the authenticated user, i.e., whether the attribute is currently writable
- (optional) additional option definitions for the attribute.
## Prerequisites
The following guides are related:
- [HAL resources](../hal-resources)
- [Backend API overview](#TODO:api-overview)
## API Backend
Schemas in the backend are regular Grape endpoints. For example, the schema of all projects is rendered through the [`::API::V3::Projects::Schemas::ProjectsSchemaAPI`](https://github.com/opf/openproject/blob/dev/lib/api/v3/projects/schemas/project_schema_api.rb). This in turn renders the associated [`::API::V3::Projects::Schemas::ProjectsSchemaRepresenter`](https://github.com/opf/openproject/blob/dev/lib/api/v3/projects/schemas/project_schema_representer.rb), which contains the set of schema properties to be rendered.
The work packages' schemas are significanatly more complex. Each work package type will define its own schema due to the dynamics of the [form configuration](https://docs.openproject.org/system-admin-guide/manage-work-packages/work-package-types/#work-package-form-configuration). With it, the order and grouping of work package attributes can be defined per type, resulting in different attributes to be displayed. In addition, custom fields can be individually enable per project for even more flexibility.
This results in not a single schema for all work packages, but one schema for each project - type combination.
The resulting schema JSON is an object with properties that look like the following:
```json
"property": {
"type": "String",
"name": "Schema property",
"required": true,
"hasDefault": false,
"writable": true,
"minLength": 1,
"maxLength": 255,
"options": { /** */ }
},
...
```
### Schema examples
This section describes some of the existing schemas.
**Projects**
For projects, there is a single APIv3 endpoint for their schemas: `/api/v3/projects/schema`. This schema is identitcal for all projects. You can simply request the OpenProject Community schema for projects here: https://community.openproject.com/api/v3/projects/schema. It contains a set of static properties (name, identifier, status, etc.), as well as all project-level custom fields.
**Work packages**
The work package schema is more complicated, as work package types can be customized to define what attributes the type should show as part of the [form configuration](https://docs.openproject.org/system-admin-guide/manage-work-packages/work-package-types/#work-package-form-configuration). Additionally, the visibility of custom fields can be controlled on a per-project level.
This results in work package schemas being defined per project and type combination. The URL of each schema looks like this: `/api/v3/work_packages/schemas/{project id}-{type-id}`.
An exemplary schema response on the Community for the OpenProject project (`ID=14`) and the Bug type (`ID=1`) is https://community.openproject.com/api/v3/work_packages/schemas/14-1
The work package schema also contains the reference to the attribute groups from the form configuration in the `_attributeGroups` property.
## Frontend usage
The OpenProject frontend usually ensure that whenever you get access to a HAL resource, its associated schema (if there is any) is also loaded. This is done through the [`SchemaCacheService`](https://github.com/opf/openproject/tree/dev/frontend/src/app/components/schemas/schema-cache.service.ts). It will request the associated schema unless it has already been cached in the global states object to avoid loading a schema multiple times.
In some cases, such as the work package `/api/v3/work_packages` or `/api/v3/queries` endpoints, the needed schemas to represent the work packages contained in the collection are embedded automatically in the `_embedded.schemas` endpoint. Services handling these loaded requests such as the [`WorkPackagesStatesIntializationService`](https://github.com/opf/openproject/blob/dev/frontend/src/app/components/wp-list/wp-states-initialization.service.ts#L97-L101) will automatically update the schema states.
If you look at the HAL+JSON response of a work package API request, you will see it has a `_links.schema.href` property which identifies the schema resource that the loaded work package is associated with. (Exemplary request: http://community.openproject.com/api/v3/work_packages/34250)
If you have work package resource, you can get hold of its associated schema as follows:
```typescript
const schemaCache = injector.get(SchemaCacheService);
const workPackage = /** Work package from input or something */
schemaCacheService
.ensureLoaded(workPackage)
.then((schema:SchemaResource) => {
// Output the localized name of the "subject" property.
console.log(schema.subject.name);
});
```
The schema resource is made out of properties that the frontend identifies as [`IFIeldSchema`](https://github.com/opf/openproject/blob/dev/frontend/src/app/modules/fields/field.base.ts) interface:
```typescript
export interface IFieldSchema {
// Type of the schema property, such as "String", "Integer", etc.
type:string;
// Whether the property is writable
writable:boolean;
// A set or link of allowed values e.g., for list-types
allowedValues?:any;
// Whether this property requires a value to be saved
// (translates to input[required] property)
required?:boolean;
// Whether this property has a default value when saving
hasDefault:boolean;
// The localized name of this property
name:string;
// A set of options transmitted by the backend, mostly empty
options?:any;
}
```
### Form schemas
When you try to update a resource such as a work package, you will commonly request a `Form` resource for this work package, which is a temporary resource that will have your changes applied to them, including error handling. In these forms, an embedded schema is output that represents the schema with permissions applied for the current user.
For example, if you try to update a work package type from let's say `Bug` to `Feature`, you would POST to the form with its type link updated, and are returned with a form object. The embedded schema of this form now points to the `Feature` type, and may contain additional attributes to render due to the differing form configuration.
These embedded schemas are never globally cached in the frontend, as they are highly dependent on the changes pushed to the form resource. They are always contained within a `ResourceChangeset`. Please see [the separate guide on changesets](#TODO:changesets) for more information.