Skip to content

Commit 4698c9d

Browse files
authored
Add docs feature pages (#29)
- Update features/index.md - Add features/parse.md - Add features/validate.md - Add features/visit.md - Add features/print.md - Add features/execute.md - Add features/linq.md - Update mkdocs.yml for new pages
1 parent b4faae1 commit 4698c9d

8 files changed

Lines changed: 1018 additions & 4 deletions

File tree

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
# Execute
2+
3+
The execute feature wires together parsing, validation, and execution so you can run FunQL requests end-to-end with a
4+
single call. It builds on the execution pipeline, ensuring that all steps are performed in the correct order and in the
5+
right way, reducing complexity for developers.
6+
7+
This page explains how to enable the execute feature, how to run requests, and how to customize the pipeline.
8+
9+
## Adding the feature
10+
11+
Use `AddExecuteFeature()` to register the services required to execute FunQL requests:
12+
13+
```csharp
14+
public sealed class ApiSchema : Schema
15+
{
16+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
17+
{
18+
schema.AddExecuteFeature();
19+
}
20+
}
21+
```
22+
23+
This sets up the execute feature with default configurations and the core execution handlers, like handlers for parsing
24+
and validating. See [Advanced configuration](#advanced-configuration) on how to customize the feature.
25+
26+
## Executing requests
27+
28+
To avoid duplication, the examples for executing FunQL requests (both REST-style parameters and full FunQL queries) are
29+
documented in the _Executing queries_ guide:
30+
31+
- [Learn how to execute requests →](../../executing-queries/index.md)
32+
33+
## Advanced configuration
34+
35+
The `AddExecuteFeature()` method has two optional arguments:
36+
37+
- `action`: An action to customize the feature.
38+
- `withCoreExecutionHandlers`: Whether to add all core execution handlers. Default `true`.
39+
40+
For example, add your own execution handler:
41+
42+
```csharp
43+
public sealed class ApiSchema : Schema
44+
{
45+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
46+
{
47+
schema.AddExecuteFeature(config => {
48+
// Customize the feature here
49+
config.WithExecutionHandler("MyCustomHandler", new MyCustomHandler(), 0);
50+
});
51+
}
52+
}
53+
```
54+
55+
### Core execution handlers
56+
57+
With `withCoreExecutionHandlers` set to `true` (default), the execute feature adds the following core execution
58+
handlers:
59+
60+
- **`ParseRequestExecutionHandler`**: Parses a full FunQL request string (e.g., `listSets(skip(1))`) into a `Request`
61+
AST for further processing. This is designed for full FunQL queries, treating FunQL as a query language (_QL_).
62+
- **`ParseRequestForParametersExecutionHandler`**: Builds a `Request` from given parameters (e.g., `filter`, `sort`),
63+
parsing each parameter separately. This is designed for _REST_ APIs where the FunQL parameters are extracted from the
64+
URL, like `../sets?filter=gte(price, 500)&sort=desc(price)`.
65+
- **`ValidateRequestExecutionHandler`**: Validates the parsed `Request` against the schema.
66+
67+
!!! tip
68+
69+
Add the [LINQ feature](linq.md) **after** the execute feature to automatically register the LINQ execution handlers.
70+
These handlers translate the `Request` into LINQ and apply filter/sort/limit/skip to the target `IQueryable`.
71+
72+
### Custom execution handlers
73+
74+
You can also add your own execution handlers to the pipeline. For example, a handler that measures the time it takes to
75+
execute the request:
76+
77+
```csharp
78+
public sealed class TimingExecutionHandler : IExecutionHandler
79+
{
80+
public async Task Execute(IExecutorState state, ExecutorDelegate next, CancellationToken cancellationToken)
81+
{
82+
var stopwatch = Stopwatch.StartNew();
83+
try
84+
{
85+
await next(state, cancellationToken);
86+
}
87+
finally
88+
{
89+
stopwatch.Stop();
90+
Console.WriteLine($"Request executed in {stopwatch.ElapsedMilliseconds} ms");
91+
}
92+
}
93+
}
94+
```
95+
96+
Then register the handler when adding the execute feature:
97+
98+
```csharp
99+
public sealed class ApiSchema : Schema
100+
{
101+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
102+
{
103+
schema.AddExecuteFeature(config => {
104+
// Register the handler here
105+
config.WithExecutionHandler(
106+
// Name of the handler, used to identify it in the pipeline
107+
name: "TimingExecutionHandler",
108+
new TimingExecutionHandler(),
109+
// Lower order means earlier in the pipeline, so pick a very
110+
// low value to measure the whole pipeline
111+
order: int.MinValue
112+
);
113+
});
114+
}
115+
}
116+
```
117+
118+
Now whenever a request is executed, the `TimingExecutionHandler` will log how long it takes to execute the request.
119+
120+
**Dynamic context:**
121+
122+
For handlers that require dynamic context or need to share data with other handlers, use `IExecutorState` to enter an
123+
`IExecuteContext`. This stores data that handlers later in the pipeline can then use.
124+
125+
As an example, we update the `TimingExecutionHandler` to enter a `TimingContext` with a shared `Stopwatch` for a
126+
different handler to use:
127+
128+
```csharp
129+
public sealed record TimingContext(Stopwatch Stopwatch) : IExecuteContext;
130+
131+
public sealed class TimingExecutionHandler : IExecutionHandler
132+
{
133+
public async Task Execute(IExecutorState state, ExecutorDelegate next, CancellationToken cancellationToken)
134+
{
135+
var stopwatch = Stopwatch.StartNew();
136+
// Enter the context before calling the next handler
137+
state.EnterContext(new TimingContext(stopwatch));
138+
139+
await next(state, cancellationToken);
140+
141+
// Exit the context to clean up the data
142+
state.ExitContext();
143+
}
144+
}
145+
```
146+
147+
Now create a handler that uses the `TimingContext` to log the elapsed time:
148+
149+
```csharp
150+
public sealed class LogTimingExecutionHandler : IExecutionHandler
151+
{
152+
public Task Execute(IExecutorState state, ExecutorDelegate next, CancellationToken cancellationToken)
153+
{
154+
// Find the context and log the elapsed time
155+
var context = state.FindContext<TimingContext>();
156+
if (context != null) {
157+
Console.WriteLine($"Time elapsed is {context.stopwatch.ElapsedMilliseconds} ms");
158+
} // else: Context was not entered
159+
160+
return next(state, cancellationToken);
161+
}
162+
}
163+
```
164+
165+
The `LogTimingExecutionHandler` logs the elapsed time only when a `TimingContext` has been entered.
166+
167+
## What's next
168+
169+
With the execution feature added, it's time to use it:
170+
171+
- [Learn more about executing queries →](../../executing-queries/index.md)
172+
- [Learn more about the LINQ feature →](linq.md)
Lines changed: 136 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,139 @@
11
# Features
22

3-
!!! info "Work in Progress"
3+
FunQL provides a modular architecture where each capability (like parse, validate, execute) is its own configurable
4+
feature. By only including the features you need, you ensure that your schema remains lightweight and optimized for your
5+
use case.
46

5-
We are actively working on the documentation for FunQL .NET.
6-
Please check back later or visit [our repository](https://github.com/funql/funql-dotnet) to track progress or
7-
contribute.
7+
This section introduces FunQL's features, explains their role, and shows you how to add them to your schema.
8+
9+
## Adding core features
10+
11+
To quickly enable all core features (Parse, Validate, Execute, Print, and Visit), use:
12+
13+
```csharp
14+
public sealed class ApiSchema : Schema
15+
{
16+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
17+
{
18+
schema.AddCoreFeatures();
19+
}
20+
}
21+
```
22+
23+
For a lightweight schema, selectively add only the features you need.
24+
25+
---
26+
27+
## Parse
28+
29+
The parse feature enables FunQL to transform raw queries into structured query nodes by generating an Abstract Syntax
30+
Tree (AST). This is the first step in handling FunQL queries before validation or execution.
31+
32+
**Adding the feature:**
33+
34+
```csharp
35+
public sealed class ApiSchema : Schema
36+
{
37+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
38+
{
39+
schema.AddParseFeature();
40+
}
41+
}
42+
```
43+
44+
[Learn more about the parse feature →](parse.md)
45+
46+
## Validate
47+
48+
The validate feature allows for validating that FunQL queries comply with the rules defined in the schema. This prevents
49+
invalid queries from being processed by catching errors early.
50+
51+
**Adding the feature:**
52+
53+
```csharp
54+
public sealed class ApiSchema : Schema
55+
{
56+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
57+
{
58+
schema.AddValidateFeature();
59+
}
60+
}
61+
```
62+
63+
[Learn more about the validate feature →](validate.md)
64+
65+
## Execute
66+
67+
The execute feature simplifies the entire query-processing pipeline by combining parsing, validation, and execution into
68+
a single method, leveraging FunQL's [execution pipeline](../../executing-queries/pipeline.md).
69+
70+
**Adding the feature:**
71+
72+
```csharp
73+
public sealed class ApiSchema : Schema
74+
{
75+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
76+
{
77+
schema.AddExecuteFeature();
78+
}
79+
}
80+
```
81+
82+
[Learn more about the execute feature →](execute.md)
83+
84+
## LINQ
85+
86+
The LINQ feature translates FunQL queries into LINQ expressions, enabling seamless integration with LINQ-based
87+
frameworks such as Entity Framework Core.
88+
89+
**Adding the feature:**
90+
91+
```csharp
92+
public sealed class ApiSchema : Schema
93+
{
94+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
95+
{
96+
schema.AddLinqFeature();
97+
}
98+
}
99+
```
100+
101+
[Learn more about the LINQ feature →](linq.md)
102+
103+
## Visit
104+
105+
The visit feature provides functionality to traverse and inspect the FunQL AST. It is commonly used for the [validate
106+
feature](#validate), but it can be extended for custom operations like query rewriting or auditing.
107+
108+
**Adding the feature:**
109+
110+
```csharp
111+
public sealed class ApiSchema : Schema
112+
{
113+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
114+
{
115+
schema.AddVisitFeature();
116+
}
117+
}
118+
```
119+
120+
[Learn more about the visit feature →](visit.md)
121+
122+
## Print
123+
124+
The print feature converts the FunQL AST into a readable FunQL query string. This is useful for logging or debugging
125+
complex queries.
126+
127+
**Adding the feature:**
128+
129+
```csharp
130+
public sealed class ApiSchema : Schema
131+
{
132+
protected override void OnInitializeSchema(ISchemaConfigBuilder schema)
133+
{
134+
schema.AddPrintFeature();
135+
}
136+
}
137+
```
138+
139+
[Learn more about the print feature →](print.md)

0 commit comments

Comments
 (0)