Skip to content

GitLab

  • Menu
Projects Groups Snippets
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in
  • C CiviCRM Core
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 1,376
    • Issues 1,376
    • List
    • Boards
    • Service Desk
    • Milestones
  • Deployments
    • Deployments
    • Releases
  • Wiki
    • Wiki
  • Activity
  • Graph
  • Create a new issue
  • Commits
  • Issue Boards
Collapse sidebar
  • Development
  • CiviCRM Core
  • Issues
  • #2665

Closed
Open
Created Jun 24, 2021 by totten@tottenOwner

Allow more structured metadata for APIv4 actions

Overview

Expand the range of metadata that can be reported via APIv4, enabling more dev-tools and site-builder-tools. Specifically, allow for structured-data-types which are not entities. This is useful for action-parameters and value-types.

Example use-case

  • For a specific workflow-message, provide a detailed list of fields that should provided in the message's evaluation context.
  • For Afform.prefill, provide a detailed list of fields that are expected for the array $args.
  • For Afform.submit, provide a detailed list of fields that are expected for the array $values and array $args.

(The motivation here is more about providing metadata for message-workflow parameters, but the same reasoning applies to Afform APIs which already exist, so I'll use those for the examples.)

Current behaviour

The type is declared as array. This does not truly indicate what is accepted/expected/required. Ex:

use Civi\Afform\Event\AfformSubmitEvent;
class Submit extends AbstractProcessor {
  /**
   * Submitted values
   * @var array
   */
  protected $values;
}
// Afform.getActions returns:
        "name": "submit",
        "params": {
            "values": {
                "description": "Submitted values",
                "type": ["array"]
            },

Proposed behaviour

Any API parameter of type @var array may also declare a @schema. This is a symbolic name of the data-type -- and using this name, you can get more detailed metadata.

use Civi\Afform\Event\AfformSubmitEvent;
class Submit extends AbstractProcessor {
  /**
   * Submitted values
   * @var array
   * @schema /Afform/FormFields?$name
   */
  protected $values;
}
// Afform.getActions - give that `$name==newContact` would return:
        "name": "submit",
        "params": {
            "values": {
                "description": "Submitted values",
                "type": ["array"],
                "schema": "/Afform/FormFields/newContact"
            },

And then a bit more detailed usage:

// Looking up the metadata for an API's action-parameter.
$actions = civicrm_api4('Afform', 'getActions', [
  'values' => ['name' => 'newContact'],
])->indexBy('name');
$this->assertEquals('array',                         $actions['submit']['params']['args']['type']);     // Status quo
$this->assertEquals('array',                         $actions['submit']['params']['values']['type']);   // Status quo
$this->assertEquals('/Afform/FormArgs?newContact',   $actions['submit']['params']['args']['schema']);   // New
$this->assertEquals('/Afform/FormFields?newContact', $actions['submit']['params']['values']['schema']); // New

$actions = civicrm_api4('MessageWorkflow', 'getActions', [
  'values' => ['name' => 'pcp_notify'],
])->indexBy('name');
$this->assertEquals('array',                                   $actions['render']['params']['rows']['type']);   // Status quo
$this->assertEquals('/MessageWorkflow/TplParams[]?pcp_notify', $actions['render']['params']['rows']['schema']); // New

// Resolve the 'schema' via internal/optimized API
$fields = Civi::schema()->get('/Afform/FormFields?newContact');
$this-=>assertEquals('String', $fields['Contact1.first_name']['type']);

// Resolve 'schema' via external/remote API
civicrm_api4('Schema', 'get', ['where' => [['name', '=', '/Afform/FormFields/newContact']], 'format' => 'json-schema-7']);
civicrm_api4('Schema', 'get', ['where' => [['name', '=', '/MessageWorkflow/TplParams/pcp_notify']], 'format' => 'civi-schema-1'])

// Supply a schema definition
Civi::dispatcher()->addListener('civi.schema.resolve::/Afform/FormFields', function($e) {
  $afform = Civi::service('afform_scanner')->getLayout($e->getUrlParam());
  $e->setSchema(...extractFields($afform)...);
});

Comments

(1) Note that the @schema can be parameterized (e.g. $name or $entity). This means that (e.g.) the newContact form and the newActivity form would have different schemas.

(2) The @schema identifier is a relative URL. This is inspired by JSON Schema.

(3) There is a part of me which thinks it would be really nice to use an existing standard (like JSON Schema). However, this would also create internal-inconsistencies. It seems more realistic to re-use the Civi Schema (akin to '$dao::fields()`) and then define an export-mapping (Civi Schema => JSON Schema) if needed.

(4) It might also make sense to extend this to, say, {$entity}::create() and {$entity}::update() e.g.

class AbstractCreateAction ... {
  /**
   * List of values to set on the new entity.
   * @var array
   * @schema /Entity/Fields?$entity
   *   - This is just an alias for '\Civi\Api4\$entity::getFields()`
   */
  protected $values = [];
}

(5) I think I can get through my immediate work without this - hence type:backlog. However, I wanted to document the idea.

Related rambling: https://chat.civicrm.org/civicrm/pl/o5bjakwjufdgxgrw6zbumh8zjy

To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information
Assignee
Assign to
Time tracking