Skip to content

Commit 3d628a9

Browse files
committed
Add Core Concepts page to framework section and update overview
1 parent e24a638 commit 3d628a9

File tree

3 files changed

+399
-213
lines changed

3 files changed

+399
-213
lines changed

_includes/docs-sidebar.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,13 @@ <h3 class="mt-2">Framework</h3>
124124
</a>
125125
<ul id="part6" class="collapse">
126126
<li><a href="/docs/framework/overview">Overview</a></li>
127+
<li><a href="/docs/framework/core-concepts">Core Concepts</a></li>
127128
<li><a href="/docs/framework/tutorial"><strong>Tutorial</strong></a></li>
128-
<li><a href="/docs/framework/deploy">Deployment</a></li>
129129
<li><a href="/docs/framework/services">Services</a></li>
130130
<li><a href="/docs/framework/interceptors">Interceptors</a></li>
131131
<li><a href="/docs/framework/providers">Providers</a></li>
132132
<li><a href="/docs/framework/initializers">Initializers</a></li>
133+
<li><a href="/docs/framework/deploy">Deployment</a></li>
133134
<li><a href="/docs/framework/javascript-plugins">Develop with JavaScript</a></li>
134135

135136
<li class="subsection fw-bold mt-2">Security</li>

docs/framework/core-concepts.adoc

