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_reactfolder). - Once granted in a role, they reach the browser in
appData.permissions, which is what the SDK checks in menus, routes andcan(). - Keep the names in a
src/actions.jsto 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 whatsdk.apicalls. - Every action has
Does('ACL') : ACL('action....'). Without the action, the server answers a JSON 403 andsdk.apirejects witherr.status === 403. Hiding a button or a route in the frontend protects nothing. - Parameters:
api.getandapi.postarrive 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 amsg: that is what the frontend gets inerr.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'sBaselinerX::*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.