BEdita 4 documentation¶
Overview¶
BEdita is a tool to handle the data of your mobile, IoT, web and desktop applications.
- It’s a full fledged content management solution with:
- an HTTP REST driven server application - the Backend - with a complete API to model, create, modify and retrieve data
- a default progressive web application - the Manager - to control & manage your data
BEdita is built with CakePHP 3 and uses relational DBMS like MySQL, Postgres or SQLite in conjunction with (optional) NoSQL systems like Redis, Elastic Search and InfluxDb to boost performance and scale up to Big Data scenarios.
JSON API is the primary, although not unique, exchange data format.
Backend¶
The Backend is the core of BEdita: an HTTP REST service that exposes a set of API endpoints.
It’s not a standalone application service or daemon like a DBMS, but it’s an application deployed in a Web server like Apache or Nginx and accessible via standard HTTP ports.
So at first it may look like a standard Web application, but it’s not.
It doesn’t offer an HTML interface, it doesn’t respond in HTML [1] but only in JSON [2]: the only way to communicate with this service is through its JSON API REST endpoints. See API reference for an overview.
To install a backend instance see Setup
Manager¶
The Manager is a progressive javascript web application to manage all of your data using the backend REST API.
You may see this application as the primary GUI of BEdita or as a control panel or admin console.
To operate with this application you need to login first.
The first user created during setup has the permanent role of administrator and using its credentials you may:
- create new users and assign roles to them
- create new roles
- create and modify objects - contents or tree structures that may be used in your applications
- create new object types from your application domain or extend existing types
- create relations between objects
- give access permissions to endpoints and objects to some roles
- set configuration settings and view system information and relevant events
Through its dynamic permission system keep in mind that users having different roles may perform different actions:
- some users may not be able to login in the Manager - e.g. standard users of your applications
- some users may not have access to some endpoints - like modeling new types, or view some object types
- some users may have a read-only access to some endpoints - they may only see certain types but not create or modify anything
Notes
[1] | it’s possible however, only for testing/debugging purposes, to activate HTML responses to use the Backend in a browser, see Accept |
[2] | other formats like YML (or XML) could be added in future releases. |
Setup¶
Below you will find instructions to setup BEdita4 on a typical server or desktop.
For a quick setup via Docker see below.
Prerequisites¶
- PHP 7.x (recommended) or PHP >= 5.6, with extensions mbstring, intl
- MySQL 5.7 (recommended) or MySQL 5.6, Postgres 9.5/9.6 or SQLite 3
- Composer
A web server like Apache or Nginx also recommended in general, but not strictly necessary on a development enviroment.
OS: there are no virtually constraints or limitations, but a recent Linux distribution is recommended for a server setup and Linux is the only officially supported server platform.
For a development/test setup you may choose any modern desktop/server OS. BEdita dev team uses mainly Linux and MacOSX so some strange things may happen using other systems :)
But let us know if you have any OS specific troubles, we will be happy to help.
Install¶
There are three basic steps to install BEdita 4
1. Create project via composer
$ composer create-project -s dev bedita/bedita
If you are using a .zip or .tar.gz release file you just need to unpack it and then run composer install
.
2. Create an empty database
Create an empty MySQL or Postgres database and keep track of main connection parameters
like host
, username
, password
and database
.
Do nothing for SQLite since a new local file will be created.
3. Run shell script to initialize the database and create first admin user
Open a shell prompt on root installation folder and write
$ bin/cake bedita setup
- An interactive shell script will guide you through missing installation steps:
- database setup & schema check
- filesystem check
- admin user creation
You may see the first admin user created like a root user on Linux or MySQL: it has administrator role privileges and cannot be removed.
To setup a database connection you may also edit the main configuration file, see Manual Setup.
Web Server¶
On a development setup it’s possible to use PHP builtin webserver, have a look at CakePHP Development Server Using this simple command
$ bin/cake server
This will serve the backend application at http://localhost:8765/.
This setup should be used ONLY in development and NEVER as a production setup.
For test and production setups is always advisable to use a real web server like Apache2 or Nginx.
Apache2¶
Below some instructions for Apache 2.4, but also other versions of Apache, like 2.2, will work.
Please make sure that mod_rewrite is enabled. On Ubuntu 14.04 or 16.04 or on Debian 8 you may verify it like this
$ more /etc/apache2/mods-enabled/rewrite.load
LoadModule rewrite_module /usr/lib/apache2/modules/mod_rewrite.so
On other systems with different Apache configurations this check should be similar.
A simple minimal working virtualhost configuration can look like this:
<VirtualHost *:80>
ServerName api.example.com
DocumentRoot /path/to/bedita/webroot
<Directory /path/to/bedita/webroot>
Options FollowSymLinks
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
- Where:
/path/to/bedita
refers simply to the Backend path on filesystemDocumentRoot
should point to thewebroot
folderAllowOverride All
is needed to enable.htaccess
filesRequire all granted
allows access from anywhere, you may decide to set some restrictions based on hosts/IP
To enable CORS on virtualhost configuration you may add these lines, provided that mod_headers is enabled
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Headers "content-type, origin, x-requested-with, authorization"
Header set Access-Control-Allow-Methods "PUT, GET, POST, PATCH, DELETE, OPTIONS"
Header set Access-Control-Expose-Headers "Location"
- In this example:
- all origins and HTTP methods are allowed, you may want to add restrictions
- only headers used by BEdita4 are allowed
- “Location” header is exposed in response, this is useful to get URL of a newly created resource
Alternatively you can setup CORS configuration directly in BEdita, see CORS
Nginx¶
[TBD]
Manual Setup¶
To setup database connection manually or review the current connection
you may edit the main default configuration file located in config/app.php
where datasources are defined.
Look for Datasources
array definition then modify host
, username
,
password
and database
fields.
'Datasources' => [
'default' => [
'className' => 'Cake\Database\Connection',
'driver' => 'Cake\Database\Driver\Mysql',
'host' => 'localhost',
//'port' => 'non_standard_port_number',
'username' => '......',
'password' => '......',
'database' => '......',
.....
],
]
- Other noteworthy fields:
port
- populate only in case of non standard portsdriver
- change to'Cake\Database\Driver\Postgres'
or'Cake\Database\Driver\Sqlite'
accordingly- for SQlite you need to set only an absolute local file path in
database
Docker¶
You need a working Docker setup in order to pull, build or run images.
Pull official image¶
You can get the latest offical image build from Docker Hub like this.
$ docker pull bedita/bedita:latest
You may also use :4-cactus
tag instead of :latest
, they are currently synonyms.
Release tags will be available soon.
Build image locally¶
If you want to build an image from local sources you can do it like this from BEdita root folder:
$ docker build -t bedita4-local .
You may of course choose whatever name you like for the generated image instead of bedita4-local
.
Run¶
Run a Docker image setting an initial API KEY and admin username and password like this:
$ docker run -p 8090:80 --env BEDITA_API_KEY=1029384756 \
--env BEDITA_ADMIN_USR=admin --env BEDITA_ADMIN_PWD=admin \
bedita/bedita:latest
This will launch a BEdita4 instance using SQLite
as its storage backend. It should become available at http://localhost:8090/home almost instantly.
Replace bedita/bedita:latest
with bedita4-local
(or other chosen name) to launch a local built image.
Using PostgreSQL or MySQL¶
Other database backends can be used with BEdita by launching the database server in a separate Docker container.
You may simply pull mysql:5.7
or postgres:latest
official images to achieve this.
A MySQL 5.7 server can then be launched in a container with this command:
docker run -d --name mysql \
--env MYSQL_ROOT_PASSWORD=root \
--env MYSQL_DATABASE=bedita \
--env MYSQL_USER=bedita \
--env MYSQL_PASSWORD=bedita \
mysql:5.7
Then, a BEdita instance can be configured to use MySQL as its backend launching this command:
docker run -d --name=bedita \
--env DATABASE_URL=mysql://bedita:bedita@mysql:3306/bedita \
-p 8090:80 --link mysql:mysql \
bedita/bedita:latest
Notice the DATABASE_URL
environment variable setting.
The BEdita container will automatically wait until MySQL container becomes available, then will run connect to it, launch required schema migrations, and start the Web server. The application should become available at http://localhost:8090/home in a matter of few seconds. However, depending on the responsiveness of MySQL container, this might take longer.
Logging¶
Logs are written to stdout and sterr, so that they can be inspected via docker logs
. This is considered a common practice for Docker containers, and there are tools that can collect and ingest logs written this way. However, LOG_ERROR_URL
and LOG_DEBUG_URL
can be overwritten at container launch via --env
flag to send logs to a different destination. For instance, one might want to launch a Logstash container, link it to BEdita container, and send BEdita logs to Logstash.
Configuration¶
Configuration parameters of a BEdita4 application are usually stored in PHP files and on a database table.
We have:
config/app.php
file containing App, Datasource, Cache, Log and other basic low-level settings common to every CakePHP applicationconfig
table with BEdita4 specific parameters loaded afterconfig/app.php
To know more about configuration in CakePHP please read this CakePHP book chapter
In config
table every plugin (like API and Core BEdita4 plugins) can define and load its own parameters using its own context (have a look at Accept configuration for an example).
Using configurations keep in mind these common usage rules and best practices:
config
table records may generally overrideconfig/app.php
config
table may not override low-level config settings likeDatasources
,Cache
,EmailTransport
,Log
,Error
andApp
config/app.php
configurations should follow Twelve Factor app configuration principles : use environment variables for everything that is likely to vary between deploys
Below a brief BEdita4 configurations reference in alphabetical order.
Accept¶
Define extra content types to accept in HTTP requests using Accept:
header other than
JSON (application/json
) and JSONAPI (application/vnd.api+json
) that are always accepted.
Instead HTML (text/html
, application/xhtml+xml
, application/xhtml
, text/xhtml
) generally not, but can be accepted through this configuration.
Same rule could be applied in the future to content types like XML and YAML (currently not supported).
In config/app.php
we have
'Accept' => [
'html' => filter_var(env('ACCEPT_HTML', 'false'), FILTER_VALIDATE_BOOLEAN),
]
In config
table we may override it with a record with these fields
- context:
'api'
- name:
'Accept'
- content:
{ "html" : true }
CORS¶
It’s possible to setup some basic CORS configuration parameters directly in BEdita4.
Using this settings please beware of possible conflicts with similar settings made on your HTTP server.
An optional ‘CORS’ configuration should be like this example:
'CORS' => [
'allowOrigin' => '*.example.com', // string or array , also '*'
'allowMethods' => ['GET', 'POST'], // (optional) array, also '*'
'allowHeaders' => ['X-CSRF-Token'] // (optional) array, also '*'
]
Where:
- CORS.allowOrigin is a single domain or an array of domains
- CORS.allowMethods is an array of HTTP methods
- CORS.allowHeaders must be an array of HTTP headers
So if you want to allow every CORS call with the most permissive setting, on development and test systems, you may set:
'CORS' => [
'allowOrigin' => '*', // allow every origin
'allowMethods' => '*', // allow every method
'allowHeaders' => '*' // allow every header
]
Pagination¶
Pagination settings for every API list response are done via 'Pagination'
key:
'Pagination' => [
'limit' => 20,
'maxLimit' => 100,
]
Where:
- limit - int - Default number of items per page as seen in page_size meta response and query string. Defaults to 20.
- maxLimit - int - Maximum acceptable items per page on a page_size request. Defaults to 100. This value cannot exceed 500.
Plugins¶
Plugins setup for your BEdita4 instance is done through 'Plugins'
configuration key:
'Plugins' => [
'DebugKit' => ['debugOnly' => true, 'bootstrap' => true],
'MyPlugin' => ['autoload' => true, 'bootstrap' => true, 'routes' => true],
]
Where each key is a plugin name, and for each plugin available options are:
- debugOnly - boolean - (default: false) If true load this plugin in ‘debug’ mode only.
- bootstrap - boolean - (default: false) If true load the
$plugin/config/bootstrap.php
file. - routes - boolean - (default: false) If true load the
$plugin/config/routes.php
file. - ignoreMissing - boolean - (default: false) If true ignore missing bootstrap/routes files.
- autoload - boolean - (default: false) Whether or not you want an autoloader registered
Security¶
Additional security settings regarding anonymous access and JWT (JSON Web Tokens) are possible, even though not mandatory.
'Security' => [
//....
'blockAnonymousApps' => true,
'blockAnonymousUsers' => true,
'jwt' => [
'duration' => '+2 hours',
'algorithm' => 'HS256',
],
],
Where:
- Security.blockAnonymousUsers when true on each request user must be identified, anonymous requests will receive a
401 Unauthorized
response - when false anonymous read requests (GET) are possible, but identification is always required for write operations (POST, PATCH, DELETE) - Security.blockAnonymousApps when true on each request application must be identified, anonymous or unknown applications will receive a
403 Forbidden
response - Security.jwt.duration is the default duration of the generated JWT. Keeping this value low increases security, but increases load on server as more renew requests will be performed by clients.
- Security.jwt.algorithm is the encryption algorithm used to issue new tokens. Must be one of
HS256
,HS512
,HS384
, orRS256
.
Database¶
Schema¶
BEdita4 uses CakePHP Migrations plugin for schema creation and update.
Schema migration files are located in plugins/BEdita/Core/config/Migrations
and they are basically PHP files that describe
database schema evolution.
With simple shell commands you can check migration status and perform schema update. See also db_admin shell command to perform schema check and initialization.
A MySQL schema file, provided for convenience only, is available in plugins/BEdita/Core/config/schema/be4-schema-mysql.sql
.
It’s not used anywhere in BEdita but it may be used as a quick reference.
ER Diagram¶
A simple ER diagram is displayed here to give you a glimpse of BEdita4 core schema. Don’t use it as a reference though: we will keep an updated version of this diagram, but some tables or fields may still be missing.
Migrations¶
The recommended way to create a schema migration is to use the bake
shell.
In your terminal, run:
$ bin/cake bake resources_migration MigrationsName
This command will generate two files in your config/Migrations folder:
YYYYMMDDHHMMSS_MigrationName.php
: this is the file used as reference in the phinxlogYYYYMMDDHHMMSS_MigrationName.yml
: this will contain the real content of the migration
Then, you can use the yaml syntax to describe the migration:
# MigrationName migration
---
#create:
#update:
#remove:
In the following example, we are going to add a property to the profiles
model, as well as a relation with the documents
model:
# AddAuthorStuff migration
---
create:
properties:
- name: pen_name
object: profiles
property: text
relations:
- name: author_of
label: Author of
inverse_name: authored_by
inverse_label: Authored by
description: Author relation
left:
- profiles
right:
- documents
Objects in BEdita¶
Objects definition¶
Brief definition: BEdita objects
(see object) are the contents of your application,
the atoms of the information asset you are building.
BEdita objects
also represent your application data model.
Let’s try to explain that with some examples: are you building an application to show a museum collection?
Your objects will be artworks, authors and maybe historical events, locations and museum rooms, but also images and videos.
All of them with some relations between them: an artwork was created_by an author (or more authors), it could represent an historical event, it was created on a particular place and be located in a museum room or borrowed from another museum or gallery.
Are you building a rental car application?
Your objects will probably be cars, car companies, customers, locations of your offices and so on. Applying some relations you may have a car that has been produced by a car company, rented by a customer from an office.
You can create your custom object types from scratch but you have also a vast collection of core object types ready to use that may already fit your needs, like:
- contents like documents, events, news
- multimedia items like images, video, audio or collections of those items
- folders that contain other folders or contents to build a hierarchical and browsable structure for your data
Not everything is an object
in BEdita, unlike what happens in Java and other platforms.
An object in BEdita is identified with these capabilities:
- you may extend it adding new properties
- you may create semantic relations between objects
- objects can be put in a folder and have tags or categories
- every change on object properties is versioned
- objects can be translated in other languages
- objects share a common id space and have a unique name for every project - an url friendly string identifier
- you may create new object types inheriting an existing object type
Other entities in BEdita are called resources (see resource). Resources have a fixed and predefined structure and can be classified in four basic groups:
- entities like tags, categories, permissions or annotations are special entities that we assign to objects: they are not objects itself, they rather define object properties
- entities like object types, relations, properties are used to design our object model
- other entities like endpoints, configurations, applications, auth providers will be rarely seen directy by API client developers or applications users: they handle API and project internal behaviors
- roles are special entities used only to give users permissions on objects, endpoints or object types - they are not objects
Users
(see user) instead are a special object type: you may add properties and relations
to other objects, but you may not extend it with a new type.
It’s a powerful yet simple model design that you may use successfully in a wide range of applications.
Authentication & Authorization¶
BEdita4 is a multi-application system: it is designed to handle different client applications requests performed by multiple users on the same project.
For every request application and user roles should be identified: each application may have its own grants, and each user also depending on role assignement.
As a general rule: anonymous read operations are absolutely possibile, for write operations at least the user performing the request must be identified.
To handle these scenarios please have a look at Security configuration and set blockAnonymousUsers
or blockAnonymousApps
flags accordingly.
Authentication¶
User authentication¶
User authentication is performed using JWT standard. You may have a look a the official JWT introduction for a more detailed explanation of the standard.
Basically JWT enables a token based authentication flow using an access token to access resources and an opaque refresh token to renew the access token.
See Authentication /auth endpoint documentation on how you can retrieve the JWT tokens with a standard username and password user authentication.
Once the token is retrieved it can be used on every request in the Authorization
HTTP header this way:
Authorization: Bearer <token>
Application identification¶
Client applications instead are identified using a simple API KEY mechanism. Through Administration /admin endpoint it’s possible to set and retrieve an API KEY for each application registered for a project.
On each request you may set the application’s API KEY using the X-Api-Key
HTTP header
X-Api-Key: <api-key>
We prefer to talk about application identification instead of authentication because it’s a weak authentication system: web or mobile client applications may expose this api key, or at least secrecy is not guaranteed.
This is somehow similar to implicit grant
in OAuth2.
The only real authentication is user authentication: you must rely on that for a secure authentication.
Authorization¶
Once user and application performing a request are identified, also as anonymous, the requested operation may or may not be allowed depending on the outcome of the following checks:
- anonymous user and application settings: depending on Security configuration anonymous requests can be blocked with 401 or 403 responses
- endpoint permissions: each endpoint may allow or deny a request from a user using a client application
- object permissions: at the most granular level a single object may or may not be accessible or modifiable to the requesting user and application
Endpoint permissions¶
Operation grants on endpoints can be controlled through a set of rules involving roles, applications and permission types.
Possible values for endpoint, role and application in these rules are:
- endpoint endpoint id or
NULL
for every endpoint- role role id or
NULL
for every role or anonymous user- application application id or
NULL
for every application
Instead permission type
may have four different values for read operations (GET) or write operations (POST, PATCH, DELETE):
- false
(0b00)
: no permissions granted- true
(0b11)
: full permissions granted- mine
(0b01)
: permissions granted only on my resources, i.e. resources belonging to the autenticated user- block
(0b10)
: no permissions granted, and override all other permissions
The first two bit, starting from right, are for the read operations (GET) and the other two for the write operations (POST, PATCH, DELETE):
(0b0000)
: false for read [characters 00 on the right] and false for write [characters 00 in the middle, shifting by 2 positions]
The relative integer number is used on the permission type
column.
To better understand how these rules work an example is given below:
endpoint | role | application | permission |
---|---|---|---|
documents | NULL | ios-app | int 5 (0b0101) write: mine - read: mine |
documents | manager | backend | int 15 (0b1111) write: true - read: true |
payments | app | NULL | int 10 (0b1010) write: block - read: block |
events | reader | web-app | int 12 (0b1100) write: true - read: false |
- for every role (NULL) on
/documents
endpoint throughios-app
application only resources belonging to authenticated user may be read and written- users with
manager
role accessing/documents
withbackend
application are able to read and modify write every resource- users with
app
role accessing/payments
with every application are not allowed to read or write anything- users with
reader
role on/events
usginweb-app
application have a read-only access
See Administration /admin endpoint reference on how you may set these permission rules.
Keep in mind that altough powerful these rules must be applied very carefully like firewall network rules: it is quite easy to cause unintended side effects like blocking every operation or allow dangerous ones.
Object permissions¶
This feature is not yet implemented.
OAuth2¶
And what about OAuth2? At this moment BEdita4 is not (yet) an OAuth2 compliant server solution even if many OAuth2 concepts like access token and refresh token are already implemented.
Nonetheless integration with OAuth2 Authorization services is already in development stage. A server compliant implementation will follow.
Shell commands¶
A number of administration tasks can be performed via shell.
All shells and commands listed below can be invoked via a binary that’s located
in the bin/
directory. The basic syntax is:
$ bin/cake SHELL [COMMAND] [argument1 argument2 ...] [--option1 --option2=val ...]
Warning
Windows users must use bin\cake
instead (note the backslash).
For instance, if you want to run the setup command in the bedita shell you can run:
$ bin/cake bedita setup
If you want to run the init command in the db_admin shell with a couple of additional options you can run:
$ bin/cake db_admin init --no-force --seed
Note
Bash autocomplete might come in handy
bedita
¶
The bedita
shell is the first place you’ll ever want to search in
when you’re looking for a tool to perform an administrative task.
setup
¶
The setup
subcommand will lead you through the several steps required to
get a new BEdita instance to work.
You’ll be asked for database connection credentials, as well as user credentials for the first administrator user to be added to the system.
Additional steps, like checking for correct permissions on some folders, creating database tables, or checking if current database schema is up-to-date with the latest changes, are performed.
It is safe to run this command again once BEdita has been initialized.
-
-y
,
--yes
¶
Run in non-interactive mode.
-
--config-file
<path>
¶ Path to configuration file (default:
config/app.php
).
- Example
$ bin/cake bedita setup
db_admin
¶
The db_admin
shell is specialized in database administration tasks
for developers. As a user of BEdita, you’ll hardly need any of the commands
that this shell provides.
init
¶
This subcommand will initialize the database. The database connection must be already configured in order for this command to work. If you’re looking for a command-line wizard to guide you through all the steps required to install BEdita, please head to the setup command.
If any table is present in the database, you’ll be asked if you want your database to be weeped, or if you wish to abort the operation. When the schema has been created, you’ll also be asked if you want additional set of data to be loaded into the database. A minimum set of data is loaded regardless of your choice — this is required in order for BEdita to work.
This command can be run in unattended (non-interactive) mode by appending
command-line flags --[no-]force
and --[no-]seed
.
-
-f
,
--force
¶
Force removal of all tables in case target database is not empty.
-
--no-force
¶
Abort if target database is not empty.
-
-s
,
--seed
¶
Seed database with additional set of data.
-
--no-seed
¶
Don’t load any additional set of data. A minimum set of data required for BEdita to work is loaded anyway.
-
-c
<connection>
,
--connection
<connection>
¶ Database connection to be used (default:
default
).
- Example
$ bin/cake db_admin init --no-force --seed
check_schema
¶
This subcommand will perform checks on the current schema. This command is mostly useful when developing features that require making changes to the schema of BEdita’s database.
You’ll be notified of:
- migration history not in sync (schema not migrated to the latest available version)
- new changes (added or removed tables; changes to columns, indexes or constraints)
- naming that offends SQL conventions
This command exits with a non-zero exit code whenever current schema is not completely up-to-date and it follows SQL conventions, making it possible to employ this command in other automated tasks.
-
-c
<connection>
,
--connection
<connection>
¶ Database connection to be used (default:
default
).
- Example
$ bin/cake db_admin check_schema --verbose
jobs
¶
The jobs
shell is used to run asynchonous pending jobs stored in async_jobs table.
pending
¶
The pending
subcommand will launch all pending jobs waiting to be run.
A --limit
option may be used to set a maximum number of jobs to run.
-
-l
<limit>
,
--limit
<limit>
¶ Specify maximum number of jobs to run.
- Example
$ bin/cake jobs pending -l 10
run
¶
The run
subcommand will launch a single job from its UUID.
- Example
$ bin/cake jobs run 0660d795-d1bf-4ca0-9a05-2ee47943a658
spec
¶
The spec
shell can be used to automatically generate piece of documentation
for available API endpoints.
tree
¶
The tree
shell provides two tasks: check
and recover
.
check¶
It performs some object-aware checks such as:
- detect folders that are not in the tree
- detect ubiquitous folders
- detect non-folder objects that are roots
- detect non-folder objects that have descendants
- detect objects that are present more than once within same parent
Usage: bin/cake tree check
.
recover¶
It performs a basic recovery of a corrupt tree, by simply invoking CakePHP’s TreeBehavior::recover().
Usage: bin/cake tree recover
.
API reference¶
Introduction¶
BEdita 4 exposes a set of default endpoints to model and handle your data and to perform many other tyipical backend API operations. Throughout this documentation a description of each of these core endpoints is given.
All API examples are conventionally described using only HTTP requests and responses i.e. methods, url, headers and bodies. There are intentionally no API client language specific examples, so a basic knowledge of REST API concepts and HTTP is necessary.
Headers¶
Since the primary exchange format is JSON API its content negotiation rules are applied with some extensions.
Every request must contain an Accept
header with one of the following values:
Under special circumstances, and only for GET requests, also text/html
, application/xhtml+xml
, application/xhtml
and text/xhtml
may be valid, provided that HTML accept is configured (see Accept):
response will be an HTML version of the JSON body.
Main use is enable data navigation using a browser only in test and development enviroments.
Request containing input data like POST, PATCH and in some cases DELETE must also provide a Content-Type
header
where accepted values are:
Note: some special requests like POST /auth
or POST /signup
will require application/json
since not JSON API compliant,
see ref:auth-login or ref:signup-request for some examples.
- Other two important headers are:
Authorization
containing JWT token in the formBearer {token}
to identify the user performing the requestX-Api-Key
containing a token to identify the application performing the request
Typical requests will then be like:
POST /users HTTP/1.1 Accept: application/vnd.api+json Content-Type: application/vnd.api+json Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9......GET /users/123 HTTP/1.1 Accept: application/json X-Api-Key: WF0IjoxNDk1MTgzMjgzLCJuYmYiOjE0OTUxODMyODMsInN1
Dynamic endpoints¶
Every object type in BEdita defines a new endpoint dynamically, so there will be more dynamic
endpoints
in a typical project setup than the ones described here.
Some core object types like documents
or profiles
already provide their endpoints namely /documents
and /profiles
where through GET/POST/PATCH/DELETE methods you will be able to manage their data in the same way you can do in Create a user.
The only difference will be data structure and relations between objects that will of course vary from type to type hence from endpoint to endpoint.
Same will happen for new object types created dynamically in Object Types /model/object_types: a new dedicated endpoint is created,
so a new cats object type will automatically have its own /cats
endpoint.
Request and Response¶
Every BEdita 4 API call provides a standard request and response format layout: most of the endpoints are operations involving resources, think of classical CRUD operations for simplicity, and will have the same layout while some other operations like authentication are not and are slightly different.
Requests and responses will follow in general the JSON API specification as default format. There are few exceptions though: in some situations a very simple JSON object format is used as request body. See below for some examples.
Methods¶
HTTP methods used in API requests, following common REST best practices, are:
- GET to read resources or other meta informations
- POST to create new resources or for operations requiring input data like authentication
- PATCH to modify existing resources
- DELETE to delete existing resources
Request¶
DELETE and GET requests do not contain in general a body. Have a look at Common GET parameters for an overview of GET query strings.
In case of POST and PATCH methods instead a JSON API compliant format body part has to be sent by the client.
A minimal body request will have this layout:
{ "data": { "type": "documents", "attributes": { "title": "This is a title", "description": "Brief description or abstract", "body": "Document body with long text..." } } }
Very few operations like authentication require a flat key-value JSON object format like:
{ "username": "my-username", "password": "my-password" }
Response¶
Everytime a response contains a body it will be in JSON API compliant format.
A simple example of a GET /documents response will help us highlight some important concepts:
{ "data": [ { "id": "2", "type": "documents", "attributes": { "title": "My first document", "description": "A brief description", "body": "A long description", "status": "draft", "uname": "documents_my-first-document", "custom_field": 1234, "extra": { "ke1": "value1", "key2": [ "value2", "other value" ] }, "lang": "en" }, "meta": { "locked": false, "created": "2017-03-22T17:39:23+00:00", "modified": "2017-06-25T21:33:33+00:00", "published": null, "created_by": 3, "modified_by": 9 }, "links": { "self": "https://api.example.com/documents/2" }, "relationships": { "linked_to": { "links": { "related": "https://api.example.com/documents/2/linked_to", "self": "https://api.example.com/documents/2/relationships/linked_to" } } } }, { "id": "3", "type": "documents", "attributes": { } }, { } ], "links": { "self": "https://api.example.com/documents", "home": "https://api.example.com/home", "first": "https://api.example.com/documents", "last": "https://api.example.com/documents&page=5", "prev": null, "next": "https://api.example.com/documents&page=2" }, "meta": { "pagination": { "count": 92, "page": 1, "page_count": 5, "page_items": 20, "page_size": 20 } } }
This is the classical response in case of resources list. For a single resource response is very similar, but somehow reduced.
At the root level we have:
"data"
key will contain an array of resources, or a single resource when a single resource is requested like in GET /documents/2
"meta"
will contain metadata of single resources or of resource lists, mainly pagination info for lists
"links"
key appears in more than one place and will contain
"self"
(always present) with a link pointing to the resource containing it; it can be an array of resources, a single resource or a relationship"home"
link to Home Document /home endpoint"first"
"last"
"prev"
"next"
Pagination URIs to access pages in case of resources array
Giving a closer look at data key there are some important things to notice:
"id"
and"type"
must always be present,"type"
is the unique plural name of object types (like documents in this case) or of internal resources (like roles)
"attributes"
is for resource attributes, these are generally properties that a client application can modify, once permissions are granted, and contains
stantard core properties like
"title"
,"description"
,"lang"
or"body"
available for all object typescustom properties that belong to the current resource or object type only -
"custom_field"
in this examplespecial core properties available for all object types, with some specific meaning and usage:
"status"
may have onlyon
,draft
oroff
as values; it representes an editorial status, depending on client application settings objects with statusdraft
andoff
may not be visible"uname"
is the object unique name, a modifiable unique identifier of every object inside a project, represented by an URL-friendly string that may be derived from the"title"
or from other properties; upon creation auname
is automatically created and when a client application requests to create or modifiy this property the actual value may be changed by the system to avoid conflicts with an existing identicaluname
"extra"
is a special property where an application may put arbitrary data, use it as you wish… we don’t wanna know :)
"meta"
is for resource metadata, or properties that are not directly changeable by a client application, like:
"created"
,"modified"
and"published"
will show creation date, last modification date and date of publishing (whenstatus
changed toon
)"created_by"
and"modified_by"
display the id of the user that created the object and of the user that modified it for the last time"locked"
is a special property, when set to true an object is not deletable and itsstatus
anduname
cannot be modified
"links"
contains a"self"
link to representation of the object itself
"relationships"
displays links to read or manipulate related resources or objects, in general we have:
- core relations between objects and resources, like the one between user and role
- custom semantic relations between objects dynamically created using Relations /model/relations
Status codes¶
Obviously on every response a meaningful HTTP status code <https://en.wikipedia.org/wiki/List_of_HTTP_status_codes> is returned. The API consumer must always check it before reading the response body: especially to see if an error has occured.
Here a short list of the main status codes your client application will receive:
200 OK
- A successful GET or PATCH or POST that doesn’t result in a creation.201 Created
- Successful POST that results in a creation. A Location header pointing to the location of the new resource will be provided.204 No Content
- Successful not returning a body (like a DELETE).400 Bad Request
- The request is malformed.401 Unauthorized
- No authentication or invalid authentication details are provided.403 Forbidden
- Authentication succeeded but authenticated user doesn’t have access to the resource.404 Not Found
- A non-existent resource is requested.405 Method Not Allowed
- An HTTP method is being requested that isn’t allowed (like requesting a DELETE on Home Document /home).406 Not Acceptable
- A content negotiation error, content generated is not acceptable according to the Accept headers sent in the request - see Headers.415 Unsupported Media Type
- An unsupported or unknown content type was provided as part of the request.500 Internal Server Error
- Something bad happened, hope you don’t get a lot of them :)
Errors¶
In case of error this is the expected response body layout
{ "error": { "status": "401", "title": "Unauthorized", "code": "expired_token", "detail": "JWT access token has expired", "meta": { "trace": [ "#0 /var/www/bedita/plugins/BEdita/API/src/Auth/EndpointAuthorize.php(133): BEdita\\API\\Auth\\JwtAuthenticate->unauthenticated(Object(Cake\\Http\\ServerRequest), Object(Cake\\Http\\Response))", "#1 /var/www/bedita/plugins/BEdita/API/src/Auth/EndpointAuthorize.php(115): BEdita\\API\\Auth\\EndpointAuthorize->unauthenticate()", "#2 ...", "....", "#27 {main}" ] } }, "links": { "self": "https://api.example.com/documents", "home": "https://api.example.com/home" } }
Main difference between a failure and a successful response is the presence of the "error"
key providing:
"status"
response HTTP status code, for convenience"title"
short, human-readable summary of the problem that SHOULD NOT change from occurrence to occurrence of the problem"code"
API specific error code in string format, used by API consumer to better identify the problem occurred"detail"
human-readable explanation specific to this occurrence of the problem. Can be localized."meta"
will contain stacktrace in"trace"
only in debug mode"links"
with"self"
URL where error originated, and"home"
URL
Common GET parameters¶
Many HTTP query parameters in BEdita4 API GET method calls are common and may be used to manipulate API response and get only the data you need in your application.
Common parameters described here may be used in all API endpoints that handle entities/objects directly.
They are available in all endpoints except few special ones like: /auth
, /home
, /signup
and /status
. These endpoints are designed to handle particular use cases where lists of resources or objects are not directly handled.
Pagination¶
page
- select page number to retrievepage_size
- define page size; default page size and maximum possible size are defined via configuration parameter (defaults are 20 and 100) see Pagination
Examples:
/objects?page=2
- get page number 2/objects?page_size=42
- impose 42 as page size in response/objects?page_size=42&page=3
- get page number 3 using 42 as page size
In every GET response containing lists pagination data is available in a special section under meta / pagination
Example response:
Suppose we have 125 items available for our request, using 20 as
page_size
, selecting page number 7 we get{ "links": { ... }, "data": { ... }, "meta": { "pagination": { "count": 125, "page": 7, "page_count": 7, "page_items": 5, "page_size": 20 } } }
Where, as you may easily guess:
count
is the total number of items in responsepage
is the current page numberpage_count
is the total number of pagespage_items
is the numer of items displayed in this page (maximum ispage_size
)page_size
is the page size, max number of items for each page
Field selection¶
fields
- decide which fields are displayed in a response, comma separeted list
Search¶
q
orfilter[query]
- perform a natural language search using a filter, see Search Query filter below
Sort¶
sort
- sort items using a field in ascending or descending order, descending order is obtained prepending minus-
to field name
Examples:
/users?sort=username
return users alphabetically by username/users?sort=-username
return users alphabetically from last to first by username (reverse order)/roles?sort=-id
roles from last to first id
Filters¶
Filters are one the most powerful and versatile tools in BEdita API in order to retrieve only resources or objects you really need in your application.
Filter expressions¶
Generally every filter in a query string will have following syntax:
filter[{item}]={value}
the simplest form, a filter item should be equal to some valuefilter[{item}][{op}]={value}
is a logical operation on filter value, where{op}
can have following values:
neq
,ne
,'!=
or<>
negation operator, meaning that filter item shoud not be equal to{{value}}
lt
or<
less than operator, meaning that filter item shoud be less than{{value}}
lte
,le
or<=
less than or equal operator; filter item shoud be less than or equal to{{value}}
gt
or>
greater than operator; filter item shoud be greater than{{value}}
gte
,ge
or>=
greater than or equal operator; filter item should be greater than or equal to{{value}}
filter[{item}][]={value}
array sintax: add a value to an array representing possible values of a filter item
Filter expressions can be combined using &
separator as many times as you want, limited only by the URL size.
Too many filter
combinations may of course result in unwanted or meaningless results, use them with caution :)
Field filter¶
The simplest and most common filter: retrieve only resources that have a field equal to some value, or greater/less than some value.
Examples:
/users?filter[name]=Gustavo
get users that have Gustavo as firstname
/objects?filter[id][gt]=100
return users withid
greater than 100
Search Query filter¶
Simple text search may be performed with a query filter
/objects?filter[query]=gustavo
get objects that have gustavo in some of their textual fields/objects?q=gustavo
convience alias for the preceeding filter query -filter[query]=..
orq=..
are identical
Note: currently only raw text search is performed, more sophisticated actual natural language search will be available in a future release
Type filter¶
Type filters are used to select some object types, typically used in /objects
endpoint
/objects?filter[type]=events
select only objects of type events/objects?filter[type][ne]=documents
all object types except documents/objects?filter[type][]=locations&filter[type][]=profiles
select only locations and profiles
Geo filter¶
Geo filters are able to retrieve results on objects of type location
or on types extending locations
using geo coordinates.
/locations?filter[geo][center]=44.4944183,11.3464055
retrieve locations ordered by proximity to a givencenter
point expressed in terms of latitude and longitude; each item will show inmeta.extra.distance
the distance in meters to thecenter
point/locations?filter[geo][center]=44.4944183,11.3464055&filter[geo][radius]=5000
same as the above filter, but results are limited in a radius of 5km
Note: in order to work this filter requires that the underlying database supports geo-spatial functions like ST_GeomFromText
, this is true for MySQL 5.7 or PostGIS for instance.
Date ranges filter¶
Date ranges are entities used in some objects like events
to indicate start and end dates.
With this filter you are able to retrieve objects using conditions on date time intervals.
/events?filter[date_ranges][start_date][gt]=2017-08-01
events starting after2017-08-01
/events?filter[date_ranges][end_date][le]=2017-08-15
events with end date lesser than or equal to2017-08-15
/events?filter[date_ranges][start_date][gt]=2017-07-01&filter[date_ranges][end_date][lt]=2017-07-30
events starting and ending between2017-07-01
and2017-07-30
Home Document /home
¶
/home
endpoint returns basic information about available API resources
Inspired by Home Document for HTTP APIs IETF draft http://mnot.github.io/I-D/json-home/ its purpose is to show avilable endpoints, content type formats and HTTP methods
This information depends on user permissions and endpoint configuration.
Example request:
GET /home HTTP/1.1 Host: example.org Accept: application/jsonExample response:
HTTP/1.1 200 Success Host: example.org Content-Type: application/json { "links": { "self": "http://example.org/home", "home": "http://example.org/home" }, "meta": { "resources": { "/users": { "href": "http://example.org/users", "hints": { "allow": [ "GET" ], "formats": [ "application/json", "application/vnd.api+json" ] }, "/roles": { "href": "http://example.org/roles", "hints": { "allow": [ "GET" ], "formats": [ "application/json", "application/vnd.api+json" ] }, } } }
Authentication /auth
¶
/auth
endpoint is responsible for login related actions.
With it a user is able to login and to get user login data.
Different actions are supported:
- standard login - providing username and password and without
Authorization
header - token renew - with
Authorization
header and without username and password - whoami - get logged user profile data
- profile update - logged user profile data
- credential change request - request to update password
- credential change update - actual password update action using secret hash
Login¶
-
POST
/auth
¶ Perform login.
Form Parameters: - username – Username of user to be logged in. To be omitted when renewing token.
- password – Password of user to be logged in. To be omitted when renewing token.
Request Headers: - Authorization – (Optional) Use only in token renew, prefixed with
Bearer
.
Status Codes: - 200 OK – Login successful.
- 401 Unauthorized – Unauthorized user, or invalid renew token.
Response JSON Object: - meta.jwt – The JSON Web Token to be used to authenticate (in header Authorization).
- meta.renew – The renew token, to be used to reiterate login process when JWT expires.
Example request (login with username and password): since this is not a JSON API request you MUST use
Content-Type: application/json
orContent-Type: application/x-www-form-urlencoded
, see example below.POST /auth HTTP/1.1 Host: example.com Accept: application/vnd.api+json Content-Type: application/json { "username" : "bedita", "password" : "bedita" }
Same request using classic
Content-Type: application/x-www-form-urlencoded
POST /auth HTTP/1.1 Host: example.com Accept: application/vnd.api+json Content-Type: application/x-www-form-urlencoded username=bedita&password=bedita
Example response:
HTTP/1.1 200 OK Content-Type: application/vnd.api+json { "links": { "self": "http://example.com/auth", "home": "http://example.com/home" }, "meta": { "jwt": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6MSwidXNlcm5hbWUiOiJiZWRpdGEiLCJibG9ja2VkIjpmYWxzZSwibGFzdF9sb2dpbiI6IjIwMTYtMDgtMDFUMTM6MTk6MzkrMDAwMCIsImxhc3RfbG9naW5fZXJyIjpudWxsLCJudW1fbG9naW5fZXJyIjowLCJjcmVhdGVkIjoiMjAxNi0wOC0wMVQxMzoxOToyOSswMDAwIiwibW9kaWZpZWQiOiIyMDE2LTA4LTAxVDEzOjE5OjI5KzAwMDAiLCJyb2xlcyI6W10sImlzcyI6Imh0dHA6XC9cLzEwLjAuODMuNDo4MDgwIiwiaWF0IjoxNDcwMDU4NTE3LCJuYmYiOjE0NzAwNTg1MTcsImV4cCI6MTQ3MDA2NTcxN30.rGcCEKiYjETnkaKVgG5-gJxIMXALVaZ4MeV5aKbWtQE", "renew": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC8xMC4wLjgzLjQ6ODA4MCIsImlhdCI6MTQ3MDA1ODUxNywibmJmIjoxNDcwMDU4NTE3LCJzdWIiOjEsImF1ZCI6Imh0dHA6XC9cLzEwLjAuODMuNDo4MDgwXC9hdXRoIn0.mU3QToPvc0uY-XQRhDA1F2hfpRjjT2ljSerKQygk2T8" } }
Example request (token renewal):
POST /auth HTTP/1.1 Host: example.com Accept: application/vnd.api+json Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC8xMC4wLjgzLjQ6ODA4MCIsImlhdCI6MTQ3MDA1ODUxNywibmJmIjoxNDcwMDU4NTE3LCJzdWIiOjEsImF1ZCI6Imh0dHA6XC9cLzEwLjAuODMuNDo4MDgwXC9hdXRoIn0.mU3QToPvc0uY-XQRhDA1F2hfpRjjT2ljSerKQygk2T8
Example response: (same as above, with new JWT and renew token)
Who Am I?¶
-
GET
/auth/user
¶ Get logged user profile data.
Request Headers: - Authorization – Use token prefixed with
Bearer
.
Status Codes: - 200 OK – Get operation successful.
- 401 Unauthorized – Unauthorized user, user not logged.
Response JSON Object: - data – User profile data
Example request
{token} is JWT token from previuos login and renew examples:
GET /auth/user HTTP/1.1 Host: example.com Accept: application/vnd.api+json Authorization: Bearer {token}
Example response:
HTTP/1.1 200 OK Content-Type: application/vnd.api+json { "data": { "id": "2", "type": "users", "attributes": { "username": "gustavo", "blocked": false, "last_login": "2016-10-06T08:17:36+00:00", "last_login_err": null, "num_login_err": 0, "name": "Gustavo", "surname": "Supporto" } }, "links": { "self": "http://example.com/auth/user", "home": "http://example.com/home" }, }
Note: some fields in “attributes” are missing for brevity.
- Authorization – Use token prefixed with
Update user profle¶
-
PATCH
/auth/user
¶ Update logged user profile data with some restrictions. For basic security reasons some fields are not directly changeable: username, email and password. Only exception: password may be changed if current valid password is provided in old_password field.
Request Headers: - Authorization – Use token prefixed with
Bearer
.
Status Codes: - 200 OK – Get operation successful.
- 401 Unauthorized – Unauthorized user, user not logged.
Response JSON Object: - data – User profile data
Example request
{token} is JWT token from previuos login and renew examples:
PATCH /auth/user HTTP/1.1 Host: example.com Authorization: Bearer {token} Accept: application/vnd.api+json Content-Type: application/json { "city" : "Bologna", "country" : "Italy" }
Example response:
HTTP/1.1 200 OK Content-Type: application/vnd.api+json { "data": { "id": "2", "type": "users", "attributes": { "username": "gustavo", "name": "GUstavo", "surname": "Supporto" "city" : "Bologna", "country" : "Italy" } }, "links": { "self": "http://example.com/auth/user", "home": "http://example.com/home" }, }
Note: some fields in “attributes” are missing for brevity.
Example of a password change request
PATCH /auth/user HTTP/1.1 Host: example.com Authorization: Bearer {token} Accept: application/vnd.api+json Content-Type: application/json { "password" : "a new super strong password", "old_password" : "my current password" }
- Authorization – Use token prefixed with
Credentials change¶
Authentications credential change works in two steps:
- a credential change request action
- an actual credential change using a secret hash
Only use case currently supported is password
change.
After a request action an email is sent to requesting user containing a URL with a secret hash to actually perform the change.
-
POST
/auth/change
¶ Request a credential change.
Form Parameters: - contact – Email of user requesting credendials change.
- change_url – Change URL that will be sent via email.
Status Codes: - 204 No Content – No content on operation success.
- 400 Bad Request – On malformed or missing input data.
- 404 Not Found – If no user is found.
Example request: Since this is not a JSON API request you MUST use
Content-Type: application/json
POST /auth/change HTTP/1.1 Content-Type: application/json { "contact": "{my email}", "change_url": "{change url}" }
A change_url
is required in order to create the URL that will be sent to the user in the form
{change_url}?uuid={uuid}
where {uuid}
is a system generated hash that will expire after 24h.
In your change_url
page you will have to read the uuid
query parameter and proceed to actual change performing the following request.
-
PATCH
/auth/change
¶ Perform a credential (password) change.
Form Parameters: - uuid – Secret UUID sent via email in
change_url
. - password – New user password.
- login – Optional boolean parameter, if
true
a login is also performed.
Status Codes: - 200 OK – On operation success.
- 400 Bad Request – On malformed or missing input data.
- 404 Not Found – Not found if provided UUID is incorrect or expired.
Example request: Since this is not a JSON API request you MUST use
Content-Type: application/json
PATCH /auth/change HTTP/1.1 Content-Type: application/json { "uuid": "{uuid}", "password": "{new password}", "login": true }
- uuid – Secret UUID sent via email in
Response will contain user data as in previous Who Am I? request.
If "login"
is true a login is also performed and JWT access token and refresh token tokens are returned in "meta"
section for immediate use. This key is optional, if missing "login": false
is assumed.
Users /users
¶
You can manage user data by using /users
endpoint.
It provides users management: creation, data retrieval, modification, removal and roles associations.
Create a user¶
You can create user data by using POST /users
endpoint.
Users data must be specified inside body JSON data.
Request body structure is:
{
"data": {
"type": "users",
"attributes": {}
}
}
-
POST
/users
¶
Example request (create user john doe):
POST /users HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "users",
"attributes": {
"username": "johndoe",
"password": "j04nd0e",
"uname": "johndoe"
}
}
}
Example above show all required attributes.
Expected response is HTTP/1.1 201 OK
, with application/vnd.api+json
body data representing the user just created.
When user already exists or data is not valid (i.e. data lacks of required fields), POST fails and response is 400 Bad Request - Invalid data
.
Successful response example follows:
HTTP/1.1 201 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": 19283,
"type": "users",
"attributes": {
"username": "johndoe",
"password": "j04nd0e",
"uname": "johndoe",
"created_by": 1,
"modified_by": 1,
"created": "2016-12-13T12:08:02+00:00",
"modified": "2016-12-13T12:08:02+00:00"
},
"relationships": {
"roles": {
"links": {
"related": "http://example.com/users/19283/roles",
"self": "http://example.com/users/19283/relationships/roles"
}
}
}
},
"links": {
"self": "http://example.com/users",
"home": "http://example.com/home"
}
}
data.attributes
object contains more internal attributes.
Get user data¶
You can obtain user data by using GET /users
and GET /users/(user_id)
endpoint.
-
GET
/users
¶
It returns a collection of users:
- use
id
query string parameter to retrieve a single user by id- use
id
query string parameter androles
token to retrieve user roles by user id
-
GET
/users/
(user_id)¶
Example request (get users):
GET /users HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": 19283,
"type": "users",
"attributes": {
"username": "johndoe",
"name": "john",
"surname": "doe"
}
}
],
"links": {
"self": "http://example.com/users/19283",
"home": "http://example.com/home",
"first": "http://example.com/users",
"last": "http://example.com/users",
"prev": null,
"next": null
},
"meta": {
"pagination": {
"count": 1,
"page": 1,
"page_count": 1,
"page_items": 1,
"page_size": 20
}
}
}
data
is an array of objects; in this example, you see only one.
data.attributes
object contains more internal attributes.
-
GET
/users/
(user_id)/roles
¶
You can obtain user roles by using GET /users/(user_id)/roles
endpoint.
Example request (get user johndoe roles):
GET /users/19283/roles HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": "7",
"type": "roles",
"attributes": {
"name": "dummy role",
"description": null,
"unchangeable": false,
"created": "2016-12-13T11:28:32+00:00",
"modified": "2016-12-13T11:28:32+00:00"
},
"links": {
"self": "http://example.com/roles/7"
},
"relationships": {
"users": {
"links": {
"related": "http://example.com/roles/7/users",
"self": "http://example.com/roles/7/relationships/users"
}
}
}
}
],
"links": {
"self": "http://example.com/users/19283/roles",
"home": "http://example.com/home",
"first": "http://example.com/users/19283/roles",
"last": "http://example.com/users/19283/roles",
"prev": null,
"next": null
},
"meta": {
"pagination": {
"count": 1,
"page": 1,
"page_count": 1,
"page_items": 1,
"page_size": 20
}
}
}
Modify a user¶
You can modify a user by using PATCH /users/(user_id)
endpoint.
-
PATCH
/users/
(user_id)¶
Example request (modify user john doe):
In this example, purpose is modifying ‘johndoe’ user’s name and surname from ‘john doe’ to ‘Johnny Doe’.
PATCH /users/19283 HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"id": 19283,
"type": "users",
"attributes": {
"name" : "Johnny",
"surname" : "Doe"
}
}
}
Response 200 OK is expected.
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": 19283,
"type": "users",
"attributes": {
"username": "johndoe",
"name": "Johnny",
"surname": "Doe"
},
"relationships": {
"roles": {
"links": {
"related": "http://example.com/users/19283/roles",
"self": "http://example.com/users/19283/relationships/roles"
}
}
}
},
"links": {
"self": "http://example.com/users/19283",
"home": "http://example.com/home"
}
}
data.attributes
object contains more internal attributes.
Remove a user¶
You can delete a user by using DEL /users/(user_id)
endpoint.
-
DELETE
/users/
(user_id)¶
Example request (delete user john doe):
Note: in this example user id is 19283.
DELETE /users/19283 HTTP/1.1
Host: example.com
Expected response is 204 No Content
. When user is not found, response is 404 Not Found
.
HTTP/1.1 204 No Content
Add a role¶
You can add a role by using POST /users/(user_id)/relationships/roles
endpoint.
(user_id)
is a placeholder for user object id.
You specify role id inside JSON body passed to request.
-
POST
/users/
(user_id)/relationships/roles
¶
Example request (add role 7 to john doe user):
In this example, purpose is adding a role (id 7) to ‘johndoe’ user (id 19283).
POST /users/19283/relationships/roles HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "roles",
"id": 7
}
}
Response 200 OK is expected.
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"links": {
"self": "http://example.com/users/19283/relationships/roles",
"home": "http://example.com/home"
}
}
Remove a role¶
You can remove a role by using DELETE /users/(user_id)/relationships/roles
endpoint.
(user_id)
is a placeholder for user object id.
You specify role id inside JSON body passed to request.
-
DELETE
/users/
(user_id)/relationships/roles
¶
Example request (remove role 7 to john doe user):
In this example, purpose is removing a role (id 7) from ‘johndoe’ user (id 19283).
DELETE /users/19283/relationships/roles HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "roles",
"id": 7
}
}
Response 200 OK is expected.
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"links": {
"self": "http://example.com/users/19283/relationships/roles",
"home": "http://example.com/home"
}
}
Roles /roles
¶
You can manage user roles by using /roles
endpoint.
It provides user roles management: creation, data retrieval, modification, removal and user association.
Create a role¶
You can create role data by using POST /roles
endpoint.
Roles data must be specified inside body JSON data.
Request body structure is:
{
"data": {
"type": "roles",
"attributes": {}
}
}
-
POST
/roles
¶
Example request (create sample role):
POST /users HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "roles",
"attributes": {
"name": "sample role"
}
}
}
Expected response is HTTP/1.1 201 OK
, with application/vnd.api+json
body data representing the role just created.
When role already exists or data is not valid (i.e. data lacks of required fields), POST fails and response is 400 Bad Request - Invalid data
.
Successful response example follows:
HTTP/1.1 201 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "7",
"type": "roles",
"attributes": {
"name": "sample role",
"created": "2016-12-13T13:38:17+00:00",
"modified": "2016-12-13T13:38:17+00:00"
},
"relationships": {
"users": {
"links": {
"related": "http://example.com/roles/1/users",
"self": "http://example.com/roles/7/relationships/users"
}
}
}
},
"links": {
"self": "http://example.com/roles",
"home": "http://example.com/home"
}
}
Get role data¶
You can obtain role data by using GET /roles
and GET /roles/(role_id)
endpoint.
-
GET
/roles
¶
It returns a collection of roles:
- use
id
query string parameter to retrieve a single role by id- use
id
query string parameter andusers
token to retrieve users by role id
-
GET
/roles/
(role_id)¶
Example request (get roles):
GET /roles HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": 7,
"type": "roles",
"attributes": {
"name": "sample role",
"description": null,
"unchangeable": false,
"created": "2016-12-13T13:38:17+00:00",
"modified": "2016-12-13T13:38:17+00:00"
},
"links": {
"self": "http://example.com/roles/7"
},
"relationships": {
"users": {
"links": {
"related": "http://example.com/roles/7/users",
"self": "http://example.com/roles/7/relationships/users"
}
}
}
}
],
"links": {
"self": "http://example.com/roles",
"home": "http://example.com/home",
"first": "http://example.com/roles",
"last": "http://example.com/roles",
"prev": null,
"next": null
},
"meta": {
"pagination": {
"count": 1,
"page": 1,
"page_count": 1,
"page_items": 1,
"page_size": 20
}
}
}
-
GET
/roles/
(role_id)/users
¶
You can obtain role users by using GET /roles/(role_id)/users
endpoint.
Example request (get users by role ‘sample role’, id 7):
GET /roles/7/users HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": "19283",
"type": "users",
"attributes": {
"username": "johndoe",
},
"links": {
"self": "http://example.com/users/19283"
},
"relationships": {
"roles": {
"links": {
"related": "http://example.com/users/19283/roles",
"self": "http://example.com/users/19283/relationships/roles"
}
}
}
}
],
"links": {
"self": "http://example.com/roles/7/users",
"home": "http://example.com/home",
"first": "http://example.com/roles/7/users",
"last": "http://example.com/roles/7/users",
"prev": null,
"next": null
},
"meta": {
"pagination": {
"count": 1,
"page": 1,
"page_count": 1,
"page_items": 1,
"page_size": 20
}
}
}
Modify a role¶
You can modify a role by using PATCH /roles/(role_id)
endpoint.
-
PATCH
/roles
¶
Example request (modify role ‘sample role’):
In this example, purpose is modifying ‘sample role’ name to ‘Dummy Role’.
PATCH /roles/7 HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"id": 7,
"type": "roles",
"attributes": {
"name" : "Dummy Role"
}
}
}
Response 200 OK is expected.
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": 7,
"type": "roles",
"attributes": {
"name": "Dummy Role",
"description": null,
"unchangeable": false,
"created": "2016-12-13T13:38:17+00:00",
"modified": "2016-12-13T14:02:37+00:00"
},
"relationships": {
"users": {
"links": {
"related": "http://example.com/roles/7/users",
"self": "http://example.com/roles/7/relationships/users"
}
}
}
},
"links": {
"self": "http://example.com/roles/7",
"home": "http://example.com/home"
}
}
Remove a role¶
You can delete a role by using DEL /roles/(role_id)
endpoint.
-
DELETE
/roles
¶
Example request (delete role ‘Sample Role’, id 7):
DELETE /roles/7 HTTP/1.1
Host: example.com
Expected response is 204 No Content
. When role is not found, response is 404 Not Found
.
HTTP/1.1 204 No Content
Add a user role¶
You can add a role to a user by using POST /roles/(role_id)/relationships/users
endpoint.
(role_id)
is a placeholder for role id.
You specify user id inside JSON body passed to request.
-
POST
/roles/
(role_id)/relationships/users
¶
Example request (add role 7 to john doe user, id 19283):
In this example, purpose is adding a role (id 7) to ‘johndoe’ user (id 19283).
POST /roles/7/relationships/users HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "users",
"id": 19283
}
}
Response 200 OK is expected.
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"links": {
"self": "http://example.com/roles/7/relationships/users",
"home": "http://example.com/home"
}
}
Remove a user role¶
You can remove a role from a user by using DELETE /roles/(role_id)/relationships/users
endpoint.
(role_id)
is a placeholder for role id.
You specify user id inside JSON body passed to request.
-
DELETE
/roles/
(role_id)/relationships/users
¶
Example request (remove role 7 from john doe user, id 19283):
In this example, purpose is removing a role (id 7) from ‘johndoe’ user (id 19283).
DELETE /roles/7/relationships/users HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "users",
"id": 19283
}
}
Response 200 OK is expected.
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"links": {
"self": "http://example.com/roles/7/relationships/users",
"home": "http://example.com/home"
}
}
Objects /objects
¶
/objects
base endpoint provides a set of methods dedicated to abstract objects management.
objects
is an abstract model for BEdita. Every object created through data modelling are instances of objects
.
You can use GET /objects
when you want to retrieve data for objects whose types extend objects
.
This endpoint provides:
- get a list of objects (multiple types at the same time), using filters
- retrieve data of a single object by unique identifier (allowed, but you can also use
GET /{objectTypeName}/{objectId}
)- remove an object (allowed, but discouraged: use
DELETE /{objectTypeName}/{objectId}
instead)
To create, modify or delete data for objects whose types extend objects
, you use dedicated endpoint /{objectTypeName}
, created through modelling (for more details, see Model /model),
({objectTypeName}
is a placeholder for a non-abstract object type):
POST /{objectTypeName}
: create dataPATCH /{objectTypeName}/{objectId}
: update dataDELETE /{objectTypeName}/{objectId}
: move to trashcanDELETE /trash/{objectId}
: remove from trashcan
Note: in the following sections, {objectId}
and (object_id)
are sample placeholders for the unique identifier of the object, typically an integer number.
Other placeholders used in this page are {objectTypeName}
, {status}
, {uname}
, {title}
, {createdDate}
, {modifiedDate}
, {createdBy}
, {modifiedBy}
, {status}
, {revisionNumber}
etc.
In the examples, response data contains a reduced number of fields, for better readability.
Get a collection of objects¶
-
GET
/objects
¶
The /objects
endpoint can be used to retrieve a collection of objects of different types.
Available filters are Type filter, Field filter and Search Query filter.
Example request (get all objects):
GET /objects HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": "{objectId1}",
"type": "{objectTypeName1}",
"attributes": {
"status": "{status1}",
"uname": "{uname1}",
"title": "{title1}"
},
"meta": {
"created": "{createdDate1}",
"modified": "{modifiedDate1}",
"created_by": "{createdBy1}",
"modified_by": "{modifiedBy1}"
},
"links": {
"self": "https://example.com/{objectTypeName1}/{objectId1}"
},
"relationships": {
"{relationName1}": {
"links": {
"related": "https://example.com/{objectTypeName1}/{objectId1}/{relationName1}",
"self": "https://example.com/{objectTypeName1}/{objectId1}/relationships/{relationName1}"
}
},
"{relationName2}": {
"links": {
"related": "https://example.com/{objectTypeName1}/{objectId1}/{relationName2}",
"self": "https://example.com/{objectTypeName1}/{objectId1}/relationships/{relationName2}"
}
},
}
},
{
"id": "{objectId2}",
"type": "{objectTypeName2}",
"attributes": {
"status": "{status2}",
"uname": "{uname2}",
"title": "{title2}"
},
"meta": {
"created": "{createdDate2}",
"modified": "{modifiedDate2}",
"created_by": "{createdBy2}",
"modified_by": "{modifiedBy2}"
},
"links": {
"self": "https://example.com/{objectTypeName2}/{objectId2}"
},
"relationships": {
"{relationName1}": {
"links": {
"related": "https://example.com/{objectTypeName2}/{objectId2}/{relationName1}",
"self": "https://example.com/{objectTypeName2}/{objectId2}/relationships/{relationName1}"
}
},
"{relationName2}": {
"links": {
"related": "https://example.com/{objectTypeName2}/{objectId2}/{relationName2}",
"self": "https://example.com/{objectTypeName2}/{objectId2}/relationships/{relationName2}"
}
},
}
},
],
"links": {
"self": "https://example.com/objects",
"home": "https://example.com/home",
"first": "https://example.com/objects",
"last": "https://example.com/objects?page=1",
"prev": null,
"next": "https://example.com/objects?page=1"
},
"meta": {
"pagination": {
"count": 2,
"page": 1,
"page_count": 2,
"page_items": 20,
"page_size": 20
},
"schema": {
"{objectTypeName1}": {
"$id": "https://example.com/model/schema/{objectTypeName1}",
"revision": "{revisionNumber1}"
},
"{objectTypeName2}": {
"$id": "https://example.com/model/schema/{objectTypeName2}",
"revision": "{revisionNumber2}"
}
}
}
}
In the example above, api returns 2 objects ({objectId1}
and {objectId2}
), of different types ({objectTypeName1}
and {objectTypeName2}
).
{relationName1}
and {relationName2}}
are the relationships defined in the example.
For more details, see Response
Get single object¶
-
GET
/objects/
(object_id)¶
Get details for a single object by its unique identifier.
Example request (get a single object by id):
GET /objects/{objectId} HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "{objectId}",
"type": "{objectTypeName}",
"attributes": {
"status": "{status}",
"uname": "this-is-a-test-object",
"title": "this is a test object"
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}",
"created_by": "{createdBy}",
"modified_by": "{modifiedBy}"
},
"relationships": {
"parents": {
"links": {
"related": "https://example.com/{objectTypeName}/{objectId}/parents",
"self": "https://example.com/{objectTypeName}/{objectId}/relationships/parents"
}
}
}
},
"links": {
"self": "https://example.com/objects/{objectId}",
"home": "https://example.com/home"
},
"meta": {
"schema": {
"documents": {
"$id": "https://example.com/model/schema/{objectTypeName}",
"revision": "{revisionNumber}"
}
}
}
}
Remove objects¶
-
DELETE
/objects/
(object_id)¶
You can move object to trashcan (soft delete) using DELETE /objects/{objectId}
, with empty body.
You can either do the same task using DELETE /{objectTypeName}/{objectId}
, with empty body.
When delete succeeds, 204 No Content
response is returned.
Example request (delete an object):
DELETE /objects/{objectId} HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 204 No Content
Streams /streams
¶
/streams
endpoint provides file upload, CRUD operations on streams and association of media (i.e. image) to a stream.
Main responsabilities of this endpoint are:
- file upload and new stream creation
- get a collection of streams, using usual Filters
- link a stream to a media object
- remove an existing stream
Upload file and create stream¶
-
POST
/streams/upload/{file_name}
¶ Upload a file, passing it as binary in body.
Example request:
POST /streams/upload/myfile.jpg HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: image/jpeg
Example response:
HTTP/1.1 201 Created
Content-Type: application/json
{
"data": {
"id": "991c27b0-9e3a-43ca-a9dd-6c1f831eca31",
"type": "streams",
"attributes": {
"file_name": "myfile.jpg",
"mime_type": "image/png"
},
"meta": {
},
"relationships": {
"object": {
"links": {
"related": "http://example.com/streams/991c27b0-9e3a-43ca-a9dd-6c1f831eca31/object",
"self": "http://example.com/streams/991c27b0-9e3a-43ca-a9dd-6c1f831eca31/relationships/object"
}
}
},
"links": {
"self": "http://example.com/streams/upload/myfile.jpg",
"home": "http://example.com/home"
},
"meta": {
"schema": {
"streams": {
"$id": "http://example.com/model/schema/streams",
"revision": "627017864"
}
}
}
}
}
Get a single stream¶
-
GET
/streams/
(stream_id)¶ Get data for a stream by id.
Example request:
GET /streams/302f6fdd-bf31-4cd7-91db-baf45fb93de0 HTTP/1.1
Host: example.com
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "302f6fdd-bf31-4cd7-91db-baf45fb93de0",
"type": "streams",
"attributes": {
"file_name": "myfile.jpg",
"mime_type": "image/png"
},
"meta": {
"...": "..."
},
"relationships": {
"object": {
"links": {
"related": "http://example.com/streams/302f6fdd-bf31-4cd7-91db-baf45fb93de0/object",
"self": "http://example.com/streams/302f6fdd-bf31-4cd7-91db-baf45fb93de0/relationships/object"
}
}
}
},
"links": {
"..." : "..."
},
"meta": {
"..." : "..."
}
}
Get a collection of streams¶
The /streams
endpoint retrieve a collection of streams.
-
GET
/streams
¶ It returns a collection of streams:
- if called with
id
query string parameter the response will contain a collection of the streams requested - else it returns a paginated list of streams that are descendants of the
related publication configured in
app/config/frontend.ini.php
.
- if called with
Example request:
GET /streams HTTP/1.1
Host: example.com
Example response:
For readability the fields of objects are limited to “title” but they are similar toGET /streams/(stream_id)
example
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": "0dae7797-5cc8-4d63-8a11-98664dd675b0",
"type": "streams",
"attributes": {
"file_name": "myfile.png",
"mime_type": "image/png"
},
"meta": {
"..." : "..."
},
"links": {
"..." : "..."
},
"relationships": {
"..." : "..."
}
}
],
"links": {
"..." : "..."
},
"meta": {
"..." : "..."
},
}
Create an image¶
POST /images
provides image creation.
-
POST
/images
¶
Example request:
POST /images HTTP/1.1
Host: example.com
Content-Type:application/vnd.api+json
Accept: application/vnd.api+json
Example body:
{
"data": {
"type": "images",
"attributes": {
"title": "My media"
}
}
}
Example response:
HTTP/1.1 201 Created
Content-Type: application/vnd.api+json
{
"data": {
"id": "6",
"type": "images",
"attributes": {
"..." : "..."
},
"meta": {
"..." : "..."
},
"relationships": {
"..." : "..."
}
},
"links": {
"..." : "..."
},
"meta": {
"..." : "..."
}
}
Link stream to media¶
Link a stream to a media using PATCH /streams/{stream_uuid}/relationships/object
.
-
PATCH
/streams/{stream_uuid}/relationships/object
¶
Example request:
PATCH /streams/302f6fdd-bf31-4cd7-91db-baf45fb93de0/relationships/object HTTP/1.1
Host: example.com
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json
Example body:
{
"data": {
"id": "6",
"type": "images"
}
}
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"links": {
"self": "http://example.com/streams/302f6fdd-bf31-4cd7-91db-baf45fb93de0/relationships/object",
"home": "http://example.com/home"
}
}
Get an image¶
-
GET
/images/{image_id}
¶ Get detailed data for an image by id.
Example request:
GET /images/6 HTTP/1.1
Host: example.com
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "6",
"type": "images",
"attributes": {
"..." : "..."
},
"meta": {
"..." : "..."
},
"relationships": {
"streams": {
"data": [
{
"id": "302f6fdd-bf31-4cd7-91db-baf45fb93de0",
"type": "streams"
}
],
"links": {
"related": "http://example.com/images/6/streams",
"self": "http://example.com/images/6/relationships/streams"
}
}
}
},
"links": {
"..." : "..."
},
"meta": {
"..." : "..."
},
"included": [
{
"id": "302f6fdd-bf31-4cd7-91db-baf45fb93de0",
"type": "streams",
"attributes": {
"..." : "..."
},
"meta": {
"..." : "..."
},
"links": {
"..." : "..."
},
"relationships": {
"..." : "..."
}
}
]
}
Folders /folders
¶
Folders endpoint provides folders data, structure and contents management: search, create, modify folders, add or remove contents, reorder data, organize folders tree, etc.
Mainly it provides:
- manage data: retrieve, create, modify or remove folders
- manage tree: retrieve parent folder data, set or remove a parent folder association
- manage contents: add, remove, search/get, reorder folders contents
Note: in the following paragraphs, (folder_id)
and {folderId}
are sample placeholders for the unique identifier of the folder, typically an integer number;
other placeholders used in this page are {parentId}
, {relatedId}
, {rightId}
, {leftId}
, {createdDate}
, {modifiedDate}
and {revisionNumber}
.
Folders data¶
Get data¶
-
GET
/folders
¶
All folders retrieval is performed through GET /folders
.
Folders endpoint provides several filters to obtain data.
Usual filters, like Field filter or Search Query filter, are available; extra filters are roots
and parent
.
Filter roots
provides a method to fetch only root folders.
Usage: GET /folders?filter[roots]
.
Filter parent
allows you to obtain folders whose parent is a specific folder, by ID.
Usage: GET /folders?filter[parent]={parentId}
.
When the api call is successfull, response with 200 OK
http status code is returned.
Example request (get root folders):
GET /folders?filter[roots] HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": "{folderId1}",
"type": "folders",
"attributes": {
"title": "Sample root",
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}",
"path": "/{folderId}"
},
"links": {
"self": "https://example.com/folders/{folderId1}"
},
"relationships": {
"children": {
"links": {
"related": "https://example.com/folders/{folderId1}/children",
"self": "https://example.com/folders/{folderId1}/relationships/children"
}
},
"parent": {
"links": {
"related": "https://example.com/folders/{folderId1}/parent",
"self": "https://example.com/folders/{folderId1}/relationships/parent"
}
}
}
},
{
"id": "{folderId2}",
"type": "folders",
"attributes": {
"title": "Root Folder",
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}",
"path": "/{folderId2}"
},
"links": {
"self": "https://example.com/folders/{folderId2}"
},
"relationships": {
"children": {
"links": {
"related": "https://example.com/folders/{folderId2}/children",
"self": "https://example.com/folders/{folderId2}/relationships/children"
}
},
"parent": {
"links": {
"related": "https://example.com/folders/{folderId2}/parent",
"self": "https://example.com/folders/{folderId2}/relationships/parent"
}
}
}
}
],
"links": {
"self": "https://example.com/folders?filter%5Broots%5D=",
"home": "https://example.com/home",
"first": "https://example.com/folders?filter%5Broots%5D=",
"last": "https://example.com/folders?filter%5Broots%5D=",
"prev": null,
"next": null
},
"meta": {
"pagination": {
"count": 2,
"page": 1,
"page_count": 1,
"page_items": 2,
"page_size": 20
},
"schema": {
"folders": {
"$id": "https://example.com/model/schema/folders",
"revision": "{revisionNumber}"
}
}
}
}
Get data for a single folder¶
-
GET
/folders/
(folder_id)¶
Retrieve folder details by folder unique identifier.
Example request (get a folder by ID):
GET /folders/{folderId} HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "{folderId}",
"type": "folders",
"attributes": {
"title": "Root Folder"
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}",
"path": "/{folderId}"
},
"relationships": {
"children": {
"links": {
"related": "https://example.com/folders/{folderId}/children",
"self": "https://example.com/folders/{folderId}/relationships/children"
}
},
"parent": {
"links": {
"related": "https://example.com/folders/{folderId}/parent",
"self": "https://example.com/folders/{folderId}/relationships/parent"
}
}
}
},
"links": {
"self": "https://example.com/folders/{folderId}",
"home": "https://example.com/home"
},
"meta": {
"schema": {
"folders": {
"$id": "https://example.com/model/schema/folders",
"revision": "{revisionNumber}"
}
}
}
}
Create a folder¶
-
POST
/folders
¶
You can create folders by using POST /folders
endpoint.
Folders data must be specified inside body JSON data.
Request body structure is:
{
"data": {
"type": "folders",
"attributes": {}
}
}
Example request (create sample folder):
POST /folders HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "folders",
"attributes": {
"title": "Root Folder"
}
}
}
Expected response is HTTP/1.1 201 Created
, with application/vnd.api+json
body data representing the folder just created.
When folder already exists or data is not valid (i.e. data lacks of required fields), POST fails and response is 400 Bad Request - Invalid data
.
Successful response example follows:
HTTP/1.1 201 Created
Content-Type: application/vnd.api+json
{
"data": {
"id": "{folderId}",
"type": "folders",
"attributes": {
"uname": "root-folder",
"title": "Root Folder"
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}",
"path": "/{folderId}"
},
"relationships": {
"children": {
"links": {
"related": "https://example.com/folders/{folderId}/children",
"self": "https://example.com/folders/{folderId}/relationships/children"
}
},
"parent": {
"links": {
"related": "https://example.com/folders/{folderId}/parent",
"self": "https://example.com/folders/{folderId}/relationships/parent"
}
}
}
},
"links": {
"self": "https://example.com/folders",
"home": "https://example.com/home"
},
"meta": {
"schema": {
"folders": {
"$id": "https://example.com/model/schema/folders",
"revision": "{revisionNumber}"
}
}
}
}
Modify a folder¶
-
PATCH
/folders/
(folder_id)¶
A folder can be modified by calling a PATCH /folders/(folder_id)
with proper payload.
Necessary fields in payload are data.id
, data.type
and data.attributes
(not empty).
Example request (modify a folder title):
PATCH /folders/{folderId} HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
{
"data": {
"id": "{folderId}",
"type": "folders",
"attributes": {
"title": "My new folder"
}
}
}
Remove a folder¶
-
DELETE
/folders/
(folder_id)¶
Move a folder to trash (soft delete) using DELETE /folders/{folderId}
.
Expected HTTP status response is 204 No Content
and an empty body is returned.
Example request (delete a folder):
DELETE /folders/{folderId} HTTP/1.1
Host: example.com
Example response:
HTTP/1.1 204 No Content
Folders tree¶
Get the parent¶
-
GET
/folders/
(folder_id)/parent
¶
When a folder is not a root folder (it’s a subfolder), parent folder data can be retrieved.
You can obtain data of parent folder, for a specified subfolder, using GET /folders/(folder_id)/parent
, as in following example.
Example request (get a parent folder):
GET /folders/{folderId}/parent HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "{folderId}",
"type": "folders",
"attributes": {
"title": "Root Folder"
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}",
"path": "/{folderId}",
"relation": {
"id": {relationId},
"object_id": {relatedId},
"parent_id": {folderId},
"root_id": {folderId},
"parent_node_id": {parentId},
"tree_left": {leftId},
"tree_right": {rightId},
"depth_level": 1,
"menu": true
}
},
"relationships": {
"children": {
"links": {
"related": "https://example.com/folders/{folderId}/children",
"self": "https://example.com/folders/{folderId}/relationships/children"
}
},
"parent": {
"links": {
"related": "https://example.com/folders/{folderId}/parent",
"self": "https://example.com/folders/{folderId}/relationships/parent"
}
}
}
},
"links": {
"available": "https://example.com/objects?filter%5Btype%5D%5B0%5D=folders",
"self": "https://example.com/folders/{folderId}/parent",
"home": "https://example.com/home"
},
"meta": {
"schema": {
"folders": {
"$id": "https://example.com/model/schema/folders",
"revision": "{revisionNumber}"
}
}
}
}
data.meta.relations
contains the tree details for the folder (nested set model has been used to store folders tree data).
Set the parent¶
-
PATCH
/folders/
(folder_id)/relationships/parent
¶
When you want to set a parent for a folder, you need to call a PATCH
, specifying the folder identifier in the url and the parent identifier in body payload.
Example request (set a parent folder):
PATCH /folders/{folderId}/relationships/parent HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
{
"data": {
"type": "folders",
"id": "{parentId}"
}
}
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"links": {
"self": "https://example.com/folders/{folderId}/relationships/parent",
"home": "https://example.com/home"
}
}
Remove the parent¶
-
DELETE
/folders/
(folder_id)/relationships/parent
¶
When you want a folder be a root, you remove its association with the parent.
Removing folder parent association is performed through DELETE /folders/{folderId}/relationships/parent
,
specifying parent id in the body payload body, like in the Set the parent.
Expected HTTP status response is 204 No Content
and an empty body is returned.
Example request (remove a parent):
DELETE /folders/{folderId}/relationships/parent HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
{
"data": {
"type": "folders",
"id": "{parentId}"
}
}
Example response:
HTTP/1.1 204 No Content
Folders contents¶
Get contents¶
-
GET
/folders/
(folder_id)/children
¶
Contents inside a folder are retrieved through GET /folders/(folder_id)/children
;
usual filters, like Field filter or Search Query filter, are available.
Add content¶
-
POST
/folders/{folderId}/relationships/children
¶
You can save contents inside a folder using properly POST /folders/{folderId}/relationships/children
.
Payload body must contain content object type
and content identifier id
, like in the following example.
Example request (add a content to a folder):
POST /folders/{folderId}/relationships/children HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
{
"data": [
{
"type": "{contentTypeName}",
"id": "{contentId}"
}
]
}
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
Remove content¶
-
DELETE
/folders/{folderId}/relationships/children
¶
Removing contents is performed through DELETE /folders/{folderId}/relationships/children
.
Payload body must contain content object type
and content identifier id
, like in the Add content.
Expected HTTP status response is 204 No Content
and an empty body is returned.
Example request (remove a content from a folder):
DELETE /folders/{folderId}/relationships/children HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
{
"data": [
{
"type": "{contentTypeName}",
"id": "{contentId}"
}
]
}
Example response:
HTTP/1.1 204 No Content
Media thumbs /media/thumbs
¶
/media/thumbs
base endpoint provides a set of methods dedicated to thumbnails generation and retrieval.
Thumbnails can be requested by the client using the GET /media/thumbs/:id
or GET /media/thumbs?ids=:comma-separated-ids
endpoints,
where :id is a media identifier, :ids is a string containing media identifiers (separated by a comma: ids=1,2,3
).
A preset can be specified using the ?preset=<preset-name>
query parameter,
or (if explicitly allowed via configuration) a whole set of options — width, height, format, etc.
Example request:
GET /media/thumbs?ids=1,2,3&preset=async HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
// ...
"meta": {
"thumbnails": [
{
"id": 1,
"uuid": "some-uuid-1",
"ready": false,
"url": "http://example.com/something.jpg"
},
{
"id": 2,
"uuid": "some-uuid-2",
"ready": true,
"url": "http://example.com/something-else.jpg"
},
{
"id": 3,
"uuid": "some-uuid-3",
"ready": false,
"acceptable": false,
"url": "http://example.com/something.txt"
}
]
}
}
The ready response value tells you if the thumbnail has been already generated and it’s already available at url response value. The acceptable response value is false when image thumb cannot be created.
Thumbnails, set of options — width, height¶
You can request a thumbnail with specified options (width, height), using options in query string, as in following examples:
GET /media/thumbs/{{id}}?options[w]=100 HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
GET /media/thumbs/{{id}}?options[h]=100 HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
GET /media/thumbs/{{id}}?options[w]=100&options[h]=100 HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Thumbnails by preset¶
Presets allow you to use specific options and thumbnails generator (synchronous or asynchronous), according to the BEdita4 API instance configuration.
In the following example, BEdita4 api config provides various presets:
- default (with synchronous Glide generator, width 800, height 600),
- small (with synchronous Glide generator, width 100),
- medium (with synchronous Glide generator, width 400),
- async (with asynchronous Async generator, width 800, height 600).
'Thumbnails' => [
'allowAny' => filter_var(env('THUMBNAILS_ALLOW_ANY', false), FILTER_VALIDATE_BOOLEAN),
'presets' => [
'default' => [
'w' => 800,
'h' => 600,
],
'small' => [
'w' => 100,
],
'medium' => [
'w' => 400,
],
'async' => [
'generator' => 'async',
'w' => 800,
'h' => 600,
],
],
'generators' => [
'default' => [
'className' => 'BEdita/Core.Glide',
'url' => env('THUMBNAILS_DEFAULT_URL', null),
],
'async' => [
'className' => 'BEdita/Core.Async',
'url' => env('THUMBNAILS_ASYNC_URL', null),
],
],
],
In this example, you could use the presets as follows:
GET /media/thumbs/{{id}}?preset=small HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
GET /media/thumbs?ids=1,2,3&preset=small HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
GET /media/thumbs/{{id}}?preset=medium HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
GET /media/thumbs?ids=1,2,3&preset=medium HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
GET /media/thumbs/{{id}}?preset=async HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
GET /media/thumbs?ids=1,2,3&preset=async HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Annotations /annotations
¶
/annotations
endpoint provides a way to handle annotations management.
annotations
are structured info that can be related to BEdita objects.
This endpoint provides:
- get a list of annotations
- retrieve data of a single annotation by unique identifier
- create an annotation
- modify an annotation
- remove an annotation
Note: in the following sections, {annotationId}
and (annotation_id)
are sample placeholders for the unique identifier of the annotation, typically an integer number.
{objectId}
is a sample placeholders for the unique identifier of the object.
Other placeholders used in this page are {objectTypeName}
, {description}
, {userId}
, {createdDate}
, {modifiedDate}
, etc.
In the examples, response data contains a reduced number of fields, for better readability.
Get a collection of annotations¶
-
GET
/annotations
¶
Use GET /annotations
to retrieve a collection of annotations.
Available filters are Field filter and Search Query filter.
Example request (get all annotations):
GET /annotations HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": "{annotationId}",
"type": "annotations",
"attributes": {
"object_id": "{objectId}",
"description": "{description}",
"params": null
},
"meta": {
"user_id": "{userId}",
"created": "{createdDate}",
"modified": "{modifiedDate}"
},
"links": {
"self": "https://example.com/annotations/{annotationId}"
},
"relationships": {
"object": {
"links": {
"related": "https://example.com/annotations/{annotationId}/object",
"self": "https://example.com/annotations/{annotationId}/relationships/object"
}
},
}
}
],
"links": {
"self": "https://example.com/annotations",
"home": "https://example.com/home",
"first": "https://example.com/annotations",
"last": "https://example.com/annotations",
"prev": null,
"next": null
},
"meta": {
"pagination": {
"count": 2,
"page": 1,
"page_count": 1,
"page_items": 1,
"page_size": 20
}
}
}
Get single annotation¶
-
GET
/annotations/
(annotation_id)¶
Get details for a single annotation by its unique identifier.
Example request (get a single annotation by id):
GET /annotations/{annotationId} HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "{annotationId}",
"type": "annotations",
"attributes": {
"object_id": "{objectId}",
"description": "{description}",
"params": null
},
"meta": {
"user_id": "{userId}",
"created": "{createdDate}",
"modified": "{modifiedDate}"
},
"relationships": {
"object": {
"links": {
"related": "https://example.com/annotations/{annotationId}/object",
"self": "https://example.com/annotations/{annotationId}/relationships/object"
}
}
}
},
"links": {
"self": "https://example.com/annotations/{annotationId}",
"home": "https://example.com/home"
}
}
Create an annotation¶
-
POST
/annotations
¶
Annotation must be referenced to an object and it must have at least a description
.
To create a new annotation, you use POST /annotations
, specifying in the payload:
- the annotations type (
data.type
)- the referenced object (
data.attributes.object_id
)- the annotation description (
data.attributes.description
)
On succeed, response http status is 201 Created
.
Example request (create a new annotation):
POST /annotations HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
{
"data": {
"type": "annotations",
"attributes": {
"object_id": "{objectId}",
"description": "a note"
}
}
}
Example response:
HTTP/1.1 201 Created
Content-Type: application/vnd.api+json
{
"data": {
"id": "{annotationId}",
"type": "annotations",
"attributes": {
"object_id": "{objectId}",
"description": "a note",
"params": null
},
"meta": {
"user_id": "{userId}",
"created": "{createdDate}",
"modified": "{modifiedDate}"
},
"relationships": {
"object": {
"links": {
"related": "https://example.com/annotations/{annotationId}/object",
"self": "https://example.com/annotations/{annotationId}/relationships/object"
}
}
}
},
"links": {
"self": "https://example.com/annotations",
"home": "https://example.com/home"
}
}
Modify an annotation¶
-
PATCH
/annotations/
(annotation_id)¶
Annotation can be modified using PATCH /annotations/{annotationId}
.
Expected fields in body payload:
- the annotation identifier (
data.id
)- the annotations type (
data.type
)- the annotation description (
data.attributes.description
)
On succeed, response http status is 200 OK
.
A basic example follows.
Example request (modify an annotation):
PATCH /annotations/{annotationId} HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
{
"data": {
"id": "{annotationId}",
"type": "annotations",
"attributes": {
"description": "another note"
}
}
}
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "{annotationId}",
"type": "annotations",
"attributes": {
"object_id": "{objectId}",
"description": "another note",
"params": null
},
"meta": {
"user_id": "{userId}",
"created": "{createdDate}",
"modified": "{modifiedDate}"
},
"relationships": {
"object": {
"links": {
"related": "https://example.com/annotations/{annotationId}/object",
"self": "https://example.com/annotations/{annotationId}/relationships/object"
}
}
}
},
"links": {
"self": "https://example.com/annotations/{annotationId}",
"home": "https://example.com/home"
}
}
Remove annotations¶
-
DELETE
/annotations/
(annotation_id)¶
You can move annotations to trashcan (soft delete) using DELETE /annotations/{annotationId}
, with empty body.
When delete succeeds, 204 No Content
response is returned.
Example request (delete an annotation):
DELETE /annotations/{annotationId} HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 204 No Content
Translations /translations
¶
I18n data management is provided by translations
endpoint.
It provides:
- get translations with filters
- add a translation
- modify a translation
- remove a translation
Note: in the following sections, (translation_id)
and {translationId}
are sample placeholders for the unique identifier of the translation, typically an integer number;
{objectId}
is a sample placeholder for the unique identifier of the object translated.
Other placeholders used in this page are {createdDate}
, {modifiedDate}
, {createdBy}
, {modifiedBy}
, {status}
and {revisionNumber}
.
Get translation data¶
Get translations¶
-
GET
/translations
¶
-
GET
/translations/
(translation_id)¶
Retrieve all translations using GET /translations
, or a single one using GET /translations/(translation_id)
.
Usual filters, like Field filter or Search Query filter, are available.
Example request (get all translations):
GET /translations HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [
{
"id": "{translationId}",
"type": "translations",
"attributes": {
"object_id": "{objectId}",
"lang": "sp",
"status": "{status}",
"translated_fields": {
"title": "nuevo título",
"description": "nueva descripción"
}
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}",
"created_by": "{createdBy}",
"modified_by": "{modifiedBy}"
},
"links": {
"self": "https://example.com/translations/{translationId}"
},
"relationships": {
"object": {
"links": {
"related": "https://example.com/translations/{translationId}/object",
"self": "https://example.com/translations/{translationId}/relationships/object"
}
}
}
}
],
"links": {
"self": "https://example.com/translations",
"home": "https://example.com/home",
"first": "https://example.com/translations",
"last": "https://example.com/translations",
"prev": null,
"next": null
},
"meta": {
"pagination": {
"count": 1,
"page": 1,
"page_count": 1,
"page_items": 1,
"page_size": 20
}
}
}
Get main object from translation¶
-
GET
/translations/
(translation_id)/object
¶
To retrieve details of master object (the translated object), use GET /translations/(translation_id)/object
.
** Example request (get the translated object) **
GET /translations/{translationId}/object HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
** Example response **
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "{objectId}",
"type": "documents",
"attributes": {
"status": "on",
"uname": "this-is-a-test-object",
"title": "this is a test object"
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}"
},
"relationships": {
"translations": {
"links": {
"related": "https://example.com/documents/{objectId}/translations",
"self": "https://example.com/documents/{objectId}/relationships/translations"
}
}
}
},
"links": {
"available": "https://example.com/objects",
"self": "https://example.com/translations/{translationId}/object",
"home": "https://example.com/home"
},
"meta": {
"schema": {
"documents": {
"$id": "https://example.com/model/schema/documents",
"revision": "{revisionNumber}"
}
}
}
}
Get relationship object¶
-
GET
/translations/
(translation_id)/relationships/object
¶
To retrieve basic relationship info of master object (the translated object), use GET /translations/(translation_id)/relationships/object
.
** Example request (get the translated object relationships) **
GET /translations/{translationId}/relationships/object HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
** Example response **
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "{objectId}",
"type": "documents",
"relationships": {
"translations": {
"links": {
"related": "https://example.com/documents/{objectId}/translations",
"self": "https://example.com/documents/{objectId}/relationships/translations"
}
}
}
},
"links": {
"available": "https://example.com/objects",
"self": "https://example.com/translations/{translationId}/object",
"home": "https://example.com/home"
},
}
Add a translation¶
-
POST
/translations
¶
You create a translation with a POST /translations
, specifying in payload body some necessary data:
type
: “translations”. The endpoint.object_id
: the identifier of the object you want to translatestatus
of translation; it can be:”on”, “off” or “{status}”lang
: the 2 chars code for the lang of the translationtranslated_fields
: the field-value pairs representing the translation per field
When creation succeeds, 201 Created
response is returned.
You can see all together in the following example.
Example request (create a translation):
POST /translations HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "translations",
"attributes": {
"object_id": "{objectId}",
"status": "{status}",
"lang": "sp",
"translated_fields": {
"title": "título uno",
"description": "descripción uno"
}
}
}
}
Example response:
HTTP/1.1 201 Created
Content-Type: application/vnd.api+json
{
"data": {
"id": "{translationId}",
"type": "translations",
"attributes": {
"object_id": "{objectId}",
"lang": "sp",
"status": "{status}",
"translated_fields": {
"title": "título uno",
"description": "descripción uno"
}
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}",
"created_by": "{createdBy}",
"modified_by": "{modifiedBy}"
},
"relationships": {
"object": {
"links": {
"related": "https://example.com/translations/{translationId}/object",
"self": "https://example.com/translations/{translationId}/relationships/object"
}
}
}
},
"links": {
"self": "https://example.com/translations",
"home": "https://example.com/home"
}
}
Modify a translation¶
-
PATCH
/translations/
(translation_id)¶
Translation can be modified using PATCH /translations/(translation_id)
, specifying in payload body some necessary data:
id
: the translation unique identifiertype
: “translations”. The endpoint.translated_fields
: the field-value pairs representing the translation per field
When patch succeeds, 200 OK
response is returned.
Example request (modify a translation):
PATCH /translations/{translationId} HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"id": "{translationId}",
"type": "translations",
"attributes": {
"translated_fields": {
"title": "nuevo título",
"description": "nueva descripción"
}
}
}
}
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "{translationId}",
"type": "translations",
"attributes": {
"object_id": "{objectId}",
"lang": "sp",
"status": "{status}",
"translated_fields": {
"title": "nuevo título",
"description": "nueva descripción"
}
},
"meta": {
"created": "{createdDate}",
"modified": "{modifiedDate}",
"created_by": "{createdBy}",
"modified_by": "{modifiedBy}"
},
"relationships": {
"object": {
"links": {
"related": "https://example.com/translations/{translationId}/object",
"self": "https://example.com/translations/{translationId}/relationships/object"
}
}
}
},
"links": {
"self": "https://example.com/translations/{translationId}",
"home": "https://example.com/home"
}
}
Remove a translation¶
-
DELETE
/translations/
(translation_id)¶
Move translation to trash (soft delete) using DELETE /translations/(translation_id)
, with empty body.
When delete succeeds, 204 No Content
response is returned.
Example request (delete a translation):
DELETE /translations/{translationId} HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 204 No Content
Administration /admin
¶
Note
This endpoint is not yet available
/admin
endpoint deals with administrative operations on current project
Managed resources by this endpoint are:
- asynchronous jobs
- client applications definitions, including API KEYs management
- custom endpoints
- configuration properties
Model /model
¶
/model
base endpoint provides a set of methods dedicated to data modeling.
Unlike other endpoints /model
is basically a prefix to the actual endpoints that handle object types, property types, properties and relations. You can see this endpoints collection as the API Modeling part of BEdita.
Main operations available on these endpoints:
- list, create, update and (de)activate object types via
/model/object_types
- define new property types using JSON SCHEMA
/model/property_types
- handle object type properties with
/model/properties
- define relations between objects with
/model/relations
- retrieve JSON SCHEMA of object types and resources from
/model/schema
Object Types /model/object_types
¶
Through this endpoint you will be able to create new object types, modify, enable/disable or remove an object type.
Create an object type¶
Creation of a new object type happens through POST /model/object_types
endpoint.
-
POST
/model/object_types
¶
Example request (create `cats` type):
POST /model/object_types HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "object_types",
"attributes": {
"name": "cats",
"singular": "cat",
"description": "This is a cat"
}
}
In its simplest form the only required attributes are:
name
object type in plural form, this is the canonical form for this type that will activate/cats
endpointsingular
singular form for type
Both must be in lowered snake_case format as internal naming convention, see Naming convention, and must contain at least a letter (a-z) to avoid collisions with numeric ids. They also must be unique in your project.
description
is an optional description of your type
Expected response is HTTP/1.1 201 OK
, with application/vnd.api+json
body data representing object type just created.
If the object type already exists or data is not valid (i.e. lacks of required fields or inconsistent input), POST fails and response will be 400 Bad Request - Invalid data
.
Get single object type¶
You can obtain a single type by using GET /model/object_types/{type}
endpoint.
-
GET
/model/object_types/{type}
¶
{type}
can be a numeric id of the object type or itsname
Example request (get object types):
GET /model/object_types/cats HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "13",
"type": "object_types",
"attributes": {
"name": "cats",
"singular": "cat",
"is_abstract": false,
"description": "This is a cat",
"associations": null,
"hidden": null,
"enabled": true,
"table": "BEdita/Core.Objects",
"parent_name": "objects"
},
"meta": {
"created": "2017-11-21T13:56:54+00:00",
"modified": "2017-11-21T13:56:54+00:00",
"core_type": false,
"alias": "Cats",
"relations": []
},
"relationships": {
"left_relations": {
"data": [],
"links": {
"related": "http://api.example.com/model/object_types/13/left_relations",
"self": "http://api.example.com/model/object_types/13/relationships/left_relations"
}
},
"right_relations": {
"data": [],
"links": {
"related": "http://api.example.com/model/object_types/13/right_relations",
"self": "http://api.example.com/model/object_types/13/relationships/right_relations"
}
},
"parent": {
"links": {
"related": "http://api.example.com/model/object_types/13/parent",
"self": "http://api.example.com/model/object_types/13/relationships/parent"
}
}
}
},
"links": {
"self": "http://api.example.com/model/object_types/cats",
"home": "http://api.example.com/home"
}
}
Example response shows us some additional interesting fields:
"is_abstract"
tells us if an object type is abstract or concrete; an abstract type can only have concrete subtypes but there can be no object of this type"associations"
: list of internal entities associated with this type likeDateRanges
orStreams
, formatted as JSON array, defaults to null"hidden"
: list of core properties to hide, those properties will be ignored on every request and response for this type, defaults to null"enabled"
: is a simple flag to activate/deactivate an object type in your project; you will not be able to set it tofalse
if objects of this type or sub-types exist"table"
: table class responsible to store your object data, expressed in CakePHP plugin syntax, defaults to"BEdita/Core.Objects"
"parent_name"
: “name” of the parent abstract type, defaults to"objects"
core_type
is a system flag indicating whether this is acore
object type or a user created type on this project
Relationships available are instead:
"left_relations"
and"right_relations"
representing object types with _left_ or _right_ side relations to this type, see Relations /model/relations for an axplanation"parent"
abstract parent type of this one
Object types list¶
To retrieve a list of object types you can simply use GET /model/object_types
and take advantage of common filters like Field filter or Search Query filter
-
GET
/model/object_types
¶
Example request get enabled object types:
GET /model/object_types?filter[enabled]=true HTTP/1.1
Accept: application/vnd.api+json
Response will contain an array of object_types
in typical list format as shown in Response.
Modify an object type¶
You can modify an object type by using PATCH /model/object_types/{type}
endpoint.
-
PATCH
/model/object_types/{type}
¶
{type}
can be a numeric id of the object type or itsname
Example request: modify an object type:
In this example we will simply disable the newly created type and change its description
PATCH /model/object_types/13 HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"id": 13,
"type": "object_types",
"attributes": {
"enabled" : false,
"description" : "This was a cat"
}
}
}
Response 200 OK is expected.
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": 13,
"type": "object_types",
"attributes": {
"username": "johndoe",
"name": "Johnny",
"surname": "Doe"
},
"meta": {
...
},
"relationships": {
...
}
},
"links": {
"self": "http://api.example.com/model/object_types/13",
"home": "http://api.example.com/home"
}
}
"meta"
and "relationships"
here omitted for brevity.
Remove an object type¶
You can delete permanently an object type by using DELETE /model/object_types/{type}
endpoint.
This operation cannot be reversed and will not be allowed if:
- one or more objects of this type exist
core_type
attribute flag istrue
- a subtype of this type exits
-
DELETE
/model/object_types/
(type)¶
{type}
can be a numeric id of the object type or itsname
Example request: delete cats:
DELETE /model/object_types/cats HTTP/1.1
Host: api.example.com
Expected HTTP status response is 204 No Content
.
If object type is not found, response will be 404 Not Found
, if delete operation is not allowed a 403 Forbidden
will be sent.
HTTP/1.1 204 No Content
Properties /model/properties
¶
With this endpoint you will be able to define object properties: create new ones, disable or hide others, update and remove.
Note
There are two kind of properties
static
properties that match actual database columns and that cannot be changed or removed, but can be hiddendynamic
properties defined at project level
In other words: static
are predefined properties set at the database level common to any project, dynamic
are custom properties available on the current project only.
Create a property¶
To add a new _dynamic_ object property you can use POST /model/properties
endpoint.
-
POST
/model/properties
¶
Example request: create new property:
POST /model/properties HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "properties",
"attributes": {
"name": "nickname",
"description": "Profile nickname",
"property_type_name": "string",
"object_type_name": "profiles"
}
}
}
Required attributes in properties creation are:
name
property name, must be in lowered snake_case and must not create collisions with other property names already registered onobject_type_name
, and in its parent types and subtypesproperty_type_name
selected property type, must be a valid name avilable among Property Types /model/property_typesobject_type_name
object type that will have this property
Expected response is HTTP/1.1 201 OK
, with application/vnd.api+json
body data representing the property just created.
If property name
already exists or data is not valid (i.e. lacks of required fields or inconsistent input), POST fails and response will be 400 Bad Request - Invalid data
.
Get a property¶
You can obtain a single property by using GET /model/properties/{id}
endpoint.
-
GET
/model/properties/{id}
¶
{id}
property id, numeric or hash
Example request get property:
GET /model/properties/1 HTTP/1.1
Host: api.example.com
Accept:id/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "1",
"type": "properties",
"attributes": {
"name": "nickname",
"description": "Profile nickname",
"property_type_name": "string",
"object_type_name": "profiles"
},
"meta": {
"created": "2017-11-23T14:24:11+00:00",
"modified": "2017-11-23T14:24:11+00:00"
}
},
"links": {
"self": "http://api.example.com/model/properties/1",
"home": "http://api.example.com/home"
}
}
Expected HTTP status response is 200 OK
, if property is not found response will be 404 Not Found
View properties list¶
You can retrieve list of properties using GET /model/properties
. Common filters like Field filter or Search Query filter may come in handy.
A special filter[object_type]
can be used to get properties of a particular object type only.
-
GET
/model/properties
¶
Example request get enabled object types:
GET /model/properties?filter[object_type]=documents HTTP/1.1
Accept: application/vnd.api+json
Response will contain an array of properties
in typical list format as shown in Response belonging to documents
type only
In this response you will note two important things:
- some items have an _hash_ id like
"id": "608e814b-e4d7-57c2-8599-9692803e30bc"
and some a classic numeric one; has format _ids_ representstatic
fields, i.e. fields that match actual database columns that in general cannot be removed, but can be hidden - seehidden
object types attribute in Create an object type; instead pure numeric _ids_ represent puredynamic
properties defined at project level;static
will be present in any project,dynamic
are custom ones available on the current project only. "object_type_name"
will refer to the actual object type owning the property, it can be an abstract type; as a consequence"objects"
and"media"
and other customabstract
types may appear as"object_type_name"
also making a request likefilter[object_type]=documents
: properties displayed will have the requested type and its abstract parent types asobject_type_name
.
An additional filter may be used to select static or dynamic properties, namely filter[type]=static
or filter[type]=dynamic
to view only a properties subset.
Modify a property¶
To change a property there is a PATCH /model/properties/{{id}}
method available.
Remember that only dynamic
properties may be modified, whereas static
ones are immutable, but can be hidden.
-
PATCH
/model/properties/{{id}}
¶
{id}
dynamic property numericid
Example request: modify a property:
In this example we will simply disable the newly created property and change its description
PATCH /model/properties/1 HTTP/1.1
Content-Type: application/json
Accept: application/vnd.api+json
{
"data": {
"id": "1",
"type": "properties",
"attributes": {
"name": "nickname",
"description": "A brand new property description"
}
}
}
Response status 200 OK
expected upon success and complete modified property is returned like in Get a property.
Please note that once a property has been used in your project changing some attributes may not be changed in order to avoid conflicts and errors, namely: name
, property_type_name
and object_type_name
.
Remove a property¶
You can permanently remove a property invoking DELETE /model/properties/{id}
. Only dynamic
properties may be removed.
Please note that this operation cannot be reversed!
-
DELETE
/model/properties/
(id)¶
{id}
dynamic property numericid
Example request: delete property:
DELETE /model/properties/1 HTTP/1.1
Expected HTTP status response is 204 No Content
.
If property is not found, response will be 404 Not Found
, if delete operation is not allowed a 403 Forbidden
will be sent.
HTTP/1.1 204 No Content
Relations /model/relations
¶
/model/relations
endpoint manages relations between object types.
This is one of the most important features in BEdita: you can create dynamic semantic relations between object types, adding constraint rules and parameters without any data model or database modification.
Relations in BEdita have been heavily inspired by semantic web RDF triples.
Every relation should be expressed in a form {subject} {predicate} {object}
where both {subject}
and {object}
are BEdita objects and {predicate}
is the relation name.
In BEdita for every relation an inverse one is also created, this will be more clear in the example below.
With this endpoint you can:
- create, update/remove an object relation
- list available relations
- handle which object types are involved in a relation
Create a relation¶
Creation of a new relation happens through POST /model/relations
endpoint.
-
POST
/model/relations
¶
Example request - create relation:
POST /model/relations HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "relations",
"attributes": {
"name": "owner_of",
"label": "Owner of",
"inverse_name": "belong_to",
"inverse_label": "Belong to",
"description": "Cat owner relation"
}
}
}
Required attributes creating a new relation are:
name
principal relation nameinverse_name
inverse relation name
Both attributes must be different and unique inside the project, should differ from property names of object types involved in this relation and as naming convention should also be expressed as a _predicate_ linking object types together.
They also must be in lowered snake_case format as internal naming convention.
Our goal in this example, although not yet complete, is to create the relation [users] [owner_of] [cats]
and its inverse [cats] [belong_to] [users]
.
Other optional attributes are
label
alternative text to show instead ofname
inverse_label
alternative text to show instead ofinverse_name
description
optional description of the relation
Expected response is HTTP/1.1 201 OK
, with application/vnd.api+json
body data representing relation just created.
If the relation already exists or data is not valid (i.e. lacks of required fields or inconsistent input), POST fails and response will be 400 Bad Request - Invalid data
.
Get a single relation¶
You can obtain a single relation by invoking GET /model/relations/{relation}
.
-
GET
/model/relations/{relation}
¶
{relation}
can be a numeric id of the relation, itsname
orinverse_name
Example request (get a relation):
GET /model/relations/owner_of HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": {
"id": "1",
"type": "relations",
"attributes": {
"name": "owner_of",
"label": "Owner of",
"inverse_name": "belong_to",
"inverse_label": "Belong to",
"description": "Cat owner relation",
"params": {}
},
"relationships": {
"left_object_types": {
"links": {
"related": "http://api.example.com/model/relations/1/left_object_types",
"self": "http://api.example.com/model/relations/1/relationships/left_object_types"
}
},
"right_object_types": {
"links": {
"related": "http://api.example.com/model/relations/1/right_object_types",
"self": "http://api.example.com/model/relations/1/relationships/right_object_types"
}
}
}
},
"links": {
"self": "http://api.example.com/model/relations/owner_of",
"home": "http://api.example.com/home"
}
}
Example response shows us something interesting in "relationships"
section:
"left_object_types"
links object types to use on _the_ left side of the relation, think of thesubject
in thesubject predicate object
expression, i.e. object types that areowner_of
other object types (on the _right_ side)"right_object_types"
links object types to use on the _right_ side of the relation, think of theobject
in thesubject predicate object
expression, i.e. object types thatbelong_to
object types on the _left_ side
Relations list¶
To retrieve a list of relations you can simply invoke GET /model/relations
and use common filters like Field filter or Search Query filter
-
GET
/model/relations
¶
Example request: get relations:
GET /model/relations?filter[name]=owner_of HTTP/1.1
Accept: application/vnd.api+json
Response will contain an array of relations
in typical list format as shown in Response.
In this particular case response content in "data"
section will be the same as in the previous example, here below a reduced version of it for brevity.
Example response: get relations:
HTTP/1.1 200 OK
Content-Type: application/vnd.api+json
{
"data": [{
"id": "1",
"type": "relations",
"attributes": {
"name": "owner_of",
"..." : "..."
},
"relationships": {
"..." : "..."
}
}],
"links": {
"..." : "..."
},
"meta": {
"..." : "..."
}
}
Handle left and right object types¶
Relation modeling in BEdita is expressed, as seen before, in {subject} {predicate} {object}
form wich is translated to {left object types} {relation name} {right object types}
on a left to right expression flow.
To achieve this we need to manipulate left and right side of the expression: both may contain a list of object types involved in the relation.
Add object types¶
You may add object types to left side POST /model/relations/{{relation}}/relationships/left_object_types
and right side with
POST /model/relations/{{relation}}/relationships/right_object_types
-
POST
/model/relations/{{relation}}/relationships/left_object_types
¶
{relation}
can be a numeric id of the relation, itsname
orinverse_name
Example request: add left object types:
POST /model/relations/owner_of/relationships/left_object_types HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": [
{
"type": "object_types",
"id": "3"
},
{
"type": "object_types",
"id": "2"
}
]
}
In this example users
(with _id_ 3) and profiles
(_id_ 2) have been added as left side object types to the relation created before.
Expected response is 200 OK
upon success, while response will contain only a "links"
section pointing to the list of left/right object types for the current relation.
Get object types¶
To retrieve object types involved in a relation you can invoke GET /model/relations/{{relation}}/left_object_types
for the left side and
GET /model/relations/{{relation}}/right_object_types
for the right side.
{relation}
as usual can be a numeric id of the relation, its name
or inverse_name
and response will present a list of object types in "data"
section.
Replace object types¶
By invoking PATCH /model/relations/{{relation}}/relationships/left_object_types
you will replace all object types on the left side with a provided list. Same thing will happen with PATCH /model/relations/{{relation}}/relationships/right_object_types
.
-
PATCH
/model/relations/{{relation}}/relationships/right_object_types
¶
{relation}
can be a numeric id of the relation, itsname
orinverse_name
Example request: replace right object types:
PATCH /model/relations/owner_of/relationships/left_object_types HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": [
{
"type": "object_types",
"id": "13"
}
]
}
With this method we have completed our relation putting our custom object type seen in Create an object type on the right side.
Now we have: {users | profiles} {owner_of} {cats}
and inverse from right to left {cats} {belong_to} {users | profiles}
.
Expected response is 200 OK
upon success, while response will be contain only a "links"
section pointing to the list of left/right object types for the current relation.
Remove object types¶
To remove an object type from a relation you can call DELETE /model/relations/{{relation}}/relationships/left_object_types
(left side) or
DELETE /model/relations/{{relation}}/relationships/right_object_types
(right side) specifying an object type id in the request body.
-
DELETE
/model/relations/{{relation}}/relationships/left_object_types
¶
{relation}
can be a numeric id of the relation, itsname
orinverse_name
Example request: remove left object types:
DELETE /model/relations/owner_of/relationships/left_object_types HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"type": "object_types",
"id": "2"
}
}
Here we have removed profiles
(_id_ 2) from the left side. Now our relation will look like {users} {owner_of} {cats}
.
Expected HTTP status response is 204 No Content
and an empty body is returned.
If object type is not a valid left/right type response will be 400 Bad Request
.
Operation is not allowed if an object of the type you want to remove is already used in the relation. A 403 Forbidden
error will be sent in this case.
Modify a relation¶
You can modify a relation by using PATCH /model/relations/{relation}
endpoint.
-
PATCH
/model/relations/{relation}
¶
{relation}
can be a numeric id of the relation, itsname
orinverse_name
Example request: modify a relation:
In this example we will just change the description for the relation 1
PATCH /model/relations/owner_of HTTP/1.1
Host: api.example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"id": 1,
"type": "relations",
"attributes": {
"description" : "Link users owning cats"
}
}
}
Response status 200 OK
expected upon success and complete modified relation is returned like in Get a single relation.
Remove a relation¶
You can permanently delete a relation by invoking DELETE /model/relations/{relation}
.
This operation cannot be reversed and will not be allowed if actual object relations of this kind exist.
-
DELETE
/model/relations/
(relation)¶
{relation}
can be a numeric id of the relation, itsname
orinverse_name
Example request: delete relation:
DELETE /model/relations/owner_of HTTP/1.1
Host: api.example.com
Expected HTTP status response is 204 No Content
.
If relation is not found, response will be 404 Not Found
, if delete operation is not allowed a 403 Forbidden
will be sent.
HTTP/1.1 204 No Content
Schema /model/schema
¶
A JSON SCHEMA (draft 06) representation for each object and resource type can be retrieved with GET /model/schema/{type}
.
This is a read-only endpoint: only GET method for a single object type or resource is available.
-
GET
/model/schema/{type}
¶
{type}
object type or resourcename
Example request: role schema:
GET /model/schema/roles HTTP/1.1
Host: api.example.com
Accept: application/schema+json
Example response:
HTTP/1.1 200 OK
Content-Type: application/schema+json
{
"definitions": {},
"$id": "http://api.example.com/model/schema/roles",
"$schema": "http://json-schema.org/draft-06/schema#",
"type": "object",
"properties": {
"name": {
"$id": "/properties/name",
"type": "string",
"maxLength": 32,
"default": null
},
"description": {
"$id": "/properties/description",
"type": "string",
"default": null
},
"unchangeable": {
"$id": "/properties/unchangeable",
"type": "boolean",
"default": "0",
"isMeta": true
},
"created": {
"$id": "/properties/created",
"type": "string",
"format": "date-time",
"default": null,
"isMeta": true
},
"modified": {
"$id": "/properties/modified",
"type": "string",
"format": "date-time",
"default": null,
"isMeta": true
}
},
"required": [
"name"
]
}
Valid input values for {type}
are:
- every object type
name
in its canonical plural form, i.e.documents
,profiles
,users
… - some selected resource names:
roles
,streams
,applications
This representation defines the data structure of each object and resource available in terms of their properties. Relations between objects and resources are not (yet) displayed here.
For an in-depth explanation please have a look at the excellent JSON Schema documentation.
Please note that this representation is agnostic of the actual JSON data format used, whether it’s JSON API or JSON-LD
or GraphQL
.
Hence it will not match against the JSON API response you will get, but there is only a simple rule to follow in this case.
The custom keyword "isMeta"
identifies meta properties, when its value is true, that will appear in the “meta” section of a typical JSON API response. All other properties will instead be found in the “attributes” section.
Naming convention¶
Main entities describing your data model must be in lowered snake_case format as internal naming convention.
This applies to name
attribute of object types (also singular
see Create an object type), property types, properties and relations (also inverse_name
)
Signup /signup
¶
/signup
is the endpoint used to allow users to directly register as project user.
Of course users can be created using Users /users endpoint, but with /signup
you may allow autonomous user registration.
- Signup works in two steps:
- signup request: a new user is created, but not activated or verified - therefore the new user may or may not perform some actions in your application
- signup activation: through a UUID hash actual activation and verification of user’s email is done
Signup request¶
This request allows unverified user signup via POST that will usually be called anonymously.
Minimum required data are: username
, password
and email
.
An activation_url
is also required in order to activate the user.
An activation email is then sent to the user containing the actual activation URL that will have this form:
{activation_url}?uuid={uuid}&redirect_url={redirect_url}
Where {uuid}
is a system generated hash, and {redirect_url}
is an optional parameter passed in /signup
request.
In your activation_url
page you have to read the uuid
query parameter and then proceed to Signup activation.
In this first step a user with status draft
will be created, user verification job is also added to async_jobs
: after 24h user verification with provided uuid will expire.
-
POST
/signup
¶ Perform user signup request.
Form Parameters: - username – Username of user, must be unique.
- password – Password of user.
- email – User email, must be unique.
- activation_url – Activation URL that will be sent via email.
- redirect_url – Optional redirect url that will be added to activation URL as parameter.
Status Codes: - 202 Accepted – Successful user creation. User data will be displayed in response.
- 400 Bad Request – Bad request if username or email have already been used by other users.
Example request: Since this is not a JSON API request you MUST use
Content-Type: application/json
POST /signup HTTP/1.1 Host: api.example.com Accept: application/vnd.api+json Content-Type: application/json { "username": "johannadoe", "password": "j0h4nn4d0e", "email": "johannadoe@nowhere.xx" "activation_url": "http://myactivationsys.xx?dum=my", "redirect_url": "app://xx?dum=my" }
Example response:
Some fields are not displayed for brevity.
HTTP/1.1 200 OK Content-Type: application/vnd.api+json { "data": { "id": "1234", "type": "users", "attributes": { "username": "johannadoe", "name": null, "surname": null, "email": "johannadoe@nowhere.xx", "status": "draft", "uname": "user-johannadoe", "title": null, "description": null, }, "meta": { "blocked": false, "last_login": null, "last_login_err": null, "num_login_err": 0, "verified": null, "locked": false, "created": "2017-07-20T08:48:25+00:00", "modified": "2017-07-20T08:48:25+00:00", }, "relationships": { "roles": { "links": { "related": "http://api.example.com/users/1234/roles", "self": "http://api.example.com/users/1234/relationships/roles" } } } }, "links": { "self": "http://api.example.com/signup", "home": "http://api.example.com/home" } }
Signup activation¶
User verification and activation are done via a simple POST like in the following example that should be invoked in your activation url page after reading the passed uuid parameter.
On success an HTTP 202 status code is returned with an empty body.
-
POST
/signup/activation
¶ Perform user signup activation.
Form Parameters: - uuid – UUID of signup activation.
Status Codes: - 202 Accepted – Successful activation.
- 404 Not Found – Not found, if provided UUID is incorrect or expired.
Example request: Since this is not a JSON API request you MUST use
Content-Type: application/json
POST /signup/activation HTTP/1.1 Content-Type: application/json { "uuid": "96b0b9fe-17fa-4cf8-bffa-1cd506421227" }
Status /status
¶
/status
is a service endpoint to check BEdita service status: environment,
software versions, filesystem, database connection are checked.
In normal production mode no details are given about software versions and about warnings or failures, whereas in debug mode more details are shown.
Only GET method is available
Example request:
GET /status HTTP/1.1 Accept: application/jsonExpected response:
HTTP/1.1 200 Success Content-Type: application/json { "links": { "self": "http://example.org/home", "home": "http://example.org/home" } "meta": { "status": { "environment": "ok" } } }
In debug mode more informations will be displayed, including software versions and plugins loaded
Debug mode response:
HTTP/1.1 200 Success Content-Type: application/json { "links": { "self": "http://example.org/home", "home": "http://example.org/home" } "meta": { "status": { "environment": "ok", "errors": [ ], "debug": true, "plugins": [ "BEdita/API", "BEdita/Core", "DebugKit", "Migrations" ], "versions": { "BEdita": "4.0.0-alpha" } } } }
Trash /trash
¶
/trash
endpoint deals with trashcan contents management.
- Main responsabilities of this endpoint:
- show trashcan contents
- view trashcan single content
- restore object (remove from trashcan, restore to system)
- delete object (remove from database)
Contents in trashcan¶
You can obtain trash contents by using GET /trash
and GET /trash/(object_id)
endpoint.
-
GET
/trash/
¶
GET /trash
returns response 200 OK
and contents as array, in ‘data’, as described in following example.
Example request:
GET /trash HTTP/1.1 Host: example.com Accept: application/json, text/javascriptExample response:
HTTP/1.1 200 OK Content-Type: application/json { "data": [ .... ], .... }
-
GET
/trash/
(object_id)¶
GET /trash/(object_id)
returns response 200 OK
if content is found, 404 Not Found
otherwise.
Example request:
GET /trash/154 HTTP/1.1 Host: example.com Accept: application/json, text/javascriptExample response:
HTTP/1.1 200 OK Content-Type: application/json { "data": { .... }, .... }
Restore contents¶
You can restore contents by using PATCH /trash/(object_id)
endpoint.
-
PATCH
/trash/
(object_id)¶
Example request (restore object 55920)
In this example, purpose is restoring object 55920.
PATCH /trash/55920 HTTP/1.1
Host: example.com
Accept: application/vnd.api+json
Content-Type: application/vnd.api+json
{
"data": {
"id": 55920,
"type": "objects"
}
}
Response 204 No Content
is expected. When object is not found in trashcan, response is 404 Not Found
.
HTTP/1.1 204 No Content
Content-Type: application/vnd.api+json
Delete contents¶
You can completely remove contents from system by using DELETE /trash/(object_id)
endpoint.
-
DELETE
/trash/
(object_id)¶
Example request (delete object 55920):
DELETE /trash/55920 HTTP/1.1
Host: example.com
Expected response is 204 No Content
. When object is not found in trashcan, response is 404 Not Found
.
HTTP/1.1 204 No Content
Glossary¶
- access token
- A string granted by the authorization server used to identify the issuer of a request.
The access token has to be sent to the resource server every time that the client wants
to access protected resources.
This token is sent in
Authorization
HTTP header using aBearer
scheme on each request like this:Authorization: Bearer <token>
- folder
- A folder is a special object used to create tree structures. The concept is similar to folders in computer filesystems. In a project you may have multiple folder trees, hence multiple root folders. Each folder, that is not a root folder, has one and only one parent folder. Other object types can instead have more than one parent folder (multifiling).
- json api
- JSON API
- JSON API is a specification for how a client should request that resources be fetched or modified, and how a server should respond to those requests. JSON API is designed to minimize both the number of requests and the amount of data transmitted between clients and servers. This efficiency is achieved without compromising readability, flexibility, or discoverability [cit. from the specification]
- json schema
- JSON Schema
- JSON Schema is a JSON-based format for describing the structure of JSON data. JSON Schema asserts what a JSON document must look like, ways to extract information from it, and how to interact with it, ideal for annotating existing JSON APIs that would not otherwise have hypermedia controls or be machine-readable [from the core spec]
- jwt
- JWT
JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.
A JWT is composed by three parts:
- an header containing informations about the token type and algorithm used. It is Base64URL encoded.
- a payload containing informations in the form of claims (informations we want to transmit). It is Base64URL encoded.
- a signature used to verify the authenticity of the JWT using a valid algorithm defined by JSON Web Signature (JWS) specification (for example a shared secret HMAC).
More info here.
- object
- An object in BEdita is the atomic content of your project’s data, it could be a core types like a document, an event, an image, a video or it could be a custom type defined specifically in your project. Have a look at Objects definition for a detailed overview.
- project
- A project in BEdita is an independent data set consisting mainly of objects, resources and media files; you may think of a database with a set of related resources like media files and configurations; each project will expose its own endpoint to applications
- refresh token
- An opaque token issued by the authorization server. It is useful to renew an expired access token without send the user credentials again.
- resource
- A resource in BEdita is an entity with a fixed and predefined set of attributes that generally maps a single record on a table; resources are used to make BEdita internals work and may not be seen or used by an application. Noteworthy examples of resources are roles, applications and streams. Have a look at Objects definition to better understand the difference between objects and resources.
- role
- A role in RBAC permission model (https://en.wikipedia.org/wiki/Role-based_access_control) is used to assign permissions to perform some operations
- stream
- A stream in BEdita is a resource that represents a generic uploaded file; it may be a local file, reachable via filesystem, or a remote one with its own URI.
- user
- Project users accessing resources with credentials to login; main attributes are username and password or some external auth provider identifiers and other profile data like first name, surname, and other contact information; each user has usually at least one role, used to grant access on endpoint operations; user authentication is not always mandatory, some endpoints may respond to GET anonymous requests, but to perform write operations a user has to be identified