From ad15186fe955cefea8b1f3176610a029c0148f0f Mon Sep 17 00:00:00 2001 From: Rolf van de Krol Date: Mon, 20 Nov 2023 14:37:27 +0100 Subject: [PATCH 1/9] feat: refactor lots of different areas of the code --- Commands/DeleteExpiredLtiNonces.php | 20 ------ README.md | 2 +- config/lti-provider.php | 14 ++-- ...p => 2023_10_26_100000_add_lti_tables.php} | 43 ++----------- src/Commands/DeleteExpiredLtiNonces.php | 4 +- src/LtiServiceProvider.php | 3 +- src/ModelDataConnector.php | 64 +++++++++++++------ src/Models/Contracts/LtiAccessToken.php | 20 ------ src/Models/Contracts/LtiClient.php | 8 +-- src/Models/Contracts/LtiContext.php | 26 -------- src/Models/Contracts/LtiEnvironment.php | 10 +-- src/Models/Contracts/LtiNonce.php | 23 ------- src/Models/Contracts/LtiResourceLink.php | 32 ---------- src/Models/Contracts/LtiUserResult.php | 20 ------ src/Models/LtiAccessToken.php | 38 ++++------- src/Models/LtiContext.php | 44 ++++--------- src/Models/LtiNonce.php | 38 ++++------- src/Models/LtiResourceLink.php | 47 +++++--------- src/Models/LtiUserResult.php | 14 ++-- ...illities.php => HasClientCapabilities.php} | 18 +++--- src/Models/Traits/HasLtiClient.php | 22 +++++++ src/Models/Traits/HasLtiEnvironment.php | 11 +++- src/Models/Traits/IsLtiEnvironment.php | 23 +++---- 23 files changed, 178 insertions(+), 366 deletions(-) delete mode 100644 Commands/DeleteExpiredLtiNonces.php rename database/migrations/{2023_10_26_100000_add_client_and_lti_tables.php => 2023_10_26_100000_add_lti_tables.php} (59%) delete mode 100644 src/Models/Contracts/LtiAccessToken.php delete mode 100644 src/Models/Contracts/LtiContext.php delete mode 100644 src/Models/Contracts/LtiNonce.php delete mode 100644 src/Models/Contracts/LtiResourceLink.php delete mode 100644 src/Models/Contracts/LtiUserResult.php rename src/Models/Traits/{HasLtiClientCapabillities.php => HasClientCapabilities.php} (55%) create mode 100644 src/Models/Traits/HasLtiClient.php diff --git a/Commands/DeleteExpiredLtiNonces.php b/Commands/DeleteExpiredLtiNonces.php deleted file mode 100644 index 76b0598..0000000 --- a/Commands/DeleteExpiredLtiNonces.php +++ /dev/null @@ -1,20 +0,0 @@ - '', - 'lti-context' => \Swis\Laravel\LtiProvider\Models\LtiContext::class, - 'lti-resource-link' => \Swis\Laravel\LtiProvider\Models\LtiResourceLink::class, - 'lti-nonce' => \Swis\Laravel\LtiProvider\Models\LtiNonce::class, - 'lti-user-result' => \Swis\Laravel\LtiProvider\Models\LtiUserResult::class, - 'lti-access-token' => \Swis\Laravel\LtiProvider\Models\LtiAccessToken::class, + 'class-names' => [ + 'lti-client' => '', + 'lti-context' => \Swis\Laravel\LtiProvider\Models\LtiContext::class, + 'lti-resource-link' => \Swis\Laravel\LtiProvider\Models\LtiResourceLink::class, + 'lti-nonce' => \Swis\Laravel\LtiProvider\Models\LtiNonce::class, + 'lti-user-result' => \Swis\Laravel\LtiProvider\Models\LtiUserResult::class, + 'lti-access-token' => \Swis\Laravel\LtiProvider\Models\LtiAccessToken::class, + ], ]; diff --git a/database/migrations/2023_10_26_100000_add_client_and_lti_tables.php b/database/migrations/2023_10_26_100000_add_lti_tables.php similarity index 59% rename from database/migrations/2023_10_26_100000_add_client_and_lti_tables.php rename to database/migrations/2023_10_26_100000_add_lti_tables.php index d9eff03..8ccfebc 100644 --- a/database/migrations/2023_10_26_100000_add_client_and_lti_tables.php +++ b/database/migrations/2023_10_26_100000_add_lti_tables.php @@ -11,46 +11,12 @@ */ public function up(): void { - Schema::create('clients', function (Blueprint $table) { - $table->uuid('id')->primary(); - $table->integer('nr')->unique(); - - // Admin title - $table->string('name'); - - // Keys & secrets - $table->string('secret', 1024)->nullable(); - $table->text('public_key')->nullable(); - - // Branding - $table->text('redirect'); - $table->string('home_url')->nullable(); - $table->string('logo')->nullable(); - - // Policies - $table->boolean('revoked')->default(false); - - // LTI information - $table->string('lti_platform_id', 255)->nullable(); - $table->string('lti_client_id', 255)->nullable(); - $table->string('lti_deployment_id', 255)->nullable(); - $table->string('lti_version', 10)->nullable(); - $table->string('lti_signature_method', 15)->default('HMAC-SHA1'); - $table->text('lti_profile'); - $table->text('lti_settings'); - $table->string('lti_user_type')->default('external_user'); - - $table->timestamps(); - - $table->unique(['lti_platform_id', 'lti_client_id', 'lti_deployment_id']); - }); - Schema::create('lti_nonces', function (Blueprint $table) { $table->uuid('id')->primary(); $table->uuidMorphs('lti_environment'); - $table->foreignUuid('client_id')->constrained('clients')->cascadeOnDelete(); + $table->uuid('client_id'); $table->string('nonce', 50); $table->dateTime('expires_at'); @@ -64,7 +30,7 @@ public function up(): void $table->uuidMorphs('lti_environment'); - $table->foreignUuid('client_id')->unique()->constrained('clients')->cascadeOnDelete(); + $table->uuid('client_id')->unique(); $table->string('access_token', 2000); $table->text('scopes'); @@ -78,7 +44,7 @@ public function up(): void $table->uuidMorphs('lti_environment'); - $table->foreignUuid('client_id')->constrained('clients')->cascadeOnDelete(); + $table->uuid('client_id'); $table->string('title')->nullable(); $table->string('external_context_id', 255); $table->text('settings'); @@ -91,7 +57,7 @@ public function up(): void $table->uuidMorphs('lti_environment'); - $table->foreignUuid('client_id')->nullable()->constrained('clients')->cascadeOnDelete(); + $table->uuid('client_id'); $table->foreignId('lti_context_id')->nullable()->constrained('lti_contexts')->cascadeOnDelete(); $table->string('title')->nullable(); @@ -125,6 +91,5 @@ public function down(): void Schema::drop('lti_contexts'); Schema::drop('lti_access_tokens'); Schema::drop('lti_nonces'); - Schema::drop('clients'); } }; diff --git a/src/Commands/DeleteExpiredLtiNonces.php b/src/Commands/DeleteExpiredLtiNonces.php index 76b0598..10d2f9d 100644 --- a/src/Commands/DeleteExpiredLtiNonces.php +++ b/src/Commands/DeleteExpiredLtiNonces.php @@ -7,7 +7,7 @@ class DeleteExpiredLtiNonces extends Command { - protected $signature = 'lti:delete-expired-nonce'; + protected $signature = 'lti:delete-expired-nonces'; protected $description = 'Cleanup the expired LTI nonces from the database'; @@ -15,6 +15,6 @@ public function handle(): int { LtiNonce::deleteExpired(); - return Command::SUCCESS; + return static::SUCCESS; } } diff --git a/src/LtiServiceProvider.php b/src/LtiServiceProvider.php index 78afda0..46bda0b 100644 --- a/src/LtiServiceProvider.php +++ b/src/LtiServiceProvider.php @@ -14,9 +14,8 @@ public function configurePackage(Package $package): void { $package ->name('lti-service-provider') - ->hasMigration('2023_10_26_100000_add_client_and_lti_tables') + ->hasMigration('2023_10_26_100000_add_lti_tables') ->publishesServiceProvider('LtiServiceProvider') - ->runsMigrations() ->hasConfigFile('lti-provider') ->hasCommand(DeleteExpiredLtiNonces::class) ->hasInstallCommand(function (InstallCommand $command) { diff --git a/src/ModelDataConnector.php b/src/ModelDataConnector.php index e3df4bf..c4aa573 100644 --- a/src/ModelDataConnector.php +++ b/src/ModelDataConnector.php @@ -14,6 +14,7 @@ use ceLTIc\LTI\ResourceLinkShareKey; use ceLTIc\LTI\Tool; use ceLTIc\LTI\UserResult; +use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Carbon; use Illuminate\Support\Collection; @@ -21,34 +22,35 @@ use Swis\Laravel\LtiProvider\Models\Contracts\LtiEnvironment; use Swis\Laravel\LtiProvider\Models\LtiUserResult; +/** @phpstan-consistent-constructor */ class ModelDataConnector extends DataConnector { - protected Model $client; + protected LtiEnvironment $environment; - public function __construct(protected LtiEnvironment $environment) + /** @var class-string */ + protected string $clientClassName; + + public function __construct(LtiEnvironment $environment, string $clientClassName) { parent::__construct((object) []); - $client = config('lti-provider.lti-client'); - - if ($client === '') { - abort(500, 'please provide an lti client in the lti-provider config'); - } - - $client = new $client(); - - if (! $client instanceof LtiClient || ! $client instanceof Model) { - abort(500, 'Client must implement LtiClient interface'); - } + $this->environment = $environment; + $this->clientClassName = $clientClassName; + } - $this->client = $client; + /** + * @return \Illuminate\Database\Eloquent\Builder + */ + protected function getClientBuilder(): Builder + { + return $this->clientClassName::query(); } public function loadPlatform(Platform $platform): bool { if (! empty($platform->getRecordId())) { /** @var LtiClient|null $client */ - $client = $this->client::firstWhere('nr', $platform->getRecordId()); + $client = $this->getClientBuilder()->firstWhere('nr', $platform->getRecordId()); if (! $client) { return false; } @@ -59,7 +61,7 @@ public function loadPlatform(Platform $platform): bool } if (! empty($platform->platformId) || ! empty($platform->clientId) || ! empty($platform->deploymentId)) { - $query = $this->client::query(); + $query = $this->getClientBuilder(); if (! empty($platform->platformId)) { $query->where('lti_platform_id', $platform->platformId); @@ -84,7 +86,7 @@ public function loadPlatform(Platform $platform): bool if (! empty($platform->getKey())) { /** @var LtiClient|null $client */ - $client = $this->client::find($platform->getKey()); + $client = $this->getClientBuilder()->find($platform->getKey()); if (! $client) { return false; } @@ -100,8 +102,8 @@ public function loadPlatform(Platform $platform): bool public function savePlatform(Platform $platform): bool { if (! empty($platform->getRecordId())) { - /** @var LtiClient|Model|null $client */ - $client = $this->client::firstWhere('nr', $platform->getRecordId()); + /** @var (LtiClient&Model)|null $client */ + $client = $this->getClientBuilder()->firstWhere('nr', $platform->getRecordId()); if (! $client) { return false; } @@ -130,7 +132,7 @@ public function deletePlatform(Platform $platform): bool public function getPlatforms(): array { /** @var Collection $clients. */ - $clients = $this->client::all(); + $clients = $this->getClientBuilder()->get(); return $clients->map(function (LtiClient $client) { $platform = new Platform($this); @@ -542,4 +544,26 @@ public function getTools(): array { throw new \Exception('getTools not implemented'); } + + public static function make(LtiEnvironment $environment): static + { + $clientClassName = config('lti-provider.class-names.lti-client'); + if ($clientClassName === '') { + abort(500, 'please provide an lti client in the lti-provider config'); + } + + if (! class_exists($clientClassName)) { + abort(500, 'Lti client class does not exist'); + } + + if (! is_subclass_of($clientClassName, Model::class)) { + abort(500, 'Lti client class must be a subclass of '.Model::class); + } + + if (! is_subclass_of($clientClassName, LtiClient::class)) { + abort(500, 'Lti client class must be an implementation of '.LtiClient::class); + } + + return new static($environment, $clientClassName); + } } diff --git a/src/Models/Contracts/LtiAccessToken.php b/src/Models/Contracts/LtiAccessToken.php deleted file mode 100644 index 0ff9d18..0000000 --- a/src/Models/Contracts/LtiAccessToken.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function client(): BelongsTo; - - public function fillLtiAccessToken(AccessToken $accessToken): void; - - public function fillFromLtiAccessToken(AccessToken $accessToken): void; -} diff --git a/src/Models/Contracts/LtiClient.php b/src/Models/Contracts/LtiClient.php index efbbeff..243f9ef 100644 --- a/src/Models/Contracts/LtiClient.php +++ b/src/Models/Contracts/LtiClient.php @@ -19,22 +19,22 @@ public function fillLtiPlatform(Platform $platform): void; public function fillFromLtiPlatform(Platform $platform): void; /** - * @return HasMany<\Illuminate\Database\Eloquent\Model> + * @return HasMany<\Swis\Laravel\LtiProvider\Models\LtiResourceLink> */ public function resourceLinks(): HasMany; /** - * @return HasMany<\Illuminate\Database\Eloquent\Model> + * @return HasMany<\Swis\Laravel\LtiProvider\Models\LtiContext> */ public function contexts(): HasMany; /** - * @return HasMany<\Illuminate\Database\Eloquent\Model> + * @return HasMany<\Swis\Laravel\LtiProvider\Models\LtiNonce> */ public function nonces(): HasMany; /** - * @return HasMany<\Illuminate\Database\Eloquent\Model> + * @return HasMany<\Swis\Laravel\LtiProvider\Models\LtiAccessToken> */ public function accessTokens(): HasMany; } diff --git a/src/Models/Contracts/LtiContext.php b/src/Models/Contracts/LtiContext.php deleted file mode 100644 index 6b6ff07..0000000 --- a/src/Models/Contracts/LtiContext.php +++ /dev/null @@ -1,26 +0,0 @@ - - */ - public function client(): BelongsTo; - - /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Swis\Laravel\LtiProvider\Models\LtiResourceLink> - */ - public function resourceLinks(): HasMany; -} diff --git a/src/Models/Contracts/LtiEnvironment.php b/src/Models/Contracts/LtiEnvironment.php index 80fbbf5..2169297 100644 --- a/src/Models/Contracts/LtiEnvironment.php +++ b/src/Models/Contracts/LtiEnvironment.php @@ -7,27 +7,27 @@ interface LtiEnvironment { /** - * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Illuminate\Database\Eloquent\Model> + * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiAccessToken> */ public function accessTokens(): \Illuminate\Database\Eloquent\Relations\MorphMany; /** - * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Illuminate\Database\Eloquent\Model> + * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiContext> */ public function contexts(): \Illuminate\Database\Eloquent\Relations\MorphMany; /** - * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Illuminate\Database\Eloquent\Model> + * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiNonce> */ public function nonces(): \Illuminate\Database\Eloquent\Relations\MorphMany; /** - * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Illuminate\Database\Eloquent\Model> + * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiResourceLink> */ public function resourceLinks(): \Illuminate\Database\Eloquent\Relations\MorphMany; /** - * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Illuminate\Database\Eloquent\Model> + * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiUserResult> */ public function userResults(): \Illuminate\Database\Eloquent\Relations\MorphMany; } diff --git a/src/Models/Contracts/LtiNonce.php b/src/Models/Contracts/LtiNonce.php deleted file mode 100644 index bd2904b..0000000 --- a/src/Models/Contracts/LtiNonce.php +++ /dev/null @@ -1,23 +0,0 @@ - - */ - public function client(): BelongsTo; - - public static function deleteExpired(): void; - - public function fillLtiPlatformNonce(PlatformNonce $nonce): void; - - public function fillFromLtiPlatformNonce(PlatformNonce $nonce): void; -} diff --git a/src/Models/Contracts/LtiResourceLink.php b/src/Models/Contracts/LtiResourceLink.php deleted file mode 100644 index 01055a4..0000000 --- a/src/Models/Contracts/LtiResourceLink.php +++ /dev/null @@ -1,32 +0,0 @@ - - */ - public function client(): BelongsTo; - - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Swis\Laravel\LtiProvider\Models\LtiContext, \Swis\Laravel\LtiProvider\Models\LtiResourceLink> - */ - public function context(): BelongsTo; - - /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Swis\Laravel\LtiProvider\Models\LtiUserResult> - */ - public function userResults(): HasMany; -} diff --git a/src/Models/Contracts/LtiUserResult.php b/src/Models/Contracts/LtiUserResult.php deleted file mode 100644 index 5cea184..0000000 --- a/src/Models/Contracts/LtiUserResult.php +++ /dev/null @@ -1,20 +0,0 @@ - - */ - public function resourceLink(): BelongsTo; -} diff --git a/src/Models/LtiAccessToken.php b/src/Models/LtiAccessToken.php index c5b32cd..002a7f4 100644 --- a/src/Models/LtiAccessToken.php +++ b/src/Models/LtiAccessToken.php @@ -7,48 +7,40 @@ use ceLTIc\LTI\AccessToken; use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Carbon; -use Swis\Laravel\LtiProvider\Models\Contracts\LtiAccessToken as LtiAccessTokenAlias; +use Swis\Laravel\LtiProvider\Models\Traits\HasLtiClient; use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** * \Swis\Laravel\LtiProvider\Models\LtiAccessToken. * - * @property string $id - * @property string $lti_environment_type - * @property string $lti_environment_id - * @property string $client_id - * @property string $access_token - * @property array $scopes - * @property \Illuminate\Support\Carbon $expires_at - * @property \Illuminate\Support\Carbon|null $created_at - * @property \Illuminate\Support\Carbon|null $updated_at - * @property \Swis\Laravel\LtiProvider\Models\Contracts\LtiClient $client - * @property \Illuminate\Database\Eloquent\Model|\Eloquent $ltiEnvironment + * @property string $id + * @property string $access_token + * @property array $scopes + * @property \Illuminate\Support\Carbon $expires_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken newQuery() * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken query() * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken whereAccessToken($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken whereClientId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken whereExpiresAt($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken whereLtiEnvironmentId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken whereLtiEnvironmentType($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken whereScopes($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken whereUpdatedAt($value) - * - * @mixin \Eloquent */ -class LtiAccessToken extends Model implements LtiAccessTokenAlias +class LtiAccessToken extends Model { + use HasLtiClient; use HasLtiEnvironment; use HasUuids; protected $fillable = [ 'client_id', + 'lti_environment_type', + 'lti_environment_id', 'access_token', 'scopes', 'expires_at', @@ -66,14 +58,6 @@ class LtiAccessToken extends Model implements LtiAccessTokenAlias 'scopes' => '[]', ]; - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Illuminate\Database\Eloquent\Model, self> - */ - public function client(): BelongsTo - { - return $this->belongsTo(config('lti-provider.lti-client')); - } - public function fillLtiAccessToken(AccessToken $accessToken): void { $accessToken->scopes = $this->scopes; diff --git a/src/Models/LtiContext.php b/src/Models/LtiContext.php index 2154d4f..c0bf5be 100644 --- a/src/Models/LtiContext.php +++ b/src/Models/LtiContext.php @@ -8,49 +8,41 @@ use Illuminate\Database\Eloquent\Casts\ArrayObject; use Illuminate\Database\Eloquent\Casts\AsArrayObject; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; -use Swis\Laravel\LtiProvider\Models\Contracts\LtiContext as LtiContextInterface; +use Swis\Laravel\LtiProvider\Models\Traits\HasLtiClient; use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** * \Swis\Laravel\LtiProvider\Models\LtiContext. * - * @property int $id - * @property string $lti_environment_type - * @property string $lti_environment_id - * @property string $client_id - * @property string|null $title - * @property string $external_context_id - * @property \Illuminate\Database\Eloquent\Casts\ArrayObject $settings - * @property \Illuminate\Support\Carbon|null $created_at - * @property \Illuminate\Support\Carbon|null $updated_at - * @property \Swis\Laravel\LtiProvider\Models\Contracts\LtiClient $client - * @property \Illuminate\Database\Eloquent\Model|\Eloquent $ltiEnvironment - * @property \Illuminate\Database\Eloquent\Collection $resourceLinks - * @property int|null $resource_links_count + * @property int $id + * @property string|null $title + * @property string $external_context_id + * @property \Illuminate\Database\Eloquent\Casts\ArrayObject $settings + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Illuminate\Database\Eloquent\Collection $resourceLinks + * @property int|null $resource_links_count * * @method static \Illuminate\Database\Eloquent\Builder|LtiContext newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|LtiContext newQuery() * @method static \Illuminate\Database\Eloquent\Builder|LtiContext query() - * @method static \Illuminate\Database\Eloquent\Builder|LtiContext whereClientId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiContext whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiContext whereExternalContextId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiContext whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiContext whereLtiEnvironmentId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiContext whereLtiEnvironmentType($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiContext whereSettings($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiContext whereTitle($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiContext whereUpdatedAt($value) - * - * @mixin \Eloquent */ -class LtiContext extends Model implements LtiContextInterface +class LtiContext extends Model { + use HasLtiClient; use HasLtiEnvironment; protected $fillable = [ 'client_id', + 'lti_environment_type', + 'lti_environment_id', 'external_context_id', 'title', 'settings', @@ -87,19 +79,11 @@ public function fillFromLtiContext(Context $context): void $this->settings = new ArrayObject($context->getSettings()); } - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Illuminate\Database\Eloquent\Model, self> - */ - public function client(): BelongsTo - { - return $this->belongsTo(config('lti-provider.lti-client')); - } - /** * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Swis\Laravel\LtiProvider\Models\LtiResourceLink> */ public function resourceLinks(): HasMany { - return $this->hasMany(LtiResourceLink::class, 'lti_context_id'); + return $this->hasMany(config('lti-provider.class-names.lti-resource-link'), 'lti_context_id'); } } diff --git a/src/Models/LtiNonce.php b/src/Models/LtiNonce.php index 82dd772..83d7e73 100644 --- a/src/Models/LtiNonce.php +++ b/src/Models/LtiNonce.php @@ -7,46 +7,38 @@ use ceLTIc\LTI\PlatformNonce; use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Model; -use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Support\Carbon; -use Swis\Laravel\LtiProvider\Models\Contracts\LtiNonce as LtiNonceInterface; +use Swis\Laravel\LtiProvider\Models\Traits\HasLtiClient; use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** * \Swis\Laravel\LtiProvider\Models\LtiNonce. * - * @property string $id - * @property string $lti_environment_type - * @property string $lti_environment_id - * @property string $client_id - * @property string $nonce - * @property \Illuminate\Support\Carbon $expires_at - * @property \Illuminate\Support\Carbon|null $created_at - * @property \Illuminate\Support\Carbon|null $updated_at - * @property \App\Models\Client $client - * @property \Illuminate\Database\Eloquent\Model|\Eloquent $ltiEnvironment + * @property string $id + * @property string $nonce + * @property \Illuminate\Support\Carbon $expires_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce newQuery() * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce query() - * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce whereClientId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce whereExpiresAt($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce whereLtiEnvironmentId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce whereLtiEnvironmentType($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce whereNonce($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiNonce whereUpdatedAt($value) - * - * @mixin \Eloquent */ -class LtiNonce extends Model implements LtiNonceInterface +class LtiNonce extends Model { + use HasLtiClient; use HasLtiEnvironment; use HasUuids; protected $fillable = [ 'client_id', + 'lti_environment_type', + 'lti_environment_id', 'nonce', 'expires_at', ]; @@ -55,17 +47,9 @@ class LtiNonce extends Model implements LtiNonceInterface 'expires_at' => 'datetime', ]; - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Illuminate\Database\Eloquent\Model, self> - */ - public function client(): BelongsTo - { - return $this->belongsTo(app(\Swis\Laravel\LtiProvider\Models\Contracts\LtiClient::class)); - } - public static function deleteExpired(): void { - self::where('expires_at', '<', now())->delete(); + self::query()->where('expires_at', '<', now())->delete(); } public function fillLtiPlatformNonce(PlatformNonce $nonce): void diff --git a/src/Models/LtiResourceLink.php b/src/Models/LtiResourceLink.php index 6cad947..e152e5e 100644 --- a/src/Models/LtiResourceLink.php +++ b/src/Models/LtiResourceLink.php @@ -10,49 +10,42 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\HasMany; -use Swis\Laravel\LtiProvider\Models\Contracts\LtiResourceLink as LtiResourceLinkAlias; +use Swis\Laravel\LtiProvider\Models\Traits\HasLtiClient; use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** * \Swis\Laravel\LtiProvider\Models\LtiResourceLink. * - * @property int $id - * @property string $lti_environment_type - * @property string $lti_environment_id - * @property string|null $client_id - * @property int|null $lti_context_id - * @property string|null $title - * @property string $external_resource_link_id - * @property \Illuminate\Database\Eloquent\Casts\ArrayObject $settings - * @property \Illuminate\Support\Carbon|null $created_at - * @property \Illuminate\Support\Carbon|null $updated_at - * @property \Swis\Laravel\LtiProvider\Models\Contracts\LtiClient|null $client - * @property \Swis\Laravel\LtiProvider\Models\LtiContext|null $context - * @property \Illuminate\Database\Eloquent\Model $ltiEnvironment - * @property int|null $user_results_count + * @property int $id + * @property int|null $lti_context_id + * @property string|null $title + * @property string $external_resource_link_id + * @property \Illuminate\Database\Eloquent\Casts\ArrayObject $settings + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at + * @property \Swis\Laravel\LtiProvider\Models\LtiContext|null $context + * @property int|null $user_results_count * * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink newQuery() * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink query() - * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereClientId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereCreatedAt($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereExternalResourceLinkId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereLtiContextId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereLtiEnvironmentId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereLtiEnvironmentType($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereSettings($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereTitle($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiResourceLink whereUpdatedAt($value) - * - * @mixin \Eloquent */ -class LtiResourceLink extends Model implements LtiResourceLinkAlias +class LtiResourceLink extends Model { + use HasLtiClient; use HasLtiEnvironment; protected $fillable = [ 'client_id', + 'lti_environment_type', + 'lti_environment_id', 'lti_context_id', 'title', 'external_resource_link_id', @@ -103,20 +96,12 @@ public function fillFromLtiResourceLink(ResourceLink $resourceLink): void $this->settings = new ArrayObject($resourceLink->getSettings()); } - /** - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Illuminate\Database\Eloquent\Model, self> - */ - public function client(): BelongsTo - { - return $this->belongsTo(config('lti-provider.lti-client')); - } - /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo<\Swis\Laravel\LtiProvider\Models\LtiContext, self> */ public function context(): BelongsTo { - return $this->belongsTo(LtiContext::class, 'lti_context_id'); + return $this->belongsTo(config('lti-provider.class-names.lti-context'), 'lti_context_id'); } /** @@ -124,6 +109,6 @@ public function context(): BelongsTo */ public function userResults(): HasMany { - return $this->hasMany(LtiUserResult::class, 'lti_resource_link_id'); + return $this->hasMany(config('lti-provider.class-names.lti-user-result'), 'lti_resource_link_id'); } } diff --git a/src/Models/LtiUserResult.php b/src/Models/LtiUserResult.php index 16ce1e6..29d175a 100644 --- a/src/Models/LtiUserResult.php +++ b/src/Models/LtiUserResult.php @@ -8,21 +8,17 @@ use ceLTIc\LTI\UserResult; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; -use Swis\Laravel\LtiProvider\Models\Contracts\LtiUserResult as LtiUserResultInterface; use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** * \Swis\Laravel\LtiProvider\Models\LtiUserResult. * * @property int $id - * @property string $lti_environment_type - * @property string $lti_environment_id * @property int $lti_resource_link_id * @property string $external_user_id * @property string $external_user_result_id * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at - * @property \Illuminate\Database\Eloquent\Model $ltiEnvironment * @property \Swis\Laravel\LtiProvider\Models\LtiResourceLink $resourceLink * * @method static \Illuminate\Database\Eloquent\Builder|LtiUserResult newModelQuery() @@ -32,18 +28,16 @@ * @method static \Illuminate\Database\Eloquent\Builder|LtiUserResult whereExternalUserId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiUserResult whereExternalUserResultId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiUserResult whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiUserResult whereLtiEnvironmentId($value) - * @method static \Illuminate\Database\Eloquent\Builder|LtiUserResult whereLtiEnvironmentType($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiUserResult whereLtiResourceLinkId($value) * @method static \Illuminate\Database\Eloquent\Builder|LtiUserResult whereUpdatedAt($value) - * - * @mixin \Eloquent */ -class LtiUserResult extends Model implements LtiUserResultInterface +class LtiUserResult extends Model { use HasLtiEnvironment; protected $fillable = [ + 'lti_environment_type', + 'lti_environment_id', 'lti_resource_link_id', 'external_user_id', 'external_user_result_id', @@ -71,6 +65,6 @@ public function fillFromLtiUserResult(UserResult $userResult): void */ public function resourceLink(): BelongsTo { - return $this->belongsTo(\Swis\Laravel\LtiProvider\Models\LtiResourceLink::class, 'lti_resource_link_id'); + return $this->belongsTo(config('lti-provider.class-names.lti-resource-link'), 'lti_resource_link_id'); } } diff --git a/src/Models/Traits/HasLtiClientCapabillities.php b/src/Models/Traits/HasClientCapabilities.php similarity index 55% rename from src/Models/Traits/HasLtiClientCapabillities.php rename to src/Models/Traits/HasClientCapabilities.php index 86eff88..a883848 100644 --- a/src/Models/Traits/HasLtiClientCapabillities.php +++ b/src/Models/Traits/HasClientCapabilities.php @@ -6,37 +6,37 @@ use Illuminate\Database\Eloquent\Relations\HasMany; -trait HasLtiClientCapabillities +trait HasClientCapabilities { /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Illuminate\Database\Eloquent\Model> + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Swis\Laravel\LtiProvider\Models\LtiContext> */ public function contexts(): HasMany { - return $this->hasMany(config('lti-provider.lti-context')); + return $this->hasMany(config('lti-provider.class-names.lti-context')); } /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Illuminate\Database\Eloquent\Model> + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Swis\Laravel\LtiProvider\Models\LtiResourceLink> */ public function resourceLinks(): HasMany { - return $this->hasMany(config('lti-provider.lti-resource-link')); + return $this->hasMany(config('lti-provider.class-names.lti-resource-link')); } /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Illuminate\Database\Eloquent\Model> + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Swis\Laravel\LtiProvider\Models\LtiNonce> */ public function nonces(): HasMany { - return $this->hasMany(config('lti-provider.lti-nonce')); + return $this->hasMany(config('lti-provider.class-names.lti-nonce')); } /** - * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Illuminate\Database\Eloquent\Model> + * @return \Illuminate\Database\Eloquent\Relations\HasMany<\Swis\Laravel\LtiProvider\Models\LtiAccessToken> */ public function accessTokens(): HasMany { - return $this->hasMany(config('lti-provider.lti-access-token')); + return $this->hasMany(config('lti-provider.class-names.lti-access-token')); } } diff --git a/src/Models/Traits/HasLtiClient.php b/src/Models/Traits/HasLtiClient.php new file mode 100644 index 0000000..3b04267 --- /dev/null +++ b/src/Models/Traits/HasLtiClient.php @@ -0,0 +1,22 @@ + + */ + public function client(): BelongsTo + { + return $this->belongsTo(config('lti-provider.class-names.lti-client')); + } +} diff --git a/src/Models/Traits/HasLtiEnvironment.php b/src/Models/Traits/HasLtiEnvironment.php index 9a3a7b8..fb912ba 100644 --- a/src/Models/Traits/HasLtiEnvironment.php +++ b/src/Models/Traits/HasLtiEnvironment.php @@ -6,13 +6,22 @@ use Illuminate\Database\Eloquent\Relations\MorphTo; +/** + * @property string $lti_environment_type + * @property string $lti_environment_id + * @property \Illuminate\Database\Eloquent\Model&\Swis\Laravel\LtiProvider\Models\Contracts\LtiEnvironment $ltiEnvironment + * + * @method static \Illuminate\Database\Eloquent\Builder|static whereLtiEnvironmentId($value) + * @method static \Illuminate\Database\Eloquent\Builder|static whereLtiEnvironmentType($value) + */ trait HasLtiEnvironment { /** - * @return \Illuminate\Database\Eloquent\Relations\MorphTo<\Illuminate\Database\Eloquent\Model,self> + * @return \Illuminate\Database\Eloquent\Relations\MorphTo<\Illuminate\Database\Eloquent\Model&\Swis\Laravel\LtiProvider\Models\Contracts\LtiEnvironment,self> */ public function ltiEnvironment(): MorphTo { + /** @phpstan-ignore-next-line */ return $this->morphTo('lti_environment'); } } diff --git a/src/Models/Traits/IsLtiEnvironment.php b/src/Models/Traits/IsLtiEnvironment.php index 3f7ffb8..ec15e7b 100644 --- a/src/Models/Traits/IsLtiEnvironment.php +++ b/src/Models/Traits/IsLtiEnvironment.php @@ -4,6 +4,7 @@ namespace Swis\Laravel\LtiProvider\Models\Traits; +use Illuminate\Database\Eloquent\Relations\MorphMany; use Swis\Laravel\LtiProvider\ModelDataConnector; trait IsLtiEnvironment @@ -11,45 +12,45 @@ trait IsLtiEnvironment /** * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiAccessToken> */ - public function accessTokens(): \Illuminate\Database\Eloquent\Relations\MorphMany + public function accessTokens(): MorphMany { - return $this->morphMany(\Swis\Laravel\LtiProvider\Models\LtiAccessToken::class, 'lti_environment'); + return $this->morphMany(config('lti-provider.class-names.lti-access-token'), 'lti_environment'); } /** * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiContext> */ - public function contexts(): \Illuminate\Database\Eloquent\Relations\MorphMany + public function contexts(): MorphMany { - return $this->morphMany(\Swis\Laravel\LtiProvider\Models\LtiContext::class, 'lti_environment'); + return $this->morphMany(config('lti-provider.class-names.lti-context'), 'lti_environment'); } /** * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiNonce> */ - public function nonces(): \Illuminate\Database\Eloquent\Relations\MorphMany + public function nonces(): MorphMany { - return $this->morphMany(\Swis\Laravel\LtiProvider\Models\LtiNonce::class, 'lti_environment'); + return $this->morphMany(config('lti-provider.class-names.lti-nonce'), 'lti_environment'); } /** * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiResourceLink> */ - public function resourceLinks(): \Illuminate\Database\Eloquent\Relations\MorphMany + public function resourceLinks(): MorphMany { - return $this->morphMany(\Swis\Laravel\LtiProvider\Models\LtiResourceLink::class, 'lti_environment'); + return $this->morphMany(config('lti-provider.class-names.lti-resource-link'), 'lti_environment'); } /** * @return \Illuminate\Database\Eloquent\Relations\MorphMany<\Swis\Laravel\LtiProvider\Models\LtiUserResult> */ - public function userResults(): \Illuminate\Database\Eloquent\Relations\MorphMany + public function userResults(): MorphMany { - return $this->morphMany(\Swis\Laravel\LtiProvider\Models\LtiUserResult::class, 'lti_environment'); + return $this->morphMany(config('lti-provider.class-names.lti-user-result'), 'lti_environment'); } public function getDataConnector(): ModelDataConnector { - return new \Swis\Laravel\LtiProvider\ModelDataConnector($this); + return ModelDataConnector::make($this); } } From 539ccc60c779c520165085171fcc83f5d1eea8fd Mon Sep 17 00:00:00 2001 From: Rolf van de Krol Date: Tue, 21 Nov 2023 11:09:08 +0100 Subject: [PATCH 2/9] chore: remove unused parts of the package skeleton --- .gitattributes | 1 - composer.json | 5 - configure.php | 266 ------------------------------------------ phpstan-baseline.neon | 0 tests/ArchTest.php | 5 - tests/ExampleTest.php | 5 - tests/Pest.php | 5 - 7 files changed, 287 deletions(-) delete mode 100644 configure.php delete mode 100644 phpstan-baseline.neon delete mode 100644 tests/ArchTest.php delete mode 100644 tests/ExampleTest.php delete mode 100644 tests/Pest.php diff --git a/.gitattributes b/.gitattributes index 9e9519b..510d810 100644 --- a/.gitattributes +++ b/.gitattributes @@ -16,4 +16,3 @@ /testbench.yaml export-ignore /UPGRADING.md export-ignore /phpstan.neon.dist export-ignore -/phpstan-baseline.neon export-ignore diff --git a/composer.json b/composer.json index ba0cfd1..d5d0b44 100644 --- a/composer.json +++ b/composer.json @@ -28,9 +28,6 @@ "nunomaduro/collision": "^7.8", "nunomaduro/larastan": "^2.0.1", "orchestra/testbench": "^8.8", - "pestphp/pest": "^2.20", - "pestphp/pest-plugin-arch": "^2.0", - "pestphp/pest-plugin-laravel": "^2.0", "phpstan/extension-installer": "^1.1", "phpstan/phpstan-deprecation-rules": "^1.0", "phpstan/phpstan-phpunit": "^1.0", @@ -55,8 +52,6 @@ "@php vendor/bin/testbench serve" ], "analyse": "vendor/bin/phpstan analyse", - "test": "vendor/bin/pest", - "test-coverage": "vendor/bin/pest --coverage", "format": "vendor/bin/pint" }, "extra": { diff --git a/configure.php b/configure.php deleted file mode 100644 index d7e0fcc..0000000 --- a/configure.php +++ /dev/null @@ -1,266 +0,0 @@ -#!/usr/bin/env php - $version) { - if (in_array($name, $names, true)) { - unset($data['require-dev'][$name]); - } - } - - file_put_contents(__DIR__.'/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); -} - -function remove_composer_script($scriptName) -{ - $data = json_decode(file_get_contents(__DIR__.'/composer.json'), true); - - foreach ($data['scripts'] as $name => $script) { - if ($scriptName === $name) { - unset($data['scripts'][$name]); - break; - } - } - - file_put_contents(__DIR__.'/composer.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); -} - -function remove_readme_paragraphs(string $file): void -{ - $contents = file_get_contents($file); - - file_put_contents( - $file, - preg_replace('/.*/s', '', $contents) ?: $contents - ); -} - -function safeUnlink(string $filename) -{ - if (file_exists($filename) && is_file($filename)) { - unlink($filename); - } -} - -function determineSeparator(string $path): string -{ - return str_replace('/', DIRECTORY_SEPARATOR, $path); -} - -function replaceForWindows(): array -{ - return preg_split('/\\r\\n|\\r|\\n/', run('dir /S /B * | findstr /v /i .git\ | findstr /v /i vendor | findstr /v /i '.basename(__FILE__).' | findstr /r /i /M /F:/ ":author :vendor :package VendorName skeleton migration_table_name vendor_name vendor_slug author@domain.com"')); -} - -function replaceForAllOtherOSes(): array -{ - return explode(PHP_EOL, run('grep -E -r -l -i ":author|:vendor|:package|VendorName|skeleton|migration_table_name|vendor_name|vendor_slug|author@domain.com" --exclude-dir=vendor ./* ./.github/* | grep -v '.basename(__FILE__))); -} - -$gitName = run('git config user.name'); -$authorName = ask('Author name', $gitName); - -$gitEmail = run('git config user.email'); -$authorEmail = ask('Author email', $gitEmail); - -$usernameGuess = explode(':', run('git config remote.origin.url'))[1]; -$usernameGuess = dirname($usernameGuess); -$usernameGuess = basename($usernameGuess); -$authorUsername = ask('Author username', $usernameGuess); - -$vendorName = ask('Vendor name', $authorUsername); -$vendorSlug = slugify($vendorName); -$vendorNamespace = str_replace('-', '', ucwords($vendorName)); -$vendorNamespace = ask('Vendor namespace', $vendorNamespace); - -$currentDirectory = getcwd(); -$folderName = basename($currentDirectory); - -$packageName = ask('Package name', $folderName); -$packageSlug = slugify($packageName); -$packageSlugWithoutPrefix = remove_prefix('laravel-', $packageSlug); - -$className = title_case($packageName); -$className = ask('Class name', $className); -$variableName = lcfirst($className); -$description = ask('Package description', "This is my package {$packageSlug}"); - -$usePhpStan = confirm('Enable PhpStan?', true); -$useLaravelPint = confirm('Enable Laravel Pint?', true); -$useDependabot = confirm('Enable Dependabot?', true); -$useLaravelRay = confirm('Use Ray for debugging?', true); -$useUpdateChangelogWorkflow = confirm('Use automatic changelog updater workflow?', true); - -writeln('------'); -writeln("Author : {$authorName} ({$authorUsername}, {$authorEmail})"); -writeln("Vendor : {$vendorName} ({$vendorSlug})"); -writeln("Package : {$packageSlug} <{$description}>"); -writeln("Namespace : {$vendorNamespace}\\{$className}"); -writeln("Class name : {$className}"); -writeln('---'); -writeln('Packages & Utilities'); -writeln('Use Laravel/Pint : '.($useLaravelPint ? 'yes' : 'no')); -writeln('Use Larastan/PhpStan : '.($usePhpStan ? 'yes' : 'no')); -writeln('Use Dependabot : '.($useDependabot ? 'yes' : 'no')); -writeln('Use Ray App : '.($useLaravelRay ? 'yes' : 'no')); -writeln('Use Auto-Changelog : '.($useUpdateChangelogWorkflow ? 'yes' : 'no')); -writeln('------'); - -writeln('This script will replace the above values in all relevant files in the project directory.'); - -if (! confirm('Modify files?', true)) { - exit(1); -} - -$files = (str_starts_with(strtoupper(PHP_OS), 'WIN') ? replaceForWindows() : replaceForAllOtherOSes()); - -foreach ($files as $file) { - replace_in_file($file, [ - ':author_name' => $authorName, - ':author_username' => $authorUsername, - 'author@domain.com' => $authorEmail, - ':vendor_name' => $vendorName, - ':vendor_slug' => $vendorSlug, - 'VendorName' => $vendorNamespace, - ':package_name' => $packageName, - ':package_slug' => $packageSlug, - ':package_slug_without_prefix' => $packageSlugWithoutPrefix, - 'Skeleton' => $className, - 'skeleton' => $packageSlug, - 'migration_table_name' => title_snake($packageSlug), - 'variable' => $variableName, - ':package_description' => $description, - ]); - - match (true) { - str_contains($file, determineSeparator('src/Skeleton.php')) => rename($file, determineSeparator('./src/'.$className.'.php')), - str_contains($file, determineSeparator('src/SkeletonServiceProvider.php')) => rename($file, determineSeparator('./src/'.$className.'ServiceProvider.php')), - str_contains($file, determineSeparator('src/Facades/Skeleton.php')) => rename($file, determineSeparator('./src/Facades/'.$className.'.php')), - str_contains($file, determineSeparator('src/Commands/SkeletonCommand.php')) => rename($file, determineSeparator('./src/Commands/'.$className.'Command.php')), - str_contains($file, determineSeparator('database/migrations/create_skeleton_table.php.stub')) => rename($file, determineSeparator('./database/migrations/create_'.title_snake($packageSlugWithoutPrefix).'_table.php.stub')), - str_contains($file, determineSeparator('config/skeleton.php')) => rename($file, determineSeparator('./config/'.$packageSlugWithoutPrefix.'.php')), - str_contains($file, 'README.md') => remove_readme_paragraphs($file), - default => [], - }; -} - -if (! $useLaravelPint) { - safeUnlink(__DIR__.'/.github/workflows/fix-php-code-style-issues.yml'); - safeUnlink(__DIR__.'/pint.json'); -} - -if (! $usePhpStan) { - safeUnlink(__DIR__.'/phpstan.neon.dist'); - safeUnlink(__DIR__.'/phpstan-baseline.neon'); - safeUnlink(__DIR__.'/.github/workflows/phpstan.yml'); - - remove_composer_deps([ - 'phpstan/extension-installer', - 'phpstan/phpstan-deprecation-rules', - 'phpstan/phpstan-phpunit', - 'nunomaduro/larastan', - ]); - - remove_composer_script('phpstan'); -} - -if (! $useDependabot) { - safeUnlink(__DIR__.'/.github/dependabot.yml'); - safeUnlink(__DIR__.'/.github/workflows/dependabot-auto-merge.yml'); -} - -if (! $useLaravelRay) { - remove_composer_deps(['spatie/laravel-ray']); -} - -if (! $useUpdateChangelogWorkflow) { - safeUnlink(__DIR__.'/.github/workflows/update-changelog.yml'); -} - -confirm('Execute `composer install` and run tests?') && run('composer install && composer test'); - -confirm('Let this script delete itself?', true) && unlink(__FILE__); diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon deleted file mode 100644 index e69de29..0000000 diff --git a/tests/ArchTest.php b/tests/ArchTest.php deleted file mode 100644 index ccc19b2..0000000 --- a/tests/ArchTest.php +++ /dev/null @@ -1,5 +0,0 @@ -expect(['dd', 'dump', 'ray']) - ->each->not->toBeUsed(); diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php deleted file mode 100644 index 5d36321..0000000 --- a/tests/ExampleTest.php +++ /dev/null @@ -1,5 +0,0 @@ -toBeTrue(); -}); diff --git a/tests/Pest.php b/tests/Pest.php deleted file mode 100644 index 5e5cf74..0000000 --- a/tests/Pest.php +++ /dev/null @@ -1,5 +0,0 @@ -in(__DIR__); From 619b20b01c4b8836552d25bfd14b3d4d0face57c Mon Sep 17 00:00:00 2001 From: Rolf van de Krol Date: Tue, 21 Nov 2023 11:11:20 +0100 Subject: [PATCH 3/9] feat: refactor key logic in de data connector --- src/ModelDataConnector.php | 41 ++++++++++++++------- src/Models/Contracts/LtiClient.php | 10 +++++ src/Models/LtiContext.php | 4 +- src/Models/LtiResourceLink.php | 4 +- src/Models/Traits/HasClientCapabilities.php | 8 ++-- 5 files changed, 46 insertions(+), 21 deletions(-) diff --git a/src/ModelDataConnector.php b/src/ModelDataConnector.php index c4aa573..2276022 100644 --- a/src/ModelDataConnector.php +++ b/src/ModelDataConnector.php @@ -46,11 +46,26 @@ protected function getClientBuilder(): Builder return $this->clientClassName::query(); } + protected function getClientLtiRecordIdColumn(): string + { + return $this->clientClassName::getLtiRecordIdColumn(); + } + + protected function getClientLtiKeyColumn(): string + { + return $this->clientClassName::getLtiKeyColumn(); + } + + protected function getClientForeignKeyFromPlatform(Platform $platform): int|string + { + return $this->clientClassName::getForeignKeyFromPlatform($platform); + } + public function loadPlatform(Platform $platform): bool { if (! empty($platform->getRecordId())) { /** @var LtiClient|null $client */ - $client = $this->getClientBuilder()->firstWhere('nr', $platform->getRecordId()); + $client = $this->getClientBuilder()->firstWhere($this->getClientLtiRecordIdColumn(), $platform->getRecordId()); if (! $client) { return false; } @@ -86,7 +101,7 @@ public function loadPlatform(Platform $platform): bool if (! empty($platform->getKey())) { /** @var LtiClient|null $client */ - $client = $this->getClientBuilder()->find($platform->getKey()); + $client = $this->getClientBuilder()->firstWhere($this->getClientLtiKeyColumn(), $platform->getKey()); if (! $client) { return false; } @@ -103,7 +118,7 @@ public function savePlatform(Platform $platform): bool { if (! empty($platform->getRecordId())) { /** @var (LtiClient&Model)|null $client */ - $client = $this->getClientBuilder()->firstWhere('nr', $platform->getRecordId()); + $client = $this->getClientBuilder()->firstWhere($this->getClientLtiRecordIdColumn(), $platform->getRecordId()); if (! $client) { return false; } @@ -158,9 +173,9 @@ public function loadContext(Context $context): bool /** @var \Swis\Laravel\LtiProvider\Models\LtiContext|null $ltiContext */ $ltiContext = $this->environment->contexts()->with('client')->where('external_context_id', $context->ltiContextId) ->whereHas('client', function ($query) use ($context) { - $query->where('id', $context->getPlatform()->getKey()); - }) - ->first(); + $query->where('id', $context->getPlatform()->getRecordId()); + })->first(); + if (! $ltiContext) { return false; } @@ -258,9 +273,9 @@ public function loadResourceLink(ResourceLink $resourceLink): bool $ltiResourceLink = $this->environment->resourceLinks()->with('client')->where('external_resource_link_id', $resourceLink->ltiResourceLinkId) ->where(function ($query) use ($resourceLink) { $query - ->where('client_id', $resourceLink->getPlatform()->getKey()) + ->where('client_id', $this->getClientForeignKeyFromPlatform($resourceLink->getPlatform())) ->orWhereHas('context', function ($query) use ($resourceLink) { - $query->where('client_id', $resourceLink->getPlatform()->getKey()); + $query->where('client_id', $this->getClientForeignKeyFromPlatform($resourceLink->getPlatform())); }); }) ->first(); @@ -358,7 +373,7 @@ public function loadPlatformNonce(PlatformNonce $nonce): bool } /** @var \Swis\Laravel\LtiProvider\Models\LtiNonce|null $ltiNonce */ - $ltiNonce = $this->environment->nonces()->where('client_id', $nonce->getPlatform()->getKey()) + $ltiNonce = $this->environment->nonces()->where('client_id', $this->getClientForeignKeyFromPlatform($nonce->getPlatform())) ->where('nonce', $nonce->getValue()) ->where('expires_at', '>', Carbon::now()) ->first(); @@ -380,7 +395,7 @@ public function savePlatformNonce(PlatformNonce $nonce): bool /** @var \Swis\Laravel\LtiProvider\Models\LtiNonce $ltiNonce */ $ltiNonce = $this->environment->nonces()->firstOrNew([ - 'client_id' => $nonce->getPlatform()->getKey(), + 'client_id' => $this->getClientForeignKeyFromPlatform($nonce->getPlatform()), 'nonce' => $nonce->getValue(), ]); @@ -396,7 +411,7 @@ public function deletePlatformNonce(PlatformNonce $nonce): bool return parent::deletePlatformNonce($nonce); } - $this->environment->nonces()->where('client_id', $nonce->getPlatform()->getKey()) + $this->environment->nonces()->where('client_id', $this->getClientForeignKeyFromPlatform($nonce->getPlatform())) ->where('nonce', $nonce->getValue()) ->delete(); @@ -410,7 +425,7 @@ public function loadAccessToken(AccessToken $accessToken): bool } /** @var \Swis\Laravel\LtiProvider\Models\LtiAccessToken|null $ltiAccessToken */ - $ltiAccessToken = $this->environment->accessTokens()->where('client_id', $accessToken->getPlatform()->getKey()) + $ltiAccessToken = $this->environment->accessTokens()->where('client_id', $this->getClientForeignKeyFromPlatform($accessToken->getPlatform())) ->first(); if (! $ltiAccessToken) { @@ -430,7 +445,7 @@ public function saveAccessToken(AccessToken $accessToken): bool /** @var \Swis\Laravel\LtiProvider\Models\LtiAccessToken $ltiAccessToken */ $ltiAccessToken = $this->environment->accessTokens()->firstOrNew([ - 'client_id' => $accessToken->getPlatform()->getKey(), + 'client_id' => $this->getClientForeignKeyFromPlatform($accessToken->getPlatform()), ]); $ltiAccessToken->fillFromLtiAccessToken($accessToken); $ltiAccessToken->save(); diff --git a/src/Models/Contracts/LtiClient.php b/src/Models/Contracts/LtiClient.php index 243f9ef..c052104 100644 --- a/src/Models/Contracts/LtiClient.php +++ b/src/Models/Contracts/LtiClient.php @@ -14,6 +14,16 @@ */ interface LtiClient { + public static function getLtiRecordIdColumn(): string; + + public static function getLtiKeyColumn(): string; + + public static function getForeignKeyFromPlatform(Platform $platform): int|string; + + public function getLtiRecordId(): ?int; + + public function getLtiKey(): string; + public function fillLtiPlatform(Platform $platform): void; public function fillFromLtiPlatform(Platform $platform): void; diff --git a/src/Models/LtiContext.php b/src/Models/LtiContext.php index c0bf5be..48ff0d3 100644 --- a/src/Models/LtiContext.php +++ b/src/Models/LtiContext.php @@ -63,7 +63,7 @@ public function fillLtiContext(Context $context): void { $context->setRecordId($this->id); - $context->setPlatformId($this->client->nr); + $context->setPlatformId($this->client->getLtiRecordId()); $context->title = $this->title; $context->ltiContextId = $this->external_context_id; @@ -72,7 +72,7 @@ public function fillLtiContext(Context $context): void public function fillFromLtiContext(Context $context): void { - $this->client_id = $context->getPlatform()->getKey(); + $this->client_id = config('lti-provider.class-names.lti-client')::getForeignKeyFromPlatform($context->getPlatform()); $this->title = $context->title; $this->external_context_id = $context->ltiContextId; diff --git a/src/Models/LtiResourceLink.php b/src/Models/LtiResourceLink.php index e152e5e..80039b1 100644 --- a/src/Models/LtiResourceLink.php +++ b/src/Models/LtiResourceLink.php @@ -78,7 +78,7 @@ public function fillLtiResourceLink(ResourceLink $resourceLink): void { $resourceLink->setRecordId($this->id); - $resourceLink->setPlatformId($this->client->nr); + $resourceLink->setPlatformId($this->client->getLtiRecordId()); $resourceLink->setContextId($this->lti_context_id); $resourceLink->title = $this->title; @@ -88,7 +88,7 @@ public function fillLtiResourceLink(ResourceLink $resourceLink): void public function fillFromLtiResourceLink(ResourceLink $resourceLink): void { - $this->client_id = $resourceLink->getPlatform()->getKey(); + $this->client_id = config('lti-provider.class-names.lti-client')::getForeignKeyFromPlatform($resourceLink->getPlatform()); $this->lti_context_id = $resourceLink->getContext()?->getRecordId(); $this->title = $resourceLink->title; diff --git a/src/Models/Traits/HasClientCapabilities.php b/src/Models/Traits/HasClientCapabilities.php index a883848..5656f0d 100644 --- a/src/Models/Traits/HasClientCapabilities.php +++ b/src/Models/Traits/HasClientCapabilities.php @@ -13,7 +13,7 @@ trait HasClientCapabilities */ public function contexts(): HasMany { - return $this->hasMany(config('lti-provider.class-names.lti-context')); + return $this->hasMany(config('lti-provider.class-names.lti-context'), 'client_id'); } /** @@ -21,7 +21,7 @@ public function contexts(): HasMany */ public function resourceLinks(): HasMany { - return $this->hasMany(config('lti-provider.class-names.lti-resource-link')); + return $this->hasMany(config('lti-provider.class-names.lti-resource-link'), 'client_id'); } /** @@ -29,7 +29,7 @@ public function resourceLinks(): HasMany */ public function nonces(): HasMany { - return $this->hasMany(config('lti-provider.class-names.lti-nonce')); + return $this->hasMany(config('lti-provider.class-names.lti-nonce'), 'client_id'); } /** @@ -37,6 +37,6 @@ public function nonces(): HasMany */ public function accessTokens(): HasMany { - return $this->hasMany(config('lti-provider.class-names.lti-access-token')); + return $this->hasMany(config('lti-provider.class-names.lti-access-token'), 'client_id'); } } From c726d57cdb0585e95d20ea39354ac16d07c78f20 Mon Sep 17 00:00:00 2001 From: Rolf van de Krol Date: Tue, 21 Nov 2023 11:24:45 +0100 Subject: [PATCH 4/9] feat: add basic client implementation --- config/lti-provider.php | 2 +- .../2023_10_26_200000_add_client_table.php | 47 +++++ src/Models/SimpleClient.php | 160 ++++++++++++++++++ 3 files changed, 208 insertions(+), 1 deletion(-) create mode 100644 database/migrations/2023_10_26_200000_add_client_table.php create mode 100644 src/Models/SimpleClient.php diff --git a/config/lti-provider.php b/config/lti-provider.php index 9e2d245..6f9d6ee 100644 --- a/config/lti-provider.php +++ b/config/lti-provider.php @@ -2,7 +2,7 @@ return [ 'class-names' => [ - 'lti-client' => '', + 'lti-client' => \Swis\Laravel\LtiProvider\Models\SimpleClient::class, 'lti-context' => \Swis\Laravel\LtiProvider\Models\LtiContext::class, 'lti-resource-link' => \Swis\Laravel\LtiProvider\Models\LtiResourceLink::class, 'lti-nonce' => \Swis\Laravel\LtiProvider\Models\LtiNonce::class, diff --git a/database/migrations/2023_10_26_200000_add_client_table.php b/database/migrations/2023_10_26_200000_add_client_table.php new file mode 100644 index 0000000..630123b --- /dev/null +++ b/database/migrations/2023_10_26_200000_add_client_table.php @@ -0,0 +1,47 @@ +id(); + + // Admin title + $table->string('name'); + + // Keys & secrets + $table->string('key', 40)->unique(); + $table->string('secret', 1024)->nullable(); + $table->text('public_key')->nullable(); + + // LTI information + $table->string('lti_platform_id', 255)->nullable(); + $table->string('lti_client_id', 255)->nullable(); + $table->string('lti_deployment_id', 255)->nullable(); + $table->string('lti_version', 10)->nullable(); + $table->string('lti_signature_method', 15)->default('HMAC-SHA1'); + $table->text('lti_profile'); + $table->text('lti_settings'); + + $table->timestamps(); + + $table->unique(['lti_platform_id', 'lti_client_id', 'lti_deployment_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::drop('clients'); + } +}; diff --git a/src/Models/SimpleClient.php b/src/Models/SimpleClient.php new file mode 100644 index 0000000..dc51af0 --- /dev/null +++ b/src/Models/SimpleClient.php @@ -0,0 +1,160 @@ + + */ + protected $casts = [ + 'grant_types' => 'array', + 'lti_profile' => AsArrayObject::class, + 'lti_settings' => AsArrayObject::class, + ]; + + /** + * @var array + */ + protected $attributes = [ + 'lti_profile' => '{}', + 'lti_settings' => '{}', + ]; + + protected static function booted(): void + { + static::creating(function (SimpleClient $client) { + $client->secret = $client->secret ?: Str::random(40); + $client->public_key = $client->public_key ?: Str::random(40); + }); + } + + public function fillLtiPlatform(Platform $platform): void + { + $platform->setRecordId($this->id); + $platform->name = $this->name; + $platform->setKey($this->key); + $platform->secret = $this->secret; + $platform->platformId = $this->lti_platform_id; + $platform->clientId = $this->lti_client_id; + $platform->deploymentId = $this->lti_deployment_id; + $platform->rsaKey = $this->public_key; + $platform->signatureMethod = $this->lti_signature_method; + $platform->consumerName = null; + $platform->consumerVersion = null; + $platform->consumerGuid = null; + $platform->profile = $this->lti_profile; + $platform->toolProxy = null; + $platform->setSettings($this->lti_settings->toArray()); + $platform->protected = false; + $platform->enabled = true; + $platform->enableFrom = null; + $platform->enableUntil = null; + $platform->lastAccess = null; + + $platform->created = $this->created_at->getTimestamp(); + $platform->updated = $this->updated_at->getTimestamp(); + } + + public function fillFromLtiPlatform(Platform $platform): void + { + $settings = $platform->getSettings(); + $profile = ! empty($platform->profile) ? $platform->profile : []; + + $this->public_key = $platform->rsaKey; + $this->lti_platform_id = $platform->platformId; + $this->lti_client_id = $platform->clientId; + $this->lti_deployment_id = $platform->deploymentId; + $this->lti_signature_method = $platform->signatureMethod; + $this->lti_profile = new ArrayObject($profile); + $this->lti_settings = new ArrayObject($settings); + } + + public static function getLtiRecordIdColumn(): string + { + return 'id'; + } + + public static function getLtiKeyColumn(): string + { + return 'key'; + } + + public function getLtiRecordId(): ?int + { + return $this->id; + } + + public function getLtiKey(): string + { + return $this->key; + } + + public static function getForeignKeyFromPlatform(Platform $platform): int|string + { + return $platform->getRecordId(); + } +} From 37423fb95b8819f4f2a6c87074c11b933150bfe5 Mon Sep 17 00:00:00 2001 From: Rolf van de Krol Date: Tue, 21 Nov 2023 11:25:28 +0100 Subject: [PATCH 5/9] feat: enable phpstan for config and migrations --- phpstan.neon.dist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/phpstan.neon.dist b/phpstan.neon.dist index f082b99..03696e1 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,6 +1,8 @@ parameters: level: 6 paths: + - config + - database - src tmpDir: build/phpstan checkMissingIterableValueType: false From 44959f684390750f4b87a037f7dd2d1d787b8cba Mon Sep 17 00:00:00 2001 From: Rolf van de Krol Date: Tue, 21 Nov 2023 11:27:57 +0100 Subject: [PATCH 6/9] test: add tests using testbench --- .gitattributes | 2 +- composer.json | 33 +- phpstan.neon.dist | 2 + phpunit.xml.dist | 7 - tests/ModelDataConnectorTest.php | 821 ++++++++++++++++++ tests/Models/ClientTest.php | 21 + tests/Models/LtiNonceTest.php | 39 + tests/Models/LtiResourceLinkTest.php | 43 + tests/Models/SimpleLtiEnvironmentTest.php | 20 + tests/TestCase.php | 29 +- workbench/app/Models/SimpleLtiEnvironment.php | 35 + workbench/database/factories/.gitkeep | 0 .../factories/SimpleClientFactory.php | 30 + .../factories/SimpleLtiEnvironmentFactory.php | 24 + ...0000_add_simple_lti_environments_table.php | 31 + 15 files changed, 1112 insertions(+), 25 deletions(-) create mode 100644 tests/ModelDataConnectorTest.php create mode 100644 tests/Models/ClientTest.php create mode 100644 tests/Models/LtiNonceTest.php create mode 100644 tests/Models/LtiResourceLinkTest.php create mode 100644 tests/Models/SimpleLtiEnvironmentTest.php create mode 100644 workbench/app/Models/SimpleLtiEnvironment.php create mode 100644 workbench/database/factories/.gitkeep create mode 100644 workbench/database/factories/SimpleClientFactory.php create mode 100644 workbench/database/factories/SimpleLtiEnvironmentFactory.php create mode 100644 workbench/database/migrations/2023_10_26_300000_add_simple_lti_environments_table.php diff --git a/.gitattributes b/.gitattributes index 510d810..14d1831 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,10 +9,10 @@ /art export-ignore /docs export-ignore /tests export-ignore +/workbench export-ignore /.editorconfig export-ignore /.php_cs.dist.php export-ignore /psalm.xml export-ignore /psalm.xml.dist export-ignore -/testbench.yaml export-ignore /UPGRADING.md export-ignore /phpstan.neon.dist export-ignore diff --git a/composer.json b/composer.json index d5d0b44..3283455 100644 --- a/composer.json +++ b/composer.json @@ -39,20 +39,31 @@ } }, "scripts": { - "post-autoload-dump": "@composer run prepare", + "post-autoload-dump": [ + "@clear", + "@prepare", + "@composer run prepare" + ], "clear": "@php vendor/bin/testbench package:purge-skeleton --ansi", "prepare": "@php vendor/bin/testbench package:discover --ansi", - "build": [ - "@composer run prepare", - "@php vendor/bin/testbench workbench:build --ansi" - ], + "build": "@php vendor/bin/testbench workbench:build --ansi", "start": [ "Composer\\Config::disableProcessTimeout", "@composer run build", "@php vendor/bin/testbench serve" ], "analyse": "vendor/bin/phpstan analyse", - "format": "vendor/bin/pint" + "test": "vendor/bin/phpunit", + "format": "vendor/bin/pint", + "serve": [ + "Composer\\Config::disableProcessTimeout", + "@build", + "@php vendor/bin/testbench serve" + ], + "lint": [ + "@php vendor/bin/pint", + "@php vendor/bin/phpstan analyse" + ] }, "extra": { "laravel": { @@ -70,5 +81,13 @@ } }, "minimum-stability": "dev", - "prefer-stable": true + "prefer-stable": true, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/", + "Workbench\\App\\": "workbench/app/", + "Workbench\\Database\\Factories\\": "workbench/database/factories/", + "Workbench\\Database\\Seeders\\": "workbench/database/seeders/" + } + } } diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 03696e1..372793f 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -4,5 +4,7 @@ parameters: - config - database - src + - tests + - workbench tmpDir: build/phpstan checkMissingIterableValueType: false diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 5c3e79c..473321a 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -20,13 +20,6 @@ tests - - - - - - - diff --git a/tests/ModelDataConnectorTest.php b/tests/ModelDataConnectorTest.php new file mode 100644 index 0000000..afad07f --- /dev/null +++ b/tests/ModelDataConnectorTest.php @@ -0,0 +1,821 @@ +ltiEnvironment = SimpleLtiEnvironment::factory()->create(); + $this->connector = ModelDataConnector::make($this->ltiEnvironment); + } + + /** @test */ + public function it_should_load_platform(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + + // Assert + $this->assertEquals($client->name, $platform->name); + } + + /** @test */ + public function it_should_update_platform(): void + { + // Arrange + $originalName = 'Foobar'; + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create([ + 'name' => $originalName, + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $platform->name = 'Barfoo'; + $platform->setSetting('a', 'b'); + $platform->save(); + + $client->refresh(); + + // Assert + $this->assertEquals($originalName, $client->name); + $this->assertEquals('b', $client->lti_settings['a']); + } + + /** @test */ + public function it_should_not_insert_platform(): void + { + // Arrange + $platform = new Platform($this->connector); + + // Act + $ok = $platform->save(); + + // Assert + $this->assertFalse($ok); + $this->assertEmpty(SimpleClient::all()); + } + + /** @test */ + public function it_should_load_context_from_external_context_id(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiContext = Context::fromPlatform($platform, $context->external_context_id); + + // Assert + $this->assertEquals($ltiContext->getRecordId(), $context->id); + } + + /** @test */ + public function it_should_insert_context(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiContext = Context::fromPlatform($platform, '123'); + $ltiContext->title = 'Barfoo'; + $ltiContext->save(); + + // Assert + $contexts = $client->contexts()->get(); + $this->assertCount(1, $contexts); + } + + /** @test */ + public function it_should_update_context(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiContext = Context::fromPlatform($platform, $context->external_context_id); + + $ltiContext->setSetting('a', 'b'); + $ltiContext->save(); + + $context->refresh(); + + // Assert + $this->assertEquals('b', $context->settings['a']); + } + + /** @test */ + public function it_should_delete_context(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiContext = Context::fromPlatform($platform, $context->external_context_id); + + $ltiContext->delete(); + + // Assert + $this->expectException(ModelNotFoundException::class); + $context->refresh(); + } + + /** @test */ + public function it_should_load_resource_link_from_record_id(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'client_id' => $client->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $ltiResourceLink = ResourceLink::fromRecordId($resourceLink->id, $this->connector); + + // Assert + $this->assertEquals($ltiResourceLink->ltiResourceLinkId, $resourceLink->external_resource_link_id); + } + + /** @test */ + public function it_should_load_resource_link_from_external_resource_link_id_without_context(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'client_id' => $client->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiResourceLink = ResourceLink::fromPlatform($platform, $resourceLink->external_resource_link_id); + + // Assert + $this->assertEquals($ltiResourceLink->title, $resourceLink->title); + } + + /** @test */ + public function it_should_insert_resource_link_without_context(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiResourceLink = ResourceLink::fromPlatform($platform, '123'); + $ltiResourceLink->title = 'Barfoo'; + $ltiResourceLink->save(); + + // Assert + $resourceLinks = $client->resourceLinks()->get(); + $this->assertCount(1, $resourceLinks); + } + + /** @test */ + public function it_should_load_resource_link_from_external_resource_link_id_with_context(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Baz', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiContext = Context::fromPlatform($platform, $context->external_context_id); + $ltiResourceLink = ResourceLink::fromContext($ltiContext, $resourceLink->external_resource_link_id); + + // Assert + $this->assertEquals($ltiResourceLink->title, $resourceLink->title); + } + + /** @test */ + public function it_should_insert_resource_link_with_context(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiContext = Context::fromPlatform($platform, $context->external_context_id); + $ltiResourceLink = ResourceLink::fromContext($ltiContext, '123'); + $ltiResourceLink->title = 'Barfoo'; + $ltiResourceLink->save(); + + // Assert + $contextResourceLinks = $context->resourceLinks()->get(); + $this->assertCount(1, $contextResourceLinks); + + $clientResourceLinks = $client->resourceLinks()->get(); + $this->assertCount(1, $clientResourceLinks); + } + + /** @test */ + public function it_should_delete_resource_link(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'client_id' => $client->id, + 'external_resource_link_id' => '123', + 'title' => 'Baz', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiResourceLink = ResourceLink::fromPlatform($platform, $resourceLink->external_resource_link_id); + + $ltiResourceLink->delete(); + + // Assert + $this->expectException(ModelNotFoundException::class); + $resourceLink->refresh(); + } + + /** @test */ + public function it_should_get_user_results_for_resource_link(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + $resourceLink1 = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + $resourceLink2 = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '456', + 'title' => 'Barfoo', + ]); + + $userResult1 = $this->ltiEnvironment->userResults()->create([ + 'lti_resource_link_id' => $resourceLink1->id, + 'external_user_result_id' => '111', + 'external_user_id' => 'aaa', + ]); + $userResult2 = $this->ltiEnvironment->userResults()->create([ + 'lti_resource_link_id' => $resourceLink1->id, + 'external_user_result_id' => '222', + 'external_user_id' => 'bbb', + ]); + $userResult3 = $this->ltiEnvironment->userResults()->create([ + 'lti_resource_link_id' => $resourceLink2->id, + 'external_user_result_id' => '333', + 'external_user_id' => 'ccc', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiResourceLink1 = ResourceLink::fromPlatform($platform, $resourceLink1->external_resource_link_id); + $ltiResourceLink2 = ResourceLink::fromPlatform($platform, $resourceLink2->external_resource_link_id); + + $userResults1 = $ltiResourceLink1->getUserResultSourcedIDs(false, IdScope::Platform); + $userResults2 = $ltiResourceLink2->getUserResultSourcedIDs(); + + // Assert + $this->assertCount(2, $userResults1); + $key = $client->getLtiKey().IdScope::SEPARATOR.$userResult1->external_user_id; + $this->assertArrayHasKey($key, $userResults1); + + $this->assertCount(1, $userResults2); + } + + /** @test */ + public function it_should_never_return_shares_for_a_result_link(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $context = Context::fromPlatform($platform, $context->external_context_id); + $ltiResourceLink = ResourceLink::fromContext($context, $resourceLink->external_resource_link_id); + + $shares = $ltiResourceLink->getShares(); + + // Assert + $this->assertCount(0, $shares); + } + + /** @test */ + public function it_should_load_user_result_from_record_id(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'client_id' => $client->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + $userResult = $this->ltiEnvironment->userResults()->create([ + 'lti_resource_link_id' => $resourceLink->id, + 'external_user_result_id' => '123', + 'external_user_id' => '456', + ]); + + // Act + $ltiUserResult = UserResult::fromRecordId($userResult->id, $this->connector); + + // Assert + $this->assertEquals($ltiUserResult->ltiUserId, $userResult->external_user_id); + } + + /** @test */ + public function it_should_load_nonce(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $nonce = $this->ltiEnvironment->nonces()->create([ + 'client_id' => $client->id, + 'nonce' => '123', + 'expires_at' => Carbon::now()->addMinutes(5), + ]); + + // Act + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); + $ltiNonce = new PlatformNonce($platform, $nonce->nonce); + $this->connector->loadPlatformNonce($ltiNonce); + + // Assert + $this->assertEquals($nonce->expires_at->getTimestamp(), $ltiNonce->expires); + } + + /** @test */ + public function it_should_insert_nonce(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiNonce = new PlatformNonce($platform, '123'); + $ltiNonce->save(); + + // Assert + $this->assertDatabaseHas('lti_nonces', [ + 'nonce' => '123', + ]); + } + + /** @test */ + public function it_should_update_nonce(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $nonce = $this->ltiEnvironment->nonces()->create([ + 'client_id' => $client->id, + 'nonce' => '123', + 'expires_at' => Carbon::now()->addMinutes(5), + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiNonce = new PlatformNonce($platform, $nonce->nonce); + $ltiNonce->save(); + + // Assert + $nonce->refresh(); + $this->assertGreaterThan(Carbon::now()->addMinutes(PlatformNonce::MAX_NONCE_AGE - 5), $nonce->expires_at); + } + + /** @test */ + public function it_should_delete_nonce(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $nonce = $this->ltiEnvironment->nonces()->create([ + 'client_id' => $client->id, + 'nonce' => '123', + 'expires_at' => Carbon::now()->addMinutes(5), + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiNonce = new PlatformNonce($platform, $nonce->nonce); + $ltiNonce->delete(); + + // Assert + $this->assertDatabaseMissing('lti_nonces', [ + 'nonce' => '123', + ]); + } + + /** @test */ + public function it_should_load_access_token(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $accessToken = $this->ltiEnvironment->accessTokens()->create([ + 'client_id' => $client->id, + 'access_token' => '123', + 'scopes' => ['foo', 'bar'], + 'expires_at' => Carbon::now()->addMinutes(5), + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiAccessToken = new AccessToken($platform); + + // Assert + $this->assertEquals($ltiAccessToken->token, $accessToken->access_token); + } + + /** @test */ + public function it_should_insert_access_token(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiAccessToken = new AccessToken($platform, ['foo', 'bar'], '123', 5 * 60); + $ltiAccessToken->save(); + + // Assert + $this->assertDatabaseHas('lti_access_tokens', [ + 'access_token' => $ltiAccessToken->token, + ]); + } + + /** @test */ + public function it_should_update_access_token(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $accessToken = $this->ltiEnvironment->accessTokens()->create([ + 'client_id' => $client->id, + 'access_token' => '123', + 'scopes' => ['foo', 'bar'], + 'expires_at' => Carbon::now()->addMinutes(5), + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiAccessToken = new AccessToken($platform, ['foo', 'bar'], '456', 5 * 60); + $ltiAccessToken->save(); + + // Assert + $accessToken->refresh(); + $this->assertEquals('456', $accessToken->access_token); + } + + /** @test */ + public function it_should_never_load_resource_link_share_key(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $context = Context::fromPlatform($platform, $context->external_context_id); + $ltiResourceLink = ResourceLink::fromContext($context, $resourceLink->external_resource_link_id); + $shareKey = new ResourceLinkShareKey($ltiResourceLink); + + $ok = $this->connector->loadResourceLinkShareKey($shareKey); + + // Assert + $this->assertFalse($ok); + } + + /** @test */ + public function it_should_never_save_resource_link_share_key(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $context = Context::fromPlatform($platform, $context->external_context_id); + $ltiResourceLink = ResourceLink::fromContext($context, $resourceLink->external_resource_link_id); + $shareKey = new ResourceLinkShareKey($ltiResourceLink); + + $ok = $shareKey->save(); + + // Assert + $this->assertFalse($ok); + } + + public function it_should_never_delete_resource_link_share_key(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $context = Context::fromPlatform($platform, $context->external_context_id); + $ltiResourceLink = ResourceLink::fromContext($context, $resourceLink->external_resource_link_id); + $shareKey = new ResourceLinkShareKey($ltiResourceLink); + + $ok = $shareKey->delete(); + + // Assert + $this->assertFalse($ok); + } + + /** @test */ + public function it_should_load_user_result_from_user_id(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'client_id' => $client->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + $userResult = $this->ltiEnvironment->userResults()->create([ + 'lti_resource_link_id' => $resourceLink->id, + 'external_user_result_id' => '123', + 'external_user_id' => '456', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiResourceLink = ResourceLink::fromPlatform($platform, $resourceLink->external_resource_link_id); + $ltiUserResult = UserResult::fromResourceLink($ltiResourceLink, $userResult->external_user_id); + + // Assert + $this->assertEquals($ltiUserResult->getRecordId(), $userResult->id); + } + + /** @test */ + public function it_should_insert_user_result(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiContext = Context::fromPlatform($platform, $context->external_context_id); + $ltiResourceLink = ResourceLink::fromContext($ltiContext, $resourceLink->external_resource_link_id); + $ltiUserResult = new UserResult(); + $ltiUserResult->setDataConnector($this->connector); + $ltiUserResult->setResourceLinkId($ltiResourceLink->getRecordId()); + $ltiUserResult->ltiUserId = '456'; + $ltiUserResult->ltiResultSourcedId = '789'; + + $ltiUserResult->save(); + + // Assert + $this->assertNotNull($ltiUserResult->getRecordId()); + $this->assertDatabaseHas('lti_user_results', [ + 'id' => $ltiUserResult->getRecordId(), + 'external_user_result_id' => '789', + 'external_user_id' => '456', + 'lti_resource_link_id' => $resourceLink->id, + ]); + } + + /** @test */ + public function it_should_update_user_result(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + $userResult = $this->ltiEnvironment->userResults()->create([ + 'lti_resource_link_id' => $resourceLink->id, + 'external_user_result_id' => '123', + 'external_user_id' => '456', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiContext = Context::fromPlatform($platform, $context->external_context_id); + $ltiResourceLink = ResourceLink::fromContext($ltiContext, $resourceLink->external_resource_link_id); + $ltiUserResult = UserResult::fromResourceLink($ltiResourceLink, $userResult->external_user_id); + + $ltiUserResult->ltiResultSourcedId = '789'; + $ltiUserResult->save(); + + // Assert + $this->assertDatabaseHas('lti_user_results', [ + 'id' => $ltiUserResult->getRecordId(), + 'external_user_result_id' => '789', + 'external_user_id' => '456', + 'lti_resource_link_id' => $ltiResourceLink->getRecordId(), + ]); + } + + /** @test */ + public function it_should_delete_user_result(): void + { + // Arrange + /** @var SimpleClient $client */ + $client = Factory::factoryForModel(SimpleClient::class)->create(); + + $context = $this->ltiEnvironment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Barfoo', + ]); + + $userResult = $this->ltiEnvironment->userResults()->create([ + 'lti_resource_link_id' => $resourceLink->id, + 'external_user_result_id' => '123', + 'external_user_id' => '456', + ]); + + // Act + $platform = Platform::fromRecordId($client->id, $this->connector); + $ltiContext = Context::fromPlatform($platform, $context->external_context_id); + $ltiResourceLink = ResourceLink::fromContext($ltiContext, $resourceLink->external_resource_link_id); + $ltiUserResult = UserResult::fromResourceLink($ltiResourceLink, $userResult->external_user_id); + + $ok = $ltiUserResult->delete(); + + // Assert + $this->assertTrue($ok); + $this->expectException(ModelNotFoundException::class); + $userResult->refresh(); + } +} diff --git a/tests/Models/ClientTest.php b/tests/Models/ClientTest.php new file mode 100644 index 0000000..56406e1 --- /dev/null +++ b/tests/Models/ClientTest.php @@ -0,0 +1,21 @@ +create(); + + self::assertDatabaseCount('clients', 1); + } +} diff --git a/tests/Models/LtiNonceTest.php b/tests/Models/LtiNonceTest.php new file mode 100644 index 0000000..2db7e0d --- /dev/null +++ b/tests/Models/LtiNonceTest.php @@ -0,0 +1,39 @@ +create(); + + $environment = SimpleLtiEnvironment::factory()->create(); + + $environment->nonces()->create([ + 'client_id' => $client->id, + 'nonce' => 'foo', + 'expires_at' => now()->subMinutes(1), + ]); + + // Act + LtiNonce::deleteExpired(); + + // Assert + $this->assertDatabaseMissing('lti_nonces', [ + 'nonce' => 'foo', + ]); + } +} diff --git a/tests/Models/LtiResourceLinkTest.php b/tests/Models/LtiResourceLinkTest.php new file mode 100644 index 0000000..4063793 --- /dev/null +++ b/tests/Models/LtiResourceLinkTest.php @@ -0,0 +1,43 @@ +create(); + + $environment = SimpleLtiEnvironment::factory()->create(); + + $context = $environment->contexts()->create([ + 'client_id' => $client->id, + 'external_context_id' => '123', + 'title' => 'Barfoo', + ]); + + // Act + $resourceLink = $environment->resourceLinks()->create([ + 'lti_context_id' => $context->id, + 'external_resource_link_id' => '123', + 'title' => 'Baz', + ]); + + // Assert + $resourceLinks = $client->resourceLinks()->get(); + $this->assertCount(1, $resourceLinks); + + $this->assertEquals($resourceLink->client_id, $client->id); + } +} diff --git a/tests/Models/SimpleLtiEnvironmentTest.php b/tests/Models/SimpleLtiEnvironmentTest.php new file mode 100644 index 0000000..f182c69 --- /dev/null +++ b/tests/Models/SimpleLtiEnvironmentTest.php @@ -0,0 +1,20 @@ +create(); + + self::assertDatabaseCount('simple_lti_environments', 1); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index bd1ede9..3b8f6e0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,10 +1,13 @@ 'Swis\\LaravelLtiProvider\\Database\\Factories\\'.class_basename($modelName).'Factory' + fn (string $modelName) => 'Workbench\\Database\\Factories\\'.class_basename($modelName).'Factory' ); } + protected function defineDatabaseMigrations() + { + $this->loadMigrationsFrom(workbench_path('database/migrations')); + $this->loadMigrationsFrom(package_path('database/migrations')); + } + protected function getPackageProviders($app) { return [ - LaravelLtiProviderServiceProvider::class, + LtiServiceProvider::class, ]; } public function getEnvironmentSetUp($app) { - config()->set('database.default', 'testing'); - - /* - $migration = include __DIR__.'/../database/migrations/create_laravel-lti-provider_table.php.stub'; - $migration->up(); - */ + config()->set('database.default', 'sqlite'); + config()->set('database.connections.sqlite', [ + 'driver' => 'sqlite', + 'database' => ':memory:', + 'prefix' => '', + ]); } } diff --git a/workbench/app/Models/SimpleLtiEnvironment.php b/workbench/app/Models/SimpleLtiEnvironment.php new file mode 100644 index 0000000..162db65 --- /dev/null +++ b/workbench/app/Models/SimpleLtiEnvironment.php @@ -0,0 +1,35 @@ + + */ +class SimpleClientFactory extends Factory +{ + protected $model = SimpleClient::class; + + /** + * {@inheritDoc} + */ + public function definition() + { + $name = $this->faker->company(); + + return [ + 'name' => $name, + 'key' => Str::slug($name), + 'secret' => Str::random(40), + 'lti_platform_id' => Str::random(8), + ]; + } +} diff --git a/workbench/database/factories/SimpleLtiEnvironmentFactory.php b/workbench/database/factories/SimpleLtiEnvironmentFactory.php new file mode 100644 index 0000000..14ff2bc --- /dev/null +++ b/workbench/database/factories/SimpleLtiEnvironmentFactory.php @@ -0,0 +1,24 @@ + + */ +class SimpleLtiEnvironmentFactory extends Factory +{ + protected $model = SimpleLtiEnvironment::class; + + /** + * {@inheritDoc} + */ + public function definition() + { + return [ + 'name' => ucfirst($this->faker->words(random_int(3, 5), true)), + ]; + } +} diff --git a/workbench/database/migrations/2023_10_26_300000_add_simple_lti_environments_table.php b/workbench/database/migrations/2023_10_26_300000_add_simple_lti_environments_table.php new file mode 100644 index 0000000..9465b69 --- /dev/null +++ b/workbench/database/migrations/2023_10_26_300000_add_simple_lti_environments_table.php @@ -0,0 +1,31 @@ +uuid('id')->primary(); + + // Admin title + $table->string('name'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::drop('simple_lti_environments'); + } +}; From 2e5992351fb18bf713d47046a829427753d2f4d8 Mon Sep 17 00:00:00 2001 From: Rolf van de Krol Date: Tue, 21 Nov 2023 22:09:27 +0100 Subject: [PATCH 7/9] test: split test to basic test and more complex test with overridden models using uuids --- ...=> 2023_10_26_100000_add_client_table.php} | 0 .../2023_10_26_200000_add_lti_tables.php | 98 ++++++++ src/ModelDataConnector.php | 8 +- src/Models/LtiAccessToken.php | 18 +- src/Models/LtiContext.php | 4 +- src/Models/LtiNonce.php | 6 +- src/Models/LtiResourceLink.php | 4 +- src/Models/LtiUserResult.php | 4 +- src/Models/SimpleClient.php | 3 - src/Models/Traits/HasLtiClient.php | 2 +- .../ModelDataConnectorTest.php | 30 +++ .../ModelDataConnectorTests.php} | 218 ++++++++---------- .../OverrideModelDataConnectorTest.php | 46 ++++ tests/TestCase.php | 13 +- workbench/app/Models/SimpleLtiEnvironment.php | 19 +- workbench/app/OverrideModels/Client.php | 157 +++++++++++++ .../app/OverrideModels/LtiAccessToken.php | 14 ++ workbench/app/OverrideModels/LtiNonce.php | 14 ++ .../OverrideModels/SimpleLtiEnvironment.php | 35 +++ workbench/database/factories/.gitkeep | 0 .../{ => Models}/SimpleClientFactory.php | 2 +- .../SimpleLtiEnvironmentFactory.php | 2 +- .../OverrideModels/ClientFactory.php | 29 +++ .../SimpleLtiEnvironmentFactory.php | 24 ++ ...0000_add_simple_lti_environments_table.php | 2 +- .../2023_10_26_400000_add_client_table.php | 47 ++++ .../2023_10_26_500000_add_lti_tables.php | 11 +- ...0000_add_simple_lti_environments_table.php | 31 +++ 28 files changed, 665 insertions(+), 176 deletions(-) rename database/migrations/{2023_10_26_200000_add_client_table.php => 2023_10_26_100000_add_client_table.php} (100%) create mode 100644 database/migrations/2023_10_26_200000_add_lti_tables.php create mode 100644 tests/ModelDataConnector/ModelDataConnectorTest.php rename tests/{ModelDataConnectorTest.php => ModelDataConnector/ModelDataConnectorTests.php} (75%) create mode 100644 tests/ModelDataConnector/OverrideModelDataConnectorTest.php create mode 100644 workbench/app/OverrideModels/Client.php create mode 100644 workbench/app/OverrideModels/LtiAccessToken.php create mode 100644 workbench/app/OverrideModels/LtiNonce.php create mode 100644 workbench/app/OverrideModels/SimpleLtiEnvironment.php delete mode 100644 workbench/database/factories/.gitkeep rename workbench/database/factories/{ => Models}/SimpleClientFactory.php (93%) rename workbench/database/factories/{ => Models}/SimpleLtiEnvironmentFactory.php (91%) create mode 100644 workbench/database/factories/OverrideModels/ClientFactory.php create mode 100644 workbench/database/factories/OverrideModels/SimpleLtiEnvironmentFactory.php create mode 100644 workbench/database/override_migrations/2023_10_26_400000_add_client_table.php rename database/migrations/2023_10_26_100000_add_lti_tables.php => workbench/database/override_migrations/2023_10_26_500000_add_lti_tables.php (87%) create mode 100644 workbench/database/override_migrations/2023_10_26_600000_add_simple_lti_environments_table.php diff --git a/database/migrations/2023_10_26_200000_add_client_table.php b/database/migrations/2023_10_26_100000_add_client_table.php similarity index 100% rename from database/migrations/2023_10_26_200000_add_client_table.php rename to database/migrations/2023_10_26_100000_add_client_table.php diff --git a/database/migrations/2023_10_26_200000_add_lti_tables.php b/database/migrations/2023_10_26_200000_add_lti_tables.php new file mode 100644 index 0000000..35eb1fc --- /dev/null +++ b/database/migrations/2023_10_26_200000_add_lti_tables.php @@ -0,0 +1,98 @@ +id(); + + $table->morphs('lti_environment'); + + $table->foreignId('client_id')->constrained('clients')->cascadeOnDelete(); + + $table->string('nonce', 50); + $table->dateTime('expires_at'); + + $table->timestamps(); + + $table->unique(['client_id', 'nonce']); + }); + + Schema::create('lti_access_tokens', function (Blueprint $table) { + $table->id(); + + $table->morphs('lti_environment'); + + $table->foreignId('client_id')->constrained('clients')->cascadeOnDelete(); + + $table->string('access_token', 2000); + $table->text('scopes'); + $table->dateTime('expires_at'); + + $table->timestamps(); + }); + + Schema::create('lti_contexts', function (Blueprint $table) { + $table->id(); + + $table->morphs('lti_environment'); + + $table->foreignId('client_id')->constrained('clients')->cascadeOnDelete(); + + $table->string('title')->nullable(); + $table->string('external_context_id', 255); + $table->text('settings'); + + $table->timestamps(); + }); + + Schema::create('lti_resource_links', function (Blueprint $table) { + $table->id(); + + $table->morphs('lti_environment'); + + $table->foreignId('client_id')->constrained('clients')->cascadeOnDelete(); + + $table->foreignId('lti_context_id')->nullable()->constrained('lti_contexts')->cascadeOnDelete(); + + $table->string('title')->nullable(); + $table->string('external_resource_link_id', 255); + $table->text('settings'); + + $table->timestamps(); + }); + + Schema::create('lti_user_results', function (Blueprint $table) { + $table->id(); + + $table->morphs('lti_environment'); + + $table->foreignId('lti_resource_link_id')->constrained('lti_resource_links')->cascadeOnDelete(); + + $table->string('external_user_id', 255); + $table->string('external_user_result_id', 255); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::drop('lti_user_results'); + Schema::drop('lti_resource_links'); + Schema::drop('lti_contexts'); + Schema::drop('lti_access_tokens'); + Schema::drop('lti_nonces'); + } +}; diff --git a/src/ModelDataConnector.php b/src/ModelDataConnector.php index 2276022..e83fe5f 100644 --- a/src/ModelDataConnector.php +++ b/src/ModelDataConnector.php @@ -171,10 +171,10 @@ public function loadContext(Context $context): bool if (! empty($context->ltiContextId)) { /** @var \Swis\Laravel\LtiProvider\Models\LtiContext|null $ltiContext */ - $ltiContext = $this->environment->contexts()->with('client')->where('external_context_id', $context->ltiContextId) - ->whereHas('client', function ($query) use ($context) { - $query->where('id', $context->getPlatform()->getRecordId()); - })->first(); + $ltiContext = $this->environment->contexts()->with('client') + ->where('external_context_id', $context->ltiContextId) + ->where('client_id', $this->getClientForeignKeyFromPlatform($context->getPlatform())) + ->first(); if (! $ltiContext) { return false; diff --git a/src/Models/LtiAccessToken.php b/src/Models/LtiAccessToken.php index 002a7f4..7c9203d 100644 --- a/src/Models/LtiAccessToken.php +++ b/src/Models/LtiAccessToken.php @@ -5,21 +5,18 @@ namespace Swis\Laravel\LtiProvider\Models; use ceLTIc\LTI\AccessToken; -use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Carbon; use Swis\Laravel\LtiProvider\Models\Traits\HasLtiClient; use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** - * \Swis\Laravel\LtiProvider\Models\LtiAccessToken. - * - * @property string $id - * @property string $access_token - * @property array $scopes - * @property \Illuminate\Support\Carbon $expires_at - * @property \Illuminate\Support\Carbon|null $created_at - * @property \Illuminate\Support\Carbon|null $updated_at + * @property string $id + * @property string $access_token + * @property array $scopes + * @property \Illuminate\Support\Carbon $expires_at + * @property \Illuminate\Support\Carbon|null $created_at + * @property \Illuminate\Support\Carbon|null $updated_at * * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken newModelQuery() * @method static \Illuminate\Database\Eloquent\Builder|LtiAccessToken newQuery() @@ -35,7 +32,8 @@ class LtiAccessToken extends Model { use HasLtiClient; use HasLtiEnvironment; - use HasUuids; + + protected $table = 'lti_access_tokens'; protected $fillable = [ 'client_id', diff --git a/src/Models/LtiContext.php b/src/Models/LtiContext.php index 48ff0d3..fbbf272 100644 --- a/src/Models/LtiContext.php +++ b/src/Models/LtiContext.php @@ -13,8 +13,6 @@ use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** - * \Swis\Laravel\LtiProvider\Models\LtiContext. - * * @property int $id * @property string|null $title * @property string $external_context_id @@ -39,6 +37,8 @@ class LtiContext extends Model use HasLtiClient; use HasLtiEnvironment; + protected $table = 'lti_contexts'; + protected $fillable = [ 'client_id', 'lti_environment_type', diff --git a/src/Models/LtiNonce.php b/src/Models/LtiNonce.php index 83d7e73..35f56ae 100644 --- a/src/Models/LtiNonce.php +++ b/src/Models/LtiNonce.php @@ -5,15 +5,12 @@ namespace Swis\Laravel\LtiProvider\Models; use ceLTIc\LTI\PlatformNonce; -use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Carbon; use Swis\Laravel\LtiProvider\Models\Traits\HasLtiClient; use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** - * \Swis\Laravel\LtiProvider\Models\LtiNonce. - * * @property string $id * @property string $nonce * @property \Illuminate\Support\Carbon $expires_at @@ -33,7 +30,8 @@ class LtiNonce extends Model { use HasLtiClient; use HasLtiEnvironment; - use HasUuids; + + protected $table = 'lti_nonces'; protected $fillable = [ 'client_id', diff --git a/src/Models/LtiResourceLink.php b/src/Models/LtiResourceLink.php index 80039b1..609100c 100644 --- a/src/Models/LtiResourceLink.php +++ b/src/Models/LtiResourceLink.php @@ -14,8 +14,6 @@ use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** - * \Swis\Laravel\LtiProvider\Models\LtiResourceLink. - * * @property int $id * @property int|null $lti_context_id * @property string|null $title @@ -42,6 +40,8 @@ class LtiResourceLink extends Model use HasLtiClient; use HasLtiEnvironment; + protected $table = 'lti_resource_links'; + protected $fillable = [ 'client_id', 'lti_environment_type', diff --git a/src/Models/LtiUserResult.php b/src/Models/LtiUserResult.php index 29d175a..98c3675 100644 --- a/src/Models/LtiUserResult.php +++ b/src/Models/LtiUserResult.php @@ -11,8 +11,6 @@ use Swis\Laravel\LtiProvider\Models\Traits\HasLtiEnvironment; /** - * \Swis\Laravel\LtiProvider\Models\LtiUserResult. - * * @property int $id * @property int $lti_resource_link_id * @property string $external_user_id @@ -35,6 +33,8 @@ class LtiUserResult extends Model { use HasLtiEnvironment; + protected $table = 'lti_user_results'; + protected $fillable = [ 'lti_environment_type', 'lti_environment_id', diff --git a/src/Models/SimpleClient.php b/src/Models/SimpleClient.php index dc51af0..cf33586 100644 --- a/src/Models/SimpleClient.php +++ b/src/Models/SimpleClient.php @@ -55,9 +55,6 @@ class SimpleClient extends Model implements LtiClient 'name', 'secret', 'public_key', - 'logo', - 'home_url', - 'revoked', 'lti_platform_id', 'lti_client_id', 'lti_deployment_id', diff --git a/src/Models/Traits/HasLtiClient.php b/src/Models/Traits/HasLtiClient.php index 3b04267..15729ab 100644 --- a/src/Models/Traits/HasLtiClient.php +++ b/src/Models/Traits/HasLtiClient.php @@ -5,7 +5,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo; /** - * @property string $client_id + * @property string|int $client_id * @property \Illuminate\Database\Eloquent\Model&\Swis\Laravel\LtiProvider\Models\Contracts\LtiClient $client * * @method static \Illuminate\Database\Eloquent\Builder|static whereClientId($value) diff --git a/tests/ModelDataConnector/ModelDataConnectorTest.php b/tests/ModelDataConnector/ModelDataConnectorTest.php new file mode 100644 index 0000000..e487261 --- /dev/null +++ b/tests/ModelDataConnector/ModelDataConnectorTest.php @@ -0,0 +1,30 @@ +ltiEnvironment = SimpleLtiEnvironment::factory()->create(); + $this->connector = ModelDataConnector::make($this->ltiEnvironment); + } +} diff --git a/tests/ModelDataConnectorTest.php b/tests/ModelDataConnector/ModelDataConnectorTests.php similarity index 75% rename from tests/ModelDataConnectorTest.php rename to tests/ModelDataConnector/ModelDataConnectorTests.php index afad07f..e3db179 100644 --- a/tests/ModelDataConnectorTest.php +++ b/tests/ModelDataConnector/ModelDataConnectorTests.php @@ -1,6 +1,6 @@ $attributes + */ + protected function createClient(array $attributes = []): LtiClient&Model { - parent::setUp(); + /** @var LtiClient&Model $client */ + $client = Factory::factoryForModel(config('lti-provider.class-names.lti-client'))->create($attributes); - $this->ltiEnvironment = SimpleLtiEnvironment::factory()->create(); - $this->connector = ModelDataConnector::make($this->ltiEnvironment); + return $client; } /** @test */ public function it_should_load_platform(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); // Assert $this->assertEquals($client->name, $platform->name); @@ -54,22 +48,21 @@ public function it_should_update_platform(): void { // Arrange $originalName = 'Foobar'; - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create([ + $client = $this->createClient([ 'name' => $originalName, ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $platform->name = 'Barfoo'; $platform->setSetting('a', 'b'); $platform->save(); - $client->refresh(); + $reloadedPlatform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); // Assert - $this->assertEquals($originalName, $client->name); - $this->assertEquals('b', $client->lti_settings['a']); + $this->assertEquals($originalName, $reloadedPlatform->name); + $this->assertEquals('b', $reloadedPlatform->getSetting('a')); } /** @test */ @@ -90,17 +83,16 @@ public function it_should_not_insert_platform(): void public function it_should_load_context_from_external_context_id(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiContext = Context::fromPlatform($platform, $context->external_context_id); // Assert @@ -111,11 +103,10 @@ public function it_should_load_context_from_external_context_id(): void public function it_should_insert_context(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiContext = Context::fromPlatform($platform, '123'); $ltiContext->title = 'Barfoo'; $ltiContext->save(); @@ -129,17 +120,16 @@ public function it_should_insert_context(): void public function it_should_update_context(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiContext = Context::fromPlatform($platform, $context->external_context_id); $ltiContext->setSetting('a', 'b'); @@ -155,17 +145,16 @@ public function it_should_update_context(): void public function it_should_delete_context(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiContext = Context::fromPlatform($platform, $context->external_context_id); $ltiContext->delete(); @@ -179,11 +168,10 @@ public function it_should_delete_context(): void public function it_should_load_resource_link_from_record_id(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_resource_link_id' => '123', 'title' => 'Barfoo', ]); @@ -199,17 +187,16 @@ public function it_should_load_resource_link_from_record_id(): void public function it_should_load_resource_link_from_external_resource_link_id_without_context(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_resource_link_id' => '123', 'title' => 'Barfoo', ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiResourceLink = ResourceLink::fromPlatform($platform, $resourceLink->external_resource_link_id); // Assert @@ -220,11 +207,10 @@ public function it_should_load_resource_link_from_external_resource_link_id_with public function it_should_insert_resource_link_without_context(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiResourceLink = ResourceLink::fromPlatform($platform, '123'); $ltiResourceLink->title = 'Barfoo'; $ltiResourceLink->save(); @@ -238,11 +224,10 @@ public function it_should_insert_resource_link_without_context(): void public function it_should_load_resource_link_from_external_resource_link_id_with_context(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); @@ -254,7 +239,7 @@ public function it_should_load_resource_link_from_external_resource_link_id_with ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiContext = Context::fromPlatform($platform, $context->external_context_id); $ltiResourceLink = ResourceLink::fromContext($ltiContext, $resourceLink->external_resource_link_id); @@ -266,17 +251,16 @@ public function it_should_load_resource_link_from_external_resource_link_id_with public function it_should_insert_resource_link_with_context(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiContext = Context::fromPlatform($platform, $context->external_context_id); $ltiResourceLink = ResourceLink::fromContext($ltiContext, '123'); $ltiResourceLink->title = 'Barfoo'; @@ -294,17 +278,16 @@ public function it_should_insert_resource_link_with_context(): void public function it_should_delete_resource_link(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_resource_link_id' => '123', 'title' => 'Baz', ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiResourceLink = ResourceLink::fromPlatform($platform, $resourceLink->external_resource_link_id); $ltiResourceLink->delete(); @@ -318,11 +301,10 @@ public function it_should_delete_resource_link(): void public function it_should_get_user_results_for_resource_link(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); @@ -355,7 +337,7 @@ public function it_should_get_user_results_for_resource_link(): void ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiResourceLink1 = ResourceLink::fromPlatform($platform, $resourceLink1->external_resource_link_id); $ltiResourceLink2 = ResourceLink::fromPlatform($platform, $resourceLink2->external_resource_link_id); @@ -374,11 +356,10 @@ public function it_should_get_user_results_for_resource_link(): void public function it_should_never_return_shares_for_a_result_link(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); @@ -390,7 +371,7 @@ public function it_should_never_return_shares_for_a_result_link(): void ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $context = Context::fromPlatform($platform, $context->external_context_id); $ltiResourceLink = ResourceLink::fromContext($context, $resourceLink->external_resource_link_id); @@ -404,11 +385,10 @@ public function it_should_never_return_shares_for_a_result_link(): void public function it_should_load_user_result_from_record_id(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_resource_link_id' => '123', 'title' => 'Barfoo', ]); @@ -429,11 +409,10 @@ public function it_should_load_user_result_from_record_id(): void public function it_should_load_nonce(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $nonce = $this->ltiEnvironment->nonces()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'nonce' => '123', 'expires_at' => Carbon::now()->addMinutes(5), ]); @@ -451,11 +430,10 @@ public function it_should_load_nonce(): void public function it_should_insert_nonce(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiNonce = new PlatformNonce($platform, '123'); $ltiNonce->save(); @@ -469,17 +447,16 @@ public function it_should_insert_nonce(): void public function it_should_update_nonce(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $nonce = $this->ltiEnvironment->nonces()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'nonce' => '123', 'expires_at' => Carbon::now()->addMinutes(5), ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiNonce = new PlatformNonce($platform, $nonce->nonce); $ltiNonce->save(); @@ -492,17 +469,16 @@ public function it_should_update_nonce(): void public function it_should_delete_nonce(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $nonce = $this->ltiEnvironment->nonces()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'nonce' => '123', 'expires_at' => Carbon::now()->addMinutes(5), ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiNonce = new PlatformNonce($platform, $nonce->nonce); $ltiNonce->delete(); @@ -516,18 +492,17 @@ public function it_should_delete_nonce(): void public function it_should_load_access_token(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $accessToken = $this->ltiEnvironment->accessTokens()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'access_token' => '123', 'scopes' => ['foo', 'bar'], 'expires_at' => Carbon::now()->addMinutes(5), ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiAccessToken = new AccessToken($platform); // Assert @@ -538,11 +513,10 @@ public function it_should_load_access_token(): void public function it_should_insert_access_token(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiAccessToken = new AccessToken($platform, ['foo', 'bar'], '123', 5 * 60); $ltiAccessToken->save(); @@ -556,18 +530,17 @@ public function it_should_insert_access_token(): void public function it_should_update_access_token(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $accessToken = $this->ltiEnvironment->accessTokens()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'access_token' => '123', 'scopes' => ['foo', 'bar'], 'expires_at' => Carbon::now()->addMinutes(5), ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiAccessToken = new AccessToken($platform, ['foo', 'bar'], '456', 5 * 60); $ltiAccessToken->save(); @@ -580,11 +553,10 @@ public function it_should_update_access_token(): void public function it_should_never_load_resource_link_share_key(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); @@ -596,7 +568,7 @@ public function it_should_never_load_resource_link_share_key(): void ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $context = Context::fromPlatform($platform, $context->external_context_id); $ltiResourceLink = ResourceLink::fromContext($context, $resourceLink->external_resource_link_id); $shareKey = new ResourceLinkShareKey($ltiResourceLink); @@ -611,11 +583,10 @@ public function it_should_never_load_resource_link_share_key(): void public function it_should_never_save_resource_link_share_key(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); @@ -627,7 +598,7 @@ public function it_should_never_save_resource_link_share_key(): void ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $context = Context::fromPlatform($platform, $context->external_context_id); $ltiResourceLink = ResourceLink::fromContext($context, $resourceLink->external_resource_link_id); $shareKey = new ResourceLinkShareKey($ltiResourceLink); @@ -641,11 +612,10 @@ public function it_should_never_save_resource_link_share_key(): void public function it_should_never_delete_resource_link_share_key(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); @@ -657,7 +627,7 @@ public function it_should_never_delete_resource_link_share_key(): void ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $context = Context::fromPlatform($platform, $context->external_context_id); $ltiResourceLink = ResourceLink::fromContext($context, $resourceLink->external_resource_link_id); $shareKey = new ResourceLinkShareKey($ltiResourceLink); @@ -672,11 +642,10 @@ public function it_should_never_delete_resource_link_share_key(): void public function it_should_load_user_result_from_user_id(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $resourceLink = $this->ltiEnvironment->resourceLinks()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_resource_link_id' => '123', 'title' => 'Barfoo', ]); @@ -688,7 +657,7 @@ public function it_should_load_user_result_from_user_id(): void ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiResourceLink = ResourceLink::fromPlatform($platform, $resourceLink->external_resource_link_id); $ltiUserResult = UserResult::fromResourceLink($ltiResourceLink, $userResult->external_user_id); @@ -700,11 +669,10 @@ public function it_should_load_user_result_from_user_id(): void public function it_should_insert_user_result(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); @@ -716,7 +684,7 @@ public function it_should_insert_user_result(): void ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiContext = Context::fromPlatform($platform, $context->external_context_id); $ltiResourceLink = ResourceLink::fromContext($ltiContext, $resourceLink->external_resource_link_id); $ltiUserResult = new UserResult(); @@ -741,11 +709,10 @@ public function it_should_insert_user_result(): void public function it_should_update_user_result(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); @@ -763,7 +730,7 @@ public function it_should_update_user_result(): void ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiContext = Context::fromPlatform($platform, $context->external_context_id); $ltiResourceLink = ResourceLink::fromContext($ltiContext, $resourceLink->external_resource_link_id); $ltiUserResult = UserResult::fromResourceLink($ltiResourceLink, $userResult->external_user_id); @@ -784,11 +751,10 @@ public function it_should_update_user_result(): void public function it_should_delete_user_result(): void { // Arrange - /** @var SimpleClient $client */ - $client = Factory::factoryForModel(SimpleClient::class)->create(); + $client = $this->createClient(); $context = $this->ltiEnvironment->contexts()->create([ - 'client_id' => $client->id, + 'client_id' => $client->getKey(), 'external_context_id' => '123', 'title' => 'Barfoo', ]); @@ -806,7 +772,7 @@ public function it_should_delete_user_result(): void ]); // Act - $platform = Platform::fromRecordId($client->id, $this->connector); + $platform = Platform::fromRecordId($client->getLtiRecordId(), $this->connector); $ltiContext = Context::fromPlatform($platform, $context->external_context_id); $ltiResourceLink = ResourceLink::fromContext($ltiContext, $resourceLink->external_resource_link_id); $ltiUserResult = UserResult::fromResourceLink($ltiResourceLink, $userResult->external_user_id); diff --git a/tests/ModelDataConnector/OverrideModelDataConnectorTest.php b/tests/ModelDataConnector/OverrideModelDataConnectorTest.php new file mode 100644 index 0000000..8a5b116 --- /dev/null +++ b/tests/ModelDataConnector/OverrideModelDataConnectorTest.php @@ -0,0 +1,46 @@ +ltiEnvironment = SimpleLtiEnvironment::factory()->create(); + $this->connector = ModelDataConnector::make($this->ltiEnvironment); + } + + protected function defineDatabaseMigrations(): void + { + $this->loadMigrationsFrom(workbench_path('database/override_migrations')); + } + + public function getEnvironmentSetUp($app): void + { + parent::getEnvironmentSetUp($app); + + config()->set('lti-provider.class-names.lti-client', \Workbench\App\OverrideModels\Client::class); + config()->set('lti-provider.class-names.lti-access-token', \Workbench\App\OverrideModels\LtiAccessToken::class); + config()->set('lti-provider.class-names.lti-nonce', \Workbench\App\OverrideModels\LtiNonce::class); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 3b8f6e0..b6ee49c 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -16,24 +16,29 @@ protected function setUp(): void parent::setUp(); Factory::guessFactoryNamesUsing( - fn (string $modelName) => 'Workbench\\Database\\Factories\\'.class_basename($modelName).'Factory' + function (string $modelName): string { + $modelNameParts = explode('\\', $modelName); + $modelName = implode('\\', array_slice($modelNameParts, -2, 2)); + + return 'Workbench\\Database\\Factories\\'.$modelName.'Factory'; + } ); } - protected function defineDatabaseMigrations() + protected function defineDatabaseMigrations(): void { $this->loadMigrationsFrom(workbench_path('database/migrations')); $this->loadMigrationsFrom(package_path('database/migrations')); } - protected function getPackageProviders($app) + protected function getPackageProviders($app): array { return [ LtiServiceProvider::class, ]; } - public function getEnvironmentSetUp($app) + public function getEnvironmentSetUp($app): void { config()->set('database.default', 'sqlite'); config()->set('database.connections.sqlite', [ diff --git a/workbench/app/Models/SimpleLtiEnvironment.php b/workbench/app/Models/SimpleLtiEnvironment.php index 162db65..39176d3 100644 --- a/workbench/app/Models/SimpleLtiEnvironment.php +++ b/workbench/app/Models/SimpleLtiEnvironment.php @@ -2,31 +2,28 @@ namespace Workbench\App\Models; -use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; use Swis\Laravel\LtiProvider\Models\Contracts\LtiEnvironment; -use Swis\Laravel\LtiProvider\Models\SimpleClient; use Swis\Laravel\LtiProvider\Models\Traits\IsLtiEnvironment; /** - * @property string $id + * @property int $id * @property string $name * @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $updated_at * - * @method static \Illuminate\Database\Eloquent\Builder|SimpleClient newModelQuery() - * @method static \Illuminate\Database\Eloquent\Builder|SimpleClient newQuery() - * @method static \Illuminate\Database\Eloquent\Builder|SimpleClient query() - * @method static \Illuminate\Database\Eloquent\Builder|SimpleClient whereCreatedAt($value) - * @method static \Illuminate\Database\Eloquent\Builder|SimpleClient whereId($value) - * @method static \Illuminate\Database\Eloquent\Builder|SimpleClient whereName($value) - * @method static \Illuminate\Database\Eloquent\Builder|SimpleClient whereUpdatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|SimpleLtiEnvironment newModelQuery() + * @method static \Illuminate\Database\Eloquent\Builder|SimpleLtiEnvironment newQuery() + * @method static \Illuminate\Database\Eloquent\Builder|SimpleLtiEnvironment query() + * @method static \Illuminate\Database\Eloquent\Builder|SimpleLtiEnvironment whereCreatedAt($value) + * @method static \Illuminate\Database\Eloquent\Builder|SimpleLtiEnvironment whereId($value) + * @method static \Illuminate\Database\Eloquent\Builder|SimpleLtiEnvironment whereName($value) + * @method static \Illuminate\Database\Eloquent\Builder|SimpleLtiEnvironment whereUpdatedAt($value) */ class SimpleLtiEnvironment extends Model implements LtiEnvironment { use HasFactory; - use HasUuids; use IsLtiEnvironment; protected $fillable = [ diff --git a/workbench/app/OverrideModels/Client.php b/workbench/app/OverrideModels/Client.php new file mode 100644 index 0000000..3d451b9 --- /dev/null +++ b/workbench/app/OverrideModels/Client.php @@ -0,0 +1,157 @@ + + */ + protected $casts = [ + 'grant_types' => 'array', + 'lti_profile' => AsArrayObject::class, + 'lti_settings' => AsArrayObject::class, + ]; + + /** + * @var array + */ + protected $attributes = [ + 'lti_profile' => '{}', + 'lti_settings' => '{}', + ]; + + protected static function booted(): void + { + static::creating(function (Client $client) { + $client->nr = $client->nr ?: Client::max('nr') + 1; + $client->secret = $client->secret ?: Str::random(40); + $client->public_key = $client->public_key ?: Str::random(40); + }); + } + + public function fillLtiPlatform(Platform $platform): void + { + $platform->setRecordId($this->nr); + $platform->name = $this->name; + $platform->setKey($this->id); + $platform->secret = $this->secret; + $platform->platformId = $this->lti_platform_id; + $platform->clientId = $this->lti_client_id; + $platform->deploymentId = $this->lti_deployment_id; + $platform->rsaKey = $this->public_key; + $platform->signatureMethod = $this->lti_signature_method; + $platform->consumerName = null; + $platform->consumerVersion = null; + $platform->consumerGuid = null; + $platform->profile = $this->lti_profile; + $platform->toolProxy = null; + $platform->setSettings($this->lti_settings->toArray()); + $platform->protected = false; + $platform->enabled = true; + $platform->enableFrom = null; + $platform->enableUntil = null; + $platform->lastAccess = null; + + $platform->created = $this->created_at->getTimestamp(); + $platform->updated = $this->updated_at->getTimestamp(); + } + + public function fillFromLtiPlatform(Platform $platform): void + { + $settings = $platform->getSettings(); + $profile = ! empty($platform->profile) ? $platform->profile : []; + + $this->public_key = $platform->rsaKey; + $this->lti_platform_id = $platform->platformId; + $this->lti_client_id = $platform->clientId; + $this->lti_deployment_id = $platform->deploymentId; + $this->lti_signature_method = $platform->signatureMethod; + $this->lti_profile = new ArrayObject($profile); + $this->lti_settings = new ArrayObject($settings); + } + + public static function getLtiRecordIdColumn(): string + { + return 'nr'; + } + + public static function getLtiKeyColumn(): string + { + return 'id'; + } + + public function getLtiRecordId(): ?int + { + return $this->nr; + } + + public function getLtiKey(): string + { + return $this->id; + } + + public static function getForeignKeyFromPlatform(Platform $platform): int|string + { + return $platform->getKey(); + } +} diff --git a/workbench/app/OverrideModels/LtiAccessToken.php b/workbench/app/OverrideModels/LtiAccessToken.php new file mode 100644 index 0000000..af5e806 --- /dev/null +++ b/workbench/app/OverrideModels/LtiAccessToken.php @@ -0,0 +1,14 @@ + + */ +class ClientFactory extends Factory +{ + protected $model = Client::class; + + /** + * {@inheritDoc} + */ + public function definition() + { + $name = $this->faker->company(); + + return [ + 'name' => $name, + 'secret' => Str::random(40), + 'lti_platform_id' => Str::random(8), + ]; + } +} diff --git a/workbench/database/factories/OverrideModels/SimpleLtiEnvironmentFactory.php b/workbench/database/factories/OverrideModels/SimpleLtiEnvironmentFactory.php new file mode 100644 index 0000000..7a067d2 --- /dev/null +++ b/workbench/database/factories/OverrideModels/SimpleLtiEnvironmentFactory.php @@ -0,0 +1,24 @@ + + */ +class SimpleLtiEnvironmentFactory extends Factory +{ + protected $model = SimpleLtiEnvironment::class; + + /** + * {@inheritDoc} + */ + public function definition() + { + return [ + 'name' => ucfirst($this->faker->words(random_int(3, 5), true)), + ]; + } +} diff --git a/workbench/database/migrations/2023_10_26_300000_add_simple_lti_environments_table.php b/workbench/database/migrations/2023_10_26_300000_add_simple_lti_environments_table.php index 9465b69..6dafa79 100644 --- a/workbench/database/migrations/2023_10_26_300000_add_simple_lti_environments_table.php +++ b/workbench/database/migrations/2023_10_26_300000_add_simple_lti_environments_table.php @@ -12,7 +12,7 @@ public function up(): void { Schema::create('simple_lti_environments', function (Blueprint $table) { - $table->uuid('id')->primary(); + $table->id(); // Admin title $table->string('name'); diff --git a/workbench/database/override_migrations/2023_10_26_400000_add_client_table.php b/workbench/database/override_migrations/2023_10_26_400000_add_client_table.php new file mode 100644 index 0000000..a7695df --- /dev/null +++ b/workbench/database/override_migrations/2023_10_26_400000_add_client_table.php @@ -0,0 +1,47 @@ +uuid('id')->primary(); + $table->integer('nr')->unique(); + + // Admin title + $table->string('name'); + + // Keys & secrets + $table->string('secret', 1024)->nullable(); + $table->text('public_key')->nullable(); + + // LTI information + $table->string('lti_platform_id', 255)->nullable(); + $table->string('lti_client_id', 255)->nullable(); + $table->string('lti_deployment_id', 255)->nullable(); + $table->string('lti_version', 10)->nullable(); + $table->string('lti_signature_method', 15)->default('HMAC-SHA1'); + $table->text('lti_profile'); + $table->text('lti_settings'); + + $table->timestamps(); + + $table->unique(['lti_platform_id', 'lti_client_id', 'lti_deployment_id']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::drop('clients'); + } +}; diff --git a/database/migrations/2023_10_26_100000_add_lti_tables.php b/workbench/database/override_migrations/2023_10_26_500000_add_lti_tables.php similarity index 87% rename from database/migrations/2023_10_26_100000_add_lti_tables.php rename to workbench/database/override_migrations/2023_10_26_500000_add_lti_tables.php index 8ccfebc..6ac964a 100644 --- a/database/migrations/2023_10_26_100000_add_lti_tables.php +++ b/workbench/database/override_migrations/2023_10_26_500000_add_lti_tables.php @@ -16,7 +16,8 @@ public function up(): void $table->uuidMorphs('lti_environment'); - $table->uuid('client_id'); + $table->foreignUuid('client_id')->constrained('clients')->cascadeOnDelete(); + $table->string('nonce', 50); $table->dateTime('expires_at'); @@ -30,7 +31,7 @@ public function up(): void $table->uuidMorphs('lti_environment'); - $table->uuid('client_id')->unique(); + $table->foreignUuid('client_id')->constrained('clients')->cascadeOnDelete(); $table->string('access_token', 2000); $table->text('scopes'); @@ -44,7 +45,8 @@ public function up(): void $table->uuidMorphs('lti_environment'); - $table->uuid('client_id'); + $table->foreignUuid('client_id')->constrained('clients')->cascadeOnDelete(); + $table->string('title')->nullable(); $table->string('external_context_id', 255); $table->text('settings'); @@ -57,7 +59,8 @@ public function up(): void $table->uuidMorphs('lti_environment'); - $table->uuid('client_id'); + $table->foreignUuid('client_id')->constrained('clients')->cascadeOnDelete(); + $table->foreignId('lti_context_id')->nullable()->constrained('lti_contexts')->cascadeOnDelete(); $table->string('title')->nullable(); diff --git a/workbench/database/override_migrations/2023_10_26_600000_add_simple_lti_environments_table.php b/workbench/database/override_migrations/2023_10_26_600000_add_simple_lti_environments_table.php new file mode 100644 index 0000000..9465b69 --- /dev/null +++ b/workbench/database/override_migrations/2023_10_26_600000_add_simple_lti_environments_table.php @@ -0,0 +1,31 @@ +uuid('id')->primary(); + + // Admin title + $table->string('name'); + + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::drop('simple_lti_environments'); + } +}; From e3373bca8b0de8ef875fefb488dcbe92dc5d69fc Mon Sep 17 00:00:00 2001 From: Rolf van de Krol Date: Tue, 21 Nov 2023 22:34:14 +0100 Subject: [PATCH 8/9] docs: update installation instructions --- README.md | 41 +++++++++++++++++++++++++++++++------- src/LtiServiceProvider.php | 1 - 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 564e862..3e18727 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# laravel-lti-provider +# Laravel LTI Provider [![Latest Version on Packagist][ico-version]][link-packagist] [![Software License][ico-license]](LICENSE.md) @@ -7,22 +7,49 @@ [![Total Downloads][ico-downloads]][link-downloads] [![Made by SWIS][ico-swis]][link-swis] +This packages provides a bridge between the Celtic LTI Provider and Laravel models. - - -## Install +## Install & Setup Via Composer ``` bash $ composer require swisnl/laravel-lti-provider ``` -## Usage -Run + +Then run command to copy the required files into your project. + ```bash php artisan lti-service-provider:install ``` -Implement the LtiClient interface to your oauth2 client model. Implement the methods and add the required attributes + +If you have Laravel package auto discovery disabled, add the service provider to your `config/app.php` file: + +```php +'providers' => [ + // ... + Swis\Laravel\Lti\Providers\LtiServiceProvider::class, +]; +``` + +The package comes with a very basic client implementation, but allows for overriding this implementation. To do so, +create a new class that implements the `Swis\Laravel\Lti\Contracts\LtiClient` interface and change the following to your +`config/lti-provider.php` file: + +```php + 'lti-client' => 'REFERENCE TO YOUR NEW CLASS', +``` + +For inspiration on how to implement your own client, take a look at the `Swis\Laravel\Lti\Models\SimpleClient` class +(the very basic implementation) or the `\Workbench\App\OverrideModels\Client` class (this is a more complex example used +in the tests to check if it is possible to override the default implementation and if the package can handle clients +with UUIDs instead of numeric ids). + +After you have set up your client, you can run the migrations to create the required tables: + +```bash +php artisan migrate +``` ## Change log diff --git a/src/LtiServiceProvider.php b/src/LtiServiceProvider.php index 46bda0b..979b1ff 100644 --- a/src/LtiServiceProvider.php +++ b/src/LtiServiceProvider.php @@ -22,7 +22,6 @@ public function configurePackage(Package $package): void $command ->publishConfigFile() ->publishMigrations() - ->askToRunMigrations() ->askToStarRepoOnGitHub('swisnl/laravel-lti-provider'); }); } From e4d0d73c9b3bcdd529100d6e4f1ae8c9a0a5e820 Mon Sep 17 00:00:00 2001 From: rolfvandekrol Date: Tue, 21 Nov 2023 21:34:52 +0000 Subject: [PATCH 9/9] Fix styling --- tests/ModelDataConnector/ModelDataConnectorTest.php | 1 - tests/ModelDataConnector/OverrideModelDataConnectorTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/ModelDataConnector/ModelDataConnectorTest.php b/tests/ModelDataConnector/ModelDataConnectorTest.php index e487261..af0001f 100644 --- a/tests/ModelDataConnector/ModelDataConnectorTest.php +++ b/tests/ModelDataConnector/ModelDataConnectorTest.php @@ -13,7 +13,6 @@ class ModelDataConnectorTest extends TestCase // This trait contains the actual tests, because we want to run the same // tests for a different set of models. use ModelDataConnectorTests; - use RefreshDatabase; protected LtiEnvironment $ltiEnvironment; diff --git a/tests/ModelDataConnector/OverrideModelDataConnectorTest.php b/tests/ModelDataConnector/OverrideModelDataConnectorTest.php index 8a5b116..d05a6d7 100644 --- a/tests/ModelDataConnector/OverrideModelDataConnectorTest.php +++ b/tests/ModelDataConnector/OverrideModelDataConnectorTest.php @@ -15,7 +15,6 @@ class OverrideModelDataConnectorTest extends TestCase // This trait contains the actual tests, because we want to run the same // tests for a different set of models. use ModelDataConnectorTests; - use RefreshDatabase; protected LtiEnvironment $ltiEnvironment;