Lines changed: 319 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,319 @@
1+
---
2+
docs_version: 9
3+
title: Plugin Core Concepts
4+
layout: docs-adoc
5+
menu: framework
6+
applies_to: restheart
7+
---
8+
9+
This page covers the essential concepts you need to understand when developing RESTHeart plugins.
10+
11+
TIP: New to plugins? Start with the link:/docs/framework/overview[Plugin Development Overview] and link:/docs/framework/tutorial[Tutorial] first.
12+
13+
== Project Setup and Dependencies
14+
15+
The only required dependency to develop a plugin is `restheart-commons`.
16+
17+
With maven:
18+
19+
[source,xml]
20+
----
21+
<dependency>
22+
<groupId>org.restheart</groupId>
23+
<artifactId>restheart-commons</artifactId>
24+
<version>VERSION</version>
25+
</dependency>
26+
----
27+
28+
With Gradle:
29+
30+
[source,gradle]
31+
----
32+
dependencies {
33+
implementation 'org.restheart:restheart-commons:VERSION'
34+
}
35+
----
36+
37+
TIP: Use the link:https://github.com/SoftInstigate/restheart-plugin-skeleton[plugin skeleton project] for a ready-to-go Maven setup.
38+
39+
== The @RegisterPlugin Annotation
40+
41+
Every plugin must be annotated with `@RegisterPlugin`. This annotation:
42+
43+
- Allows RESTHeart to discover your plugin at startup
44+
- Defines the plugin's name, description, and behavior
45+
- Specifies configuration like URIs, security settings, and execution priorities
46+
47+
**Basic example:**
48+
49+
[source,java]
50+
----
51+
@RegisterPlugin(name = "foo",
52+
description = "just an example service",
53+
defaultUri="/foo", // optional, default /<service-name>
54+
secure=false, // optional, default false
55+
enabledByDefault=false) // optional, default true
56+
public class MyPlugin implements JsonService {
57+
...
58+
}
59+
----
60+
61+
**Annotation Parameters:**
62+
63+
[options="header"]
64+
|===
65+
|param |plugin |description |mandatory |default value
66+
|`name`
67+
|all
68+
|the name of the plugin
69+
|yes
70+
|*none*
71+
|`description`
72+
|all
73+
|description of the plugin
74+
|yes
75+
|*none*
76+
|`enabledByDefault`
77+
|all
78+
|`true` to enable the plugin; can be overridden by the plugin configuration option `enabled`
79+
|no
80+
|`true`
81+
|`defaultURI`
82+
|service
83+
|the default URI of the Service; can be overridden by the service configuration option `uri`
84+
|no
85+
|/&lt;srv-name&gt;
86+
|`matchPolicy`
87+
|service
88+
|`PREFIX` to match request paths starting with `/<uri>`,`EXACT` to only match the request path `/<uri>`
89+
|no
90+
|`PREFIX`
91+
|`secure`
92+
|service
93+
|`true` to require successful authentication and authorization to be invoked; can be overridden by the service configuration option `secure`
94+
|no
95+
|`false`
96+
|`dontIntercept`
97+
|service
98+
|list of interceptPoints to be executed on requests handled by the service, e.g. `dontIntercept = { InterceptPoint.ANY, InterceptPoint.RESPONSE }`
99+
|no
100+
|`{}`
101+
|`interceptPoint`
102+
|interceptor
103+
|the intercept point: `REQUEST_BEFORE_AUTH`, `REQUEST_AFTER_AUTH`, `RESPONSE`, `RESPONSE_ASYNC`
104+
|no
105+
|REQUEST_AFTER_AUTH
106+
|`initPoint`
107+
|initializer
108+
|specify when the initializer is executed: `BEFORE_STARTUP`, `AFTER_STARTUP`
109+
|no
110+
|`AFTER_STARTUP`
111+
|`requiresContent`
112+
|proxy interceptor
113+
|Only used by Interceptors of proxied resources (the content is always available to Interceptor of Services) Set it to true to make available the content of the request (if interceptPoint is REQUEST_BEFORE_AUTH or REQUEST_AFTER_AUTH) or of the response (if interceptPoint is RESPONSE or RESPONSE_ASYNC)
114+
|no
115+
|`false`
116+
|`priority`
117+
|interceptor, initializer
118+
|the execution priority (less is higher priority)
119+
|no
120+
|`10`
121+
|`blocking`
122+
|service
123+
|With blocking = `false` the execution of the service is not dispatched to a working thread and executed by the io-thread, thus avoiding the overhead of the thread handling and switching.
124+
|no
125+
|`true`
126+
|`authorizerType`
127+
|authorizer
128+
|`ALLOWER` can authorize a request unless no `VETOER` vetoes it.
129+
|no
130+
|ALLOWER
131+
|===
132+
133+
134+
== Configuration
135+
136+
Plugins are configured in `restheart.yml` using the plugin name from `@RegisterPlugin`:
137+
138+
[source,yml]
139+
----
140+
ping:
141+
enabled: true
142+
secure: false
143+
uri: /ping
144+
msg: 'Ping!'
145+
----
146+
147+
**Special configuration options** automatically managed by RESTHeart:
148+
149+
- **enabled**: Enable/disable the plugin (overrides `enabledByDefault` in `@RegisterPlugin`)
150+
- **uri**: Set the service URI (overrides `defaultUri` in `@RegisterPlugin`)
151+
- **secure**: Require authentication/authorization (`true`) or allow open access (`false`)
152+
153+
WARNING: Services have `secure: false` by default! Always set `secure: true` for production services that need protection.
154+
155+
**Accessing configuration in code:**
156+
157+
[source,java]
158+
----
159+
@Inject("conf")
160+
Map<String, Object> conf;
161+
162+
// Use helper method to get values
163+
String msg = argValue(conf, "msg");
164+
----
165+
166+
167+
== Dependency Injection
168+
169+
Use `@Inject` to access RESTHeart's built-in objects and services. Available providers:
170+
171+
[cols="2,3,3", options="header"]
172+
|===
173+
|Injection |Type |Use For
174+
175+
|`@Inject("conf")`
176+
|`Map<String, Object>`
177+
|Plugin's configuration
178+
179+
|`@Inject("rh-config")`
180+
|`Configuration`
181+
|RESTHeart's global configuration
182+
183+
|`@Inject("mclient")`
184+
|`MongoClient`
185+
|MongoDB database access
186+
187+
|`@Inject("registry")`
188+
|`PluginsRegistry`
189+
|Access to other plugins
190+
191+
|`@Inject("acl-registry")`
192+
|`ACLRegistry`
193+
|Programmatic permission management
194+
195+
|`@Inject("gql-app-definition-cache")`
196+
|`LoadingCache<String, GraphQLApp>`
197+
|GraphQL app definition cache
198+
|===
199+
200+
**Example:**
201+
202+
[source,java]
203+
----
204+
public class MyPlugin implements JsonService {
205+
@Inject("mclient")
206+
private MongoClient mclient;
207+
208+
@Inject("conf")
209+
private Map<String, Object> conf;
210+
211+
@Override
212+
public void handle(JsonRequest req, JsonResponse res) {
213+
// Use injected MongoClient
214+
var db = mclient.getDatabase("mydb");
215+
// Use injected configuration
216+
var setting = argValue(conf, "mySetting");
217+
}
218+
}
219+
----
220+
221+
TIP: See link:/docs/framework/providers[Providers] for how to create your own injectable objects.
222+
223+
== Request and Response Types
224+
225+
Services and Interceptors are **generic classes** that use type parameters to define how they handle request and response data.
226+
227+
RESTHeart provides specialized Request/Response pairs for different data formats:
228+
229+
[cols="2,3,3", options="header"]
230+
|===
231+
|Type Pair |Content Format |Best For
232+
233+
|`JsonRequest` / `JsonResponse`
234+
|JSON objects
235+
|REST APIs, JSON services
236+
237+
|`BsonRequest` / `BsonResponse`
238+
|BSON (MongoDB format)
239+
|MongoDB operations
240+
241+
|`MongoRequest` / `MongoResponse`
242+
|MongoDB-specific
243+
|Advanced MongoDB features
244+
245+
|`ByteArrayRequest` / `ByteArrayResponse`
246+
|Raw bytes
247+
|Binary data, files
248+
249+
|`StringRequest` / `StringResponse`
250+
|Plain text
251+
|Text processing
252+
253+
|`BsonFromCsvRequest`
254+
|CSV to BSON
255+
|CSV imports
256+
|===
257+
258+
**Why this matters:**
259+
260+
- The type you choose determines how request content is parsed and cached
261+
- Each type provides helper methods for its specific use case (e.g., `MongoRequest.getPageSize()`)
262+
- Content is parsed once and cached for efficiency
263+
- Type safety helps prevent errors at compile time
264+
265+
**Example:**
266+
267+
[source,java]
268+
----
269+
@RegisterPlugin(name = "myService", defaultUri = "/myapi")
270+
public class MyService implements JsonService {
271+
@Override
272+
public void handle(JsonRequest req, JsonResponse res) {
273+
// req and res are strongly typed for JSON
274+
JsonObject content = req.getContent();
275+
res.setContent(Json.object().put("status", "ok"));
276+
}
277+
}
278+
----
279+
280+
TIP: Start with `JsonService` for most REST APIs. See link:/docs/framework/services[Services] for detailed examples of each type.
281+
282+
== How Content Parsing Works
283+
284+
Understanding how RESTHeart handles request content is important for efficient plugin development.
285+
286+
**The parsing lifecycle:**
287+
288+
1. When a request arrives, RESTHeart determines which Service will handle it
289+
2. The Service creates typed Request and Response objects
290+
3. On first call to `request.getContent()`, the content is parsed using `parseContent()`
291+
4. The parsed content is cached in the request object
292+
5. Subsequent calls to `getContent()` return the cached object (no re-parsing)
293+
294+
**Why this matters:**
295+
296+
- Content is only parsed when you actually need it (lazy evaluation)
297+
- Multiple plugins can access the same content without overhead
298+
- You can safely call `getContent()` multiple times
299+
300+
**Custom parsing:**
301+
302+
If you create a custom ServiceRequest, implement the `parseContent()` method:
303+
304+
[source,java]
305+
----
306+
public abstract class ServiceRequest<T> {
307+
/**
308+
* Parses the content from the exchange and converts it into type T.
309+
* Called automatically by getContent() on first invocation.
310+
*/
311+
public abstract T parseContent() throws IOException, BadRequestException;
312+
}
313+
----
314+
315+
== Next Steps
316+
317+
- **Apply these concepts:** link:/docs/framework/services[Build a Service]
318+
- **See it in action:** link:/docs/framework/tutorial[Plugin Development Tutorial]
319+
- **Advanced topics:** link:/docs/framework/providers[Create Custom Providers]

0 commit comments

Comments
 (0)