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
17 changes: 13 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,25 @@
],
"require": {
"php": ">=8.1.0",
"illuminate/contracts": "^10.0|^11.0|^12.0",
"illuminate/support": "^10.0|^11.0|^12.0",
"workos/workos-php": "^v4.29.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^2.15 || ^3.6",
"phpunit/phpunit": "^5.7 || ^10.1"
"friendsofphp/php-cs-fixer": "^3.64",
"phpunit/phpunit": "^10.1 || ^11.0",
"orchestra/testbench": "^8.0|^9.0|^10.0"
},
"suggest": {
"laravel/framework": "For testing"
},
"autoload": {
"psr-4": {
"WorkOS\\Laravel\\": "lib/"
}
},
"files": [
"lib/helpers.php"
]
},
"autoload-dev": {
"psr-4": {
Expand All @@ -45,7 +51,10 @@
"laravel": {
"providers": [
"WorkOS\\Laravel\\WorkOSServiceProvider"
]
],
"aliases": {
"WorkOS": "WorkOS\\Laravel\\Facades\\WorkOS"
}
}
},
"scripts": {
Expand Down
26 changes: 26 additions & 0 deletions lib/Facades/WorkOS.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace WorkOS\Laravel\Facades;

use Illuminate\Support\Facades\Facade;

/**
* @method static \WorkOS\AuditLogs auditLogs()
* @method static \WorkOS\DirectorySync directorySync()
* @method static \WorkOS\MFA mfa()
* @method static \WorkOS\Organizations organizations()
* @method static \WorkOS\Portal portal()
* @method static \WorkOS\SSO sso()
* @method static \WorkOS\UserManagement userManagement()
*
* @see \WorkOS\Laravel\WorkOSService
*/
class WorkOS extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'workos';
}
}
71 changes: 71 additions & 0 deletions lib/Services/WorkOSService.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?php

declare(strict_types=1);

namespace WorkOS\Laravel\Services;

use InvalidArgumentException;
use WorkOS\AuditLogs;
use WorkOS\DirectorySync;
use WorkOS\MFA;
use WorkOS\Organizations;
use WorkOS\Portal;
use WorkOS\SSO;
use WorkOS\UserManagement;

/**
* A singleton class that provides a fluent interface for accessing WorkOS services
* in a Laravel Application.
*
* @method \WorkOS\AuditLogs auditLogs()
* @method \WorkOS\DirectorySync directorySync()
* @method \WorkOS\MFA mfa()
* @method \WorkOS\Organizations organizations()
* @method \WorkOS\Portal portal()
* @method \WorkOS\SSO sso()
* @method \WorkOS\UserManagement userManagement()
*/
class WorkOSService
{
/**
* The array of cached service instances
*
* @var array
*/
private $instances = [];

/**
* Map of supported services to their class names
*
* @var array
*/
private $serviceMap = [
'auditLogs' => AuditLogs::class,
'directorySync' => DirectorySync::class,
'mfa' => MFA::class,
'organizations' => Organizations::class,
'portal' => Portal::class,
'sso' => SSO::class,
'userManagement' => UserManagement::class,
];

/**
* Dynamically resolve a WorkOS service.
*
* @param string $name
* @param array $arguments
* @return mixed
*/
public function __call($name, $arguments)
{
if (! array_key_exists($name, $this->serviceMap)) {
throw new InvalidArgumentException("WorkOS service [$name] is not supported.");
}

if (isset($this->instances[$name])) {
return $this->instances[$name];
}

return $this->instances[$name] = $arguments ? new $this->serviceMap[$name]($arguments) : new $this->serviceMap[$name];
}
}
39 changes: 27 additions & 12 deletions lib/WorkOSServiceProvider.php
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
<?php

declare(strict_types=1);

namespace WorkOS\Laravel;

use Illuminate\Support\ServiceProvider;
use WorkOS\Laravel\Services\WorkOSService;

