Skip to content

Backend

The feature lib/ directory is in @INC, so Clarive loads its BaselinerX::* modules at startup, as with any other feature. No core change is needed. Perl changes need a web server restart.

Permissions (actions)

lib/BaselinerX/HelloReact.pm:

package BaselinerX::HelloReact;
use Moose;
use Baseliner::Core::Registry ':dsl';
use Baseliner::Utils qw(_locl);

register 'action.hello_react.view'   => { name => _locl('View Hello React pages') };
register 'action.hello_react.config' => { name => _locl('Configure Hello React in a project') };
register 'action.hello_react.admin'  => { name => _locl('Administer Hello React') };

no Moose;
__PACKAGE__->meta->make_immutable;
1;
  • The actions show up in the role editor (under the hello_react folder).
  • Once granted in a role, they reach the browser in appData.permissions, which is what the SDK checks in menus, routes and can().
  • Keep the names in a src/actions.js to avoid repeating strings in the frontend.

Endpoints: /feature/<id>/api/...

lib/BaselinerX/Controller/HelloReact.pm:

package BaselinerX::Controller::HelloReact;
use Moose;
BEGIN { extends 'Catalyst::Controller' }

use Baseliner::Utils qw(_now);

# sdk.api.get('greeting')  ->  GET /feature/hello-react/api/greeting
__PACKAGE__->config( namespace => 'feature/hello-react/api' );

sub greeting : Local : Does('ACL') : ACL('action.hello_react.view') {
    my ( $self, $c ) = @_;
    my $p = $c->req->params;

    $c->stash->{json} = {
        success    => \1,
        message    => 'Hello ' . $c->username,
        id_project => $p->{id_project},
        time       => _now(),
    };
    $c->forward('View::JSON');
}

sub save : Local : Does('ACL') : ACL('action.hello_react.admin') {
    my ( $self, $c ) = @_;
    my $name = $c->req->params->{name} // '';

    $c->stash->{json} = { success => \1, saved => $name, by => $c->username };
    $c->forward('View::JSON');
}

no Moose;
__PACKAGE__->meta->make_immutable;
1;

Rules:

  • namespace = 'feature/<id>/api', with the exact directory <id>. That is what sdk.api calls.
  • Every action has Does('ACL') : ACL('action....'). Without the action, the server answers a JSON 403 and sdk.api rejects with err.status === 403. Hiding a button or a route in the frontend protects nothing.
  • Parameters:
    • api.get and api.post arrive in $c->req->params.
    • api.postJSON: the JSON (if it is an object) is merged into $c->req->params, and the original JSON, with its arrays intact, is kept in $c->req->{body_data}.
  • Response: $c->stash->{json} = {...} and $c->forward('View::JSON'). For errors, return an HTTP error status with a msg: that is what the frontend gets in err.message.
  • A path under /feature/<id>/api/ that does not exist returns a JSON 404 (not an HTML page).
  • Everything under /feature/<id>/ requires a logged in session, unlike /plugin/<id>/....

The usual tools are available in the controller: $c->username, mdb->..., ci->..., Util->..., as in any core controller.

Declaring components for a repository: viewer_components

The core screens that accept feature components (see SDK Reference) learn which component to paint from the repository CI: its viewer_components method, which returns a map of place to full component name. The core defines it empty ({}) in Baseliner::Role::CI::Repository and sends it to the frontend with each revision and repository.

It is declared by the owner of the repository type, never by the core. Only the keys revision, select and browse are used; the missing ones get the core view.

  • A feature with a Perl backend: its own CI class in <feature>/lib/BaselinerX/CI/, loaded with the rest of the feature's BaselinerX::* modules:

    ```perl package BaselinerX::CI::HelloRepository; use Moose; BEGIN { extends 'BaselinerX::CI::GitRepository' }

    sub collection { 'HelloRepository' } sub viewer_components { +{ revision => 'hello-react/RevisionViewer' } }

    1; ```

  • A JS plugin CI: next to its viewer_type:

    js viewer_type: () => 'reserve', viewer_components: () => ({ revision: 'foo/RevisionViewer', select: 'foo/SelectRevisions', browse: 'foo/BrowseRepository' }),

The topic view is cached: after changing viewer_components, restart the web server and clear the cache (cla db-cache_clear) before checking a topic.