Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,6 @@ endpoints:
# Prevents API from accessing these config files
ignore_files:
- streams
patch:
enabled: true
auth: true
17 changes: 17 additions & 0 deletions docs/EVENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- [Config](#config)
- [onApiConfigGetAll](#onapiconfiggetall)
- [onApiConfigGet](#onapiconfigget)
- [onApiConfigUpdate](#onapiconfigupdate)
- [Plugin](#plugin)
- [onApiPluginGetAll](#onapiplugingetall)
- [onApiPluginGet](#onapipluginget)
Expand Down Expand Up @@ -234,6 +235,7 @@ List of `Config` events:

- [onApiConfigGetAll](#onapiconfiggetall)
- [onApiConfigGet](#onapiconfigget)
- [onApiConfigUpdate](#onapiconfigupdate)

Please refer to the example code provided for full documentation of the available properties for each custom event.

Expand Down Expand Up @@ -267,6 +269,21 @@ function onApiConfigGet(Event $e) {
}
```

#### onApiConfigUpdate

This event is fired any time the `PATCH /configs/{id}` endpoint is successfully requested.

```php
function onApiConfigUpdate(Event $e) {
/**
* The GravApi ConfigModel returned in the API response.
*
* @var \GravApi\Models\ConfigModel
*/
$e['config'];
}
```

### Plugin

Whenever a `Plugin` resource is succesfully requested, there will be a custom event fired by the API plugin including the affected resource as a property of the `Event` itself.
Expand Down
51 changes: 51 additions & 0 deletions docs/postman_collection.json
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,57 @@
}
},
"response": []
},
{
"name": "Update Config",
"request": {
"auth": {
"type": "basic",
"basic": [
{
"key": "password",
"value": "D3velopment",
"type": "string"
},
{
"key": "username",
"value": "development",
"type": "string"
}
]
},
"method": "PATCH",
"header": [
{
"key": "Content-Type",
"name": "Content-Type",
"value": "application/json",
"type": "text"
}
],
"body": {
"mode": "raw",
"raw": "{\n\t\"title\": \"new site title\",\n\t\"custom_field\": null\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "localhost:8080/api/configs/site",
"host": [
"localhost"
],
"port": "8080",
"path": [
"api",
"configs",
"site"
]
}
},
"response": []
}
],
"protocolProfileBehavior": {}
Expand Down
48 changes: 47 additions & 1 deletion docs/specification.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ paths:
tags:
- plugins
summary: Update a specific plugin
description: The request's body JSON should match the config structure of the plugin which you wish to update. If you wish to remove existing properties from the config, you can set their values to `null` and they will be unset.
description: The request's body JSON should match the config structure of the plugin which you wish to update. If you wish to remove existing properties from the config, you can set their values to `null` and they will be unset. Otherwise, the given values will be merged with the existing config.
security:
- basic: ['api.super', 'admin.super']
parameters:
Expand Down Expand Up @@ -708,6 +708,52 @@ paths:
examples:
NotFound:
$ref: '#/components/examples/NotFoundResponse'
patch:
tags:
- configs
summary: Update a specific config file
description: The request's body JSON should match the structure of the config file which you wish to update. If you wish to remove existing properties from the config, you can set their values to `null` and they will be unset. Otherwise, the given values will be merged with the existing config.
security:
- basic: ['api.super', 'api.configs_edit']
parameters:
- name: id
in: path
description: The id of the config file to update
required: true
schema:
type: string
example: site
requestBody:
content:
application/json:
schema:
type: object
description: This JSON structure should match the desired configuration file's options.
responses:
200:
description: Successful operation
content:
application/json:
schema:
$ref: '#/components/schemas/ConfigResponse'
401:
description: Invalid authentication
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
InvalidAuth:
$ref: '#/components/examples/InvalidAuthResponse'
404:
description: Configuration file not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
examples:
NotFound:
$ref: '#/components/examples/NotFoundResponse'
components:
securitySchemes:
basic:
Expand Down
3 changes: 3 additions & 0 deletions grav/config/plugins/api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,6 @@ endpoints:
auth: true
ignore_files:
- streams
patch:
enabled: true
auth: true
10 changes: 10 additions & 0 deletions src/Api.php
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,16 @@ function () use ($config) {
)
);
}

if ($config->configs->patch->enabled) {
$this->patch('/{config}', ConfigHandler::class . ':updateConfig')
->add(
new AuthMiddleware(
$config->configs->patch,
[Constants::ROLE_CONFIGS_EDIT]
)
);
}
}
);
});
Expand Down
2 changes: 1 addition & 1 deletion src/Config/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ protected function configureRoute($customRoute = null)
// But may want to override it specifically if we've given custom
// settings to the Config instance (e.g. for tests)
if (isset($customRoute)) {
$this->route = $customRoute;
$this->route = trim($customRoute, '/');
} elseif ($route) {
$this->route = trim($route, '/');
}
Expand Down
1 change: 1 addition & 0 deletions src/Config/Constants.php
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ class Constants
// Events
const EVENT_ON_API_CONFIG_GET_ALL = 'onApiConfigGetAll';
const EVENT_ON_API_CONFIG_GET = 'onApiConfigGet';
const EVENT_ON_API_CONFIG_UPDATE = 'onApiConfigUpdate';
const EVENT_ON_API_PAGE_GET_ALL = 'onApiPageGetAll';
const EVENT_ON_API_PAGE_GET = 'onApiPageGet';
const EVENT_ON_API_PAGE_FIND = 'onApiPageFind';
Expand Down
47 changes: 46 additions & 1 deletion src/Handlers/ConfigHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@

use GravApi\Responses\Response;
use GravApi\Helpers\ConfigHelper;
use GravApi\Helpers\ArrayHelper;
use GravApi\Resources\ConfigResource;
use GravApi\Resources\ConfigCollectionResource;
use GravApi\Config\Constants;
use GravApi\Models\ConfigModel;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\YamlFile;

/**
* Class ConfigHandler
Expand All @@ -28,7 +31,7 @@ public function getConfigs($request, $response, $args)
public function getConfig($request, $response, $args)
{
if (!isset($args['config'])) {
return $response->withJson(Response::badRequest('No config `id given!'), 400);
return $response->withJson(Response::badRequest('No config `id` given!'), 400);
}

$config = ConfigHelper::loadConfig($args['config']);
Expand All @@ -45,4 +48,46 @@ public function getConfig($request, $response, $args)

return $response->withJson($resource->toJson());
}

public function updateConfig($request, $response, $args)
{
if (!isset($args['config'])) {
return $response->withJson(Response::badRequest('No config `id` given!'), 400);
}

$existingConfig = ConfigHelper::loadConfig($args['config']);

// If the config doesn't exist, OR it is present on the filter list
// (i.e. we don't want to allow user access to it)
if (!$existingConfig) {
return $response->withJson(Response::notFound(), 404);
}

$parsedBody = $request->getParsedBody();

// Merge the existing config with the new settings
$data = ArrayHelper::merge(
$existingConfig->data,
$parsedBody
);

$configModel = new ConfigModel($args['config'], $data);

// Save the updates to file
$filename = 'config://' . $configModel->id . '.yaml';
$file = YamlFile::instance(
$this->grav['locator']->findResource($filename, true, true)
);
$file->save($configModel->data);
$file->free();

// Reload the site config after changes are saved
$this->grav['config']->reload();

$resource = new ConfigResource($configModel);

$this->grav->fireEvent(Constants::EVENT_ON_API_CONFIG_UPDATE, new Event(['config' => $configModel]));

return $response->withJson($resource->toJson());
}
}