/**
* Class WorkOSServiceProvider.
Expand All @@ -12,30 +15,42 @@ class WorkOSServiceProvider extends ServiceProvider
/**
* Bootstrap the ServiceProvider.
*/
public function boot()
public function boot(): void
{
if ($this->app->runningInConsole()) {
$this->publishes(
[__DIR__."/../config/workos.php" => config_path("workos.php")]
[__DIR__.'/../config/workos.php' => config_path('workos.php')],
'workos-config'
);
}
}

/**
* Register the ServiceProvider as well as setup WorkOS.
*/
public function register()
public function register(): void
{
$this->mergeConfigFrom(__DIR__."/../config/workos.php", "workos");
$this->mergeConfigFrom(__DIR__.'/../config/workos.php', 'workos');

$config = $this->app["config"]->get("workos");
\WorkOS\WorkOS::setApiKey($config["api_key"]);
\WorkOS\WorkOS::setClientId($config["client_id"]);
\WorkOS\WorkOS::setIdentifier(\WorkOS\Laravel\Version::SDK_IDENTIFIER);
\WorkOS\WorkOS::setVersion(\WorkOS\Laravel\Version::SDK_VERSION);
// Ensures that the WorkOS service is configured only once, rather than every request
$this->app->singleton('workos', function ($app) {
$config = $app['config']->get('workos');

if ($config["api_base_url"]) {
\WorkOS\WorkOS::setApiBaseUrl($config["api_base_url"]);
}
\WorkOS\WorkOS::setApiKey($config['api_key']);
\WorkOS\WorkOS::setClientId($config['client_id']);
\WorkOS\WorkOS::setIdentifier(Version::SDK_IDENTIFIER);
\WorkOS\WorkOS::setVersion(Version::SDK_VERSION);

if ($config['api_base_url']) {
\WorkOS\WorkOS::setApiBaseUrl($config['api_base_url']);
}

return new WorkOSService;
});

// Allows for dependency injection (e.g. `show(WorkOSService $service)`)
// while still ensuring we're using the configured singleton rather than
// potentially generating a new, unconfigured version of the singleton
$this->app->alias('workos', WorkOSService::class);
}
}
17 changes: 17 additions & 0 deletions lib/helpers.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

use WorkOS\Laravel\Services\WorkOSService;

if (! function_exists('workos')) {
/**
* Access the WorkOS Manager.
*
* @return \WorkOS\Laravel\WorkOSManager
*/
function workos()
{
return app(WorkOSService::class);
}
}
50 changes: 50 additions & 0 deletions tests/WorkOS/WorkOSFacadeTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

namespace WorkOS\Laravel;

use WorkOS\Laravel\Facades\WorkOS;
use WorkOS\Laravel\Services\WorkOSService;

class WorkOSFacadeTest extends LaravelTestCase
{
protected $app;

protected function setUp(): void
{
$this->app = $this->setupApplication();
$this->setDefaultConfig();
$this->setupProvider($this->app);
}

protected function setDefaultConfig(array $overrides = []): void
{
$defaults = [
'api_key' => 'pk_test',
'client_id' => 'client_test',
];

foreach (array_merge($defaults, $overrides) as $key => $value) {
$this->app['config']->set("workos.{$key}", $value);
}
}

public function test_facade_resolves_workos_service()
{
WorkOS::setFacadeApplication($this->app);

$this->assertInstanceOf(\WorkOS\UserManagement::class, WorkOS::userManagement());
}

public function test_facade_provides_access_to_all_services()
{
WorkOS::setFacadeApplication($this->app);

$this->assertInstanceOf(\WorkOS\AuditLogs::class, WorkOS::auditLogs());
$this->assertInstanceOf(\WorkOS\DirectorySync::class, WorkOS::directorySync());
$this->assertInstanceOf(\WorkOS\MFA::class, WorkOS::mfa());
$this->assertInstanceOf(\WorkOS\Organizations::class, WorkOS::organizations());
$this->assertInstanceOf(\WorkOS\Portal::class, WorkOS::portal());
$this->assertInstanceOf(\WorkOS\SSO::class, WorkOS::sso());
$this->assertInstanceOf(\WorkOS\UserManagement::class, WorkOS::userManagement());
}
}
96 changes: 88 additions & 8 deletions tests/WorkOS/WorkOSServiceProviderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,104 @@

namespace WorkOS\Laravel;

use WorkOS\Laravel\Services\WorkOSService;

class WorkOSServiceProviderTest extends LaravelTestCase
{
protected $app;

protected function setUp(): void
{
$this->app = $this->setupApplication();
$this->setDefaultConfig();
$this->setupProvider($this->app);
}

public function testRegisterWorkOSServiceProviderYieldsExpectedConfig()
protected function setDefaultConfig(array $overrides = []): void
{
$this->app["config"]->set("workos.api_key", "pk_secretsauce");
$this->app["config"]->set("workos.client_id", "client_pizza");
$this->app["config"]->set("workos.api_base_url", "https://workos-hop.com/");
$this->setupProvider($this->app);
$defaults = [
'api_key' => 'pk_test',
'client_id' => 'client_test',
];

foreach (array_merge($defaults, $overrides) as $key => $value) {
$this->app['config']->set("workos.{$key}", $value);
}
}

public function test_register_work_os_service_provider_yields_expected_config()
{
$this->setDefaultConfig([
'api_key' => 'pk_secretsauce',
'client_id' => 'client_pizza',
'api_base_url' => 'https://workos-hop.com/',
]);

// Resolve the service to trigger lazy initialization
$this->app->make('workos');

$this->assertEquals('pk_secretsauce', \WorkOS\WorkOS::getApiKey());
$this->assertEquals('client_pizza', \WorkOS\WorkOS::getClientId());
$this->assertEquals('https://workos-hop.com/', \WorkOS\WorkOS::getApiBaseUrl());
}

public function test_workos_helper_function_returns_work_os_service_instance()
{
$this->assertInstanceOf(WorkOSService::class, workos());
}

public function test_workos_helper_function_enables_fluent_access()
{
$this->assertInstanceOf(\WorkOS\UserManagement::class, workos()->userManagement());
}

public function test_it_resolves_service_via_injection_and_configures_sdk()
{
$service = $this->app->make(WorkOSService::class);

$this->assertInstanceOf(WorkOSService::class, $service);
$this->assertSame($service, $this->app->make('workos'));
$this->assertSame($service, workos());
}

public function test_workos_service_resolves_all_supported_services()
{
$service = workos();

$this->assertInstanceOf(\WorkOS\AuditLogs::class, $service->auditLogs());
$this->assertInstanceOf(\WorkOS\DirectorySync::class, $service->directorySync());
$this->assertInstanceOf(\WorkOS\MFA::class, $service->mfa());
$this->assertInstanceOf(\WorkOS\Organizations::class, $service->organizations());
$this->assertInstanceOf(\WorkOS\Portal::class, $service->portal());
$this->assertInstanceOf(\WorkOS\SSO::class, $service->sso());
$this->assertInstanceOf(\WorkOS\UserManagement::class, $service->userManagement());
}

public function test_workos_service_caches_service_instances()
{
$service = workos();

$userManagement1 = $service->userManagement();
$userManagement2 = $service->userManagement();

$this->assertSame($userManagement1, $userManagement2);
}

public function test_workos_service_throws_exception_for_unsupported_service()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('WorkOS service [unsupportedService] is not supported.');

$service = workos();
$service->unsupportedService();
}

public function test_api_base_url_is_set_when_provided()
{
$this->setDefaultConfig(['api_base_url' => 'https://custom-api.workos.com/']);

$this->app->make('workos');

$this->assertEquals("pk_secretsauce", \WorkOS\WorkOS::getApiKey());
$this->assertEquals("client_pizza", \WorkOS\WorkOS::getClientId());
$this->assertEquals("https://workos-hop.com/", \WorkOS\WorkOS::getApiBaseUrl());
$this->assertEquals('https://custom-api.workos.com/', \WorkOS\WorkOS::getApiBaseUrl());
}
}