Skip to content

Commit

Permalink
Merge pull request #31 from rupadana/dev
Browse files Browse the repository at this point in the history
feat: Tenancy Support
  • Loading branch information
rupadana authored Mar 10, 2024
2 parents 11669cd + ca0230a commit 286d4ca
Show file tree
Hide file tree
Showing 24 changed files with 2,331 additions and 123 deletions.
27 changes: 0 additions & 27 deletions .github/workflows/fix-php-code-styling.yml

This file was deleted.

2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.DS_Store
.idea
.phpunit.result.cache
.phpunit.*
.vscode
build
composer.lock
Expand Down
6 changes: 6 additions & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#!/bin/sh

# Fix Styling
vendor/bin/pint

git add .
4 changes: 4 additions & 0 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh

# Running Test
vendor/bin/pest --ci
1 change: 0 additions & 1 deletion .phpunit.cache/test-results

This file was deleted.

95 changes: 48 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# A simple api service for supporting filamentphp
# Filament Api Service

[![Total Downloads](https://img.shields.io/packagist/dt/rupadana/filament-api-service.svg?style=flat-square)](https://packagist.org/packages/rupadana/filament-api-service)
![Fix Code](https://github.com/rupadana/filament-api-service/actions/workflows/fix-php-code-styling.yml/badge.svg?branch=main)
![Run Test](https://github.com/rupadana/filament-api-service/actions/workflows/run-tests.yml/badge.svg?branch=main)

A simple API service for supporting FilamentPHP

## Installation

Expand All @@ -15,7 +16,6 @@ composer require rupadana/filament-api-service

Register it to your filament Provider


```php
use Rupadana\ApiService\ApiServicePlugin;

Expand All @@ -24,7 +24,7 @@ $panel->plugins([
])
```

### Publish config
### Config

```bash
php artisan vendor:publish --tag=api-service-config
Expand All @@ -48,6 +48,10 @@ return [
'route' => [
'panel_prefix' => true,
],
'tenancy' => [
'enabled' => false,
'awareness' => false,
]
];
```

Expand All @@ -61,20 +65,19 @@ Since version 3.0, routes automatically registered. it will grouped as '/api/`ad

So, You don't need to register the routes manually.

The routes will be :
The routes will be :

- [GET] '/api/`admin`/blogs' - Return LengthAwarePaginator
- [GET] '/api/`admin`/blogs/1' - Return single resource
- [GET] '/api/`admin`/blogs' - Return LengthAwarePaginator
- [GET] '/api/`admin`/blogs/1' - Return single resource
- [PUT] '/api/`admin`/blogs/1' - Update resource
- [POST] '/api/`admin`/blogs' - Create resource
- [DELETE] '/api/`admin`/blogs/1' - Delete resource

On CreateHandler, you need to be create your custom request validation.

### Token Resource

## Token Resource

By default, Token resource only show on `super_admin` role. you can modify give permission to other permission too.
By default, Token resource only show on `super_admin` role. you can modify give permission to other permission too.

Token Resource is protected by TokenPolicy. You can disable it by publishing the config and change this line.

Expand All @@ -86,7 +89,7 @@ Token Resource is protected by TokenPolicy. You can disable it by publishing the
],
```

## Filtering & Allowed Field
### Filtering & Allowed Field

We used `"spatie/laravel-query-builder": "^5.3"` to handle query selecting, sorting and filtering. Check out [the spatie/laravel-query-builder documentation](https://spatie.be/docs/laravel-query-builder/v5/introduction) for more information.
You can specified `allowedFilters` and `allowedFields` in your model. For example:
Expand All @@ -111,29 +114,28 @@ class User extends Model {
}
```

## Create a Handler
### Create a Handler

To create a handler you can use this command. By default, i'm using CreateHandler

```bash
php artisan make:filament-api-handler BlogResource
```
```

or

```bash
php artisan make:filament-api-handler Blog
```
```

## Transform API Response
### Transform API Response

```bash
```bash
php artisan make:filament-api-transformer Blog
```

it will be create BlogTransformer in `App\Filament\Resources\BlogResource\Api\Transformers`


```php
<?php
namespace App\Filament\Resources\BlogResource\Api\Transformers;
Expand All @@ -160,7 +162,6 @@ class BlogTransformer extends JsonResource
}
```


next step you need to edit & add it to your Resource

```php
Expand All @@ -169,16 +170,15 @@ next step you need to edit & add it to your Resource
class BlogResource extends Resource
{
...
public static function getApiTransformer()
{
return BlogTransformer::class;
public static function getApiTransformer()
{
return BlogTransformer::class;
}
...
}
```


## Group Name & Prefix
### Group Name & Prefix

You can edit prefix & group route name as you want, default this plugin use model singular label;

Expand All @@ -191,49 +191,50 @@ You can edit prefix & group route name as you want, default this plugin use mode
}
```

## How to secure it?
### Tenancy

Since version 3.0, it will automatically detect routes and secure it using sanctum.
When you want to enable Tenancy on this package you can enable this by setting the config `tenancy.enabled` to `true`. This makes sure that your api responses only retreive the data which that user has access to. So if you have configured 5 tenants and an user has access to 2 tenants. Then, enabling this feature will return only the data of those 2 tenants.

To Generate Token, you just need create it from admin panel. It will be Token Resource there.
If you have enabled tenancy on this package but on a specific Resource you have defined `protected static bool $isScopedToTenant = false;`, then the API will honour this for that specific resource and will return all records.

![Image](https://res.cloudinary.com/rupadana/image/upload/v1704958748/Screenshot_2024-01-11_at_15.37.55_ncpg8n.png)
If you want to make api routes tenant aware. you can set `tenancy.awareness` to `true` in your published api-service.php. This way this package will register extra API routes which will return only the specific tenant data in the API response.

## Public API
Now your API endpoints will have URI prefix of `{tenant}` in the API routes when `tenancy.awareness` is `true`.

Set API to public by overriding this property on your API Handler. Assume we have a `PaginationHandler`
It will look like this:

```php
class PaginationHandler extends Handlers {
public static bool $public = true;
}
```bash
POST api/admin/{tenant}/blog
GET|HEAD api/admin/{tenant}/blog
PUT api/admin/{tenant}/blog/{id}
DELETE api/admin/{tenant}/blog/{id}
GET|HEAD api/admin/{tenant}/blog/{id}
```

## TODO

- [ ] Test Plugin for Tenancy purpose
- [ ] Each user can manage their own token only

## Changelog
Overriding tenancy ownership relationship name by adding this property to the Handlers `protected static ?string $tenantOwnershipRelationshipName = null;`

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.
### How to secure it?

## Contributing
Since version 3.0, it will automatically detect routes and secure it using sanctum.

Please see [CONTRIBUTING](CONTRIBUTING.md) for details.
To Generate Token, you just need create it from admin panel. It will be Token Resource there.

## Security Vulnerabilities
![Image](https://res.cloudinary.com/rupadana/image/upload/v1704958748/Screenshot_2024-01-11_at_15.37.55_ncpg8n.png)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.
### Public API

## Credits
Set API to public by overriding this property on your API Handler. Assume we have a `PaginationHandler`

- [Rupadana](https://github.com/rupadana)
- [All Contributors](../../contributors)
```php
class PaginationHandler extends Handlers {
public static bool $public = true;
}
```

## License

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
The MIT License (MIT).

## Supported By

<img src="https://res.cloudinary.com/rupadana/image/upload/v1707040287/phpstorm_xjblau.png" width="50px" height="50px"></img>
4 changes: 4 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,10 @@
},
"scripts": {
"post-autoload-dump": "@php ./vendor/bin/testbench package:discover --ansi",
"post-install-cmd": [
"yarn install",
"npx husky init"
],
"test": "vendor/bin/pest",
"test-coverage": "vendor/bin/pest --coverage"
},
Expand Down
4 changes: 4 additions & 0 deletions config/api-service.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@
'route' => [
'panel_prefix' => true,
],
'tenancy' => [
'enabled' => false,
'awareness' => false,
],
];
55 changes: 31 additions & 24 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
{
"private": true,
"type": "module",
"scripts": {
"dev:styles": "npx tailwindcss -i resources/css/index.css -o resources/dist/api-service.css --postcss --watch",
"dev:scripts": "node bin/build.js --dev",
"build:styles": "npx tailwindcss -i resources/css/index.css -o resources/dist/api-service.css --postcss --minify && npm run purge",
"build:scripts": "node bin/build.js",
"purge": "filament-purge -i resources/dist/api-service.css -o resources/dist/api-service.css -v 3.x",
"dev": "npm-run-all --parallel dev:*",
"build": "npm-run-all build:*"
},
"devDependencies": {
"@awcodes/filament-plugin-purge": "^1.1.1",
"@tailwindcss/forms": "^0.5.4",
"@tailwindcss/typography": "^0.5.9",
"autoprefixer": "^10.4.14",
"esbuild": "^0.19.2",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.26",
"postcss-import": "^15.1.0",
"prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.13",
"tailwindcss": "^3.3.3"
}
"private": true,
"type": "module",
"scripts": {
"dev:styles": "npx tailwindcss -i resources/css/index.css -o resources/dist/api-service.css --postcss --watch",
"dev:scripts": "node bin/build.js --dev",
"build:styles": "npx tailwindcss -i resources/css/index.css -o resources/dist/api-service.css --postcss --minify && npm run purge",
"build:scripts": "node bin/build.js",
"purge": "filament-purge -i resources/dist/api-service.css -o resources/dist/api-service.css -v 3.x",
"dev": "npm-run-all --parallel dev:*",
"build": "npm-run-all build:*",
"prepare": "husky"
},
"devDependencies": {
"@awcodes/filament-plugin-purge": "^1.1.1",
"@tailwindcss/forms": "^0.5.4",
"@tailwindcss/typography": "^0.5.9",
"autoprefixer": "^10.4.14",
"esbuild": "^0.19.2",
"husky": "^9.0.11",
"npm-run-all": "^4.1.5",
"postcss": "^8.4.26",
"postcss-import": "^15.1.0",
"prettier": "^2.7.1",
"prettier-plugin-tailwindcss": "^0.1.13",
"tailwindcss": "^3.3.3"
},
"lint-staged": {
"**/*.php*": [
"vendor/bin/pint"
]
}
}
41 changes: 33 additions & 8 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,46 @@
use Filament\Facades\Filament;
use Illuminate\Support\Facades\Route;
use Rupadana\ApiService\ApiService;
use Rupadana\ApiService\Exceptions\InvalidTenancyConfiguration;

Route::prefix('api')
->name('api.')
->name('api')
->group(function () {
if (ApiService::tenancyAwareness() && (! ApiService::isRoutePrefixedByPanel() || ! ApiService::isTenancyEnabled())) {
throw new InvalidTenancyConfiguration("Tenancy awareness is enabled!. Please set 'api-service.route.panel_prefix=true' and 'api-service.tenancy.enabled=true'");
}

$panels = Filament::getPanels();

foreach ($panels as $key => $panel) {
try {
$panelPrefix = ApiService::isRoutePrefixedByPanel() ? $panel->getId() : '';
Route::name($panelPrefix)
->prefix($panelPrefix)
->group(function () use ($panel) {
$panel->getPlugin('api-service')
->route($panel);
});

$hasTenancy = $panel->hasTenancy();
$tenantRoutePrefix = $panel->getTenantRoutePrefix();
$tenantSlugAttribute = $panel->getTenantSlugAttribute();
$panelRoutePrefix = ApiService::isRoutePrefixedByPanel() ? '{panel}' : '';

if (
$hasTenancy &&
ApiService::isTenancyEnabled() &&
ApiService::tenancyAwareness()
) {
Route::prefix($panelRoutePrefix . '/' . (($tenantRoutePrefix) ? "{$tenantRoutePrefix}/" : '') . '{tenant' . (($tenantSlugAttribute) ? ":{$tenantSlugAttribute}" : '') . '}')
->name($panelRoutePrefix . '.')
->group(function () use ($panel) {
$apiServicePlugin = $panel->getPlugin('api-service');
$apiServicePlugin->route($panel);
});
}

if (! ApiService::tenancyAwareness()) {
Route::prefix($panelRoutePrefix)
->name($panelRoutePrefix . '.')
->group(function () use ($panel) {
$apiServicePlugin = $panel->getPlugin('api-service');
$apiServicePlugin->route($panel);
});
}
} catch (Exception $e) {
}
}
Expand Down
Loading

0 comments on commit 286d4ca

Please sign in to comment.