diff --git a/README.md b/README.md index 6248bb6..8c6fbc4 100644 --- a/README.md +++ b/README.md @@ -41,11 +41,16 @@ $card = $client->accessCards->provision([ 'card_template_id' => '0xd3adb00b5', 'employee_id' => '123456789', 'tag_id' => 'DDEADB33FB00B5', - 'allow_on_multiple_devices' => true, 'full_name' => 'Employee name', 'email' => 'employee@yourwebsite.com', 'phone_number' => '+19547212241', 'classification' => 'full_time', + 'department' => 'Engineering', + 'location' => 'San Francisco', + 'site_name' => 'HQ Building A', + 'workstation' => '4F-207', + 'mail_stop' => 'MS-401', + 'company_address' => '123 Main St, San Francisco, CA 94105', 'start_date' => (new DateTime('now', new DateTimeZone('UTC')))->format('c'), 'expiration_date' => '2026-04-01T00:00:00.000Z', 'employee_photo' => '[image_in_base64_encoded_format]', @@ -83,6 +88,12 @@ $card = $client->accessCards->update([ 'employee_id' => '987654321', 'full_name' => 'Updated Employee Name', 'classification' => 'contractor', + 'department' => 'Marketing', + 'location' => 'New York', + 'site_name' => 'NYC Office', + 'workstation' => '2F-105', + 'mail_stop' => 'MS-200', + 'company_address' => '456 Broadway, New York, NY 10013', 'expiration_date' => (new DateTime('now', new DateTimeZone('UTC')))->modify('+3 months')->format('c'), 'employee_photo' => '[image_in_base64_encoded_format]', 'title' => 'Senior Developer' @@ -277,6 +288,67 @@ $result = $client->console->hid->orgs->activate([ ]); ``` +### Landing Pages + +```php +// List all landing pages +$landingPages = $client->console->listLandingPages(); + +foreach ($landingPages as $page) { + echo "ID: {$page->id}, Name: {$page->name}, Kind: {$page->kind}\n"; + echo " Password Protected: {$page->password_protected}\n"; + if ($page->logo_url) { + echo " Logo URL: {$page->logo_url}\n"; + } +} + +// Create a landing page +$landingPage = $client->console->createLandingPage([ + 'name' => 'Miami Office Access Pass', + 'kind' => 'universal', + 'additional_text' => 'Welcome to the Miami Office', + 'bg_color' => '#f1f5f9', + 'allow_immediate_download' => true +]); + +echo "Landing page created: {$landingPage->id}\n"; +echo "Name: {$landingPage->name}, Kind: {$landingPage->kind}\n"; + +// Update a landing page +$landingPage = $client->console->updateLandingPage('0xlandingpage1d', [ + 'name' => 'Updated Miami Office Access Pass', + 'additional_text' => 'Welcome! Tap below to get your access pass.', + 'bg_color' => '#e2e8f0' +]); + +echo "Landing page updated: {$landingPage->id}\n"; +echo "Name: {$landingPage->name}\n"; +``` + +### Credential Profiles + +```php +// List all credential profiles +$profiles = $client->console->credentialProfiles->list(); + +foreach ($profiles as $profile) { + echo "ID: {$profile->id}, Name: {$profile->name}, AID: {$profile->aid}\n"; +} + +// Create a credential profile +$profile = $client->console->credentialProfiles->create([ + 'name' => 'Main Office Profile', + 'app_name' => 'KEY-ID-main', + 'keys' => [ + ['value' => 'your_32_char_hex_master_key_here'], + ['value' => 'your_32_char_hex__read_key__here'] + ] +]); + +echo "Profile created: {$profile->id}\n"; +echo "AID: {$profile->aid}\n"; +``` + ## Error Handling ```php @@ -312,5 +384,10 @@ MIT License | GET /v1/console/pass-template-pairs | Y | | GET /v1/console/ledger-items | Y | | POST /v1/console/ios-preflight | Y | +| GET /v1/console/landing-pages | Y | +| POST /v1/console/landing-pages | Y | +| PATCH /v1/console/landing-pages/{id} | Y | +| GET /v1/console/credential-profiles | Y | +| POST /v1/console/credential-profiles | Y | | Webhooks (list/create/delete) | Y | | HID orgs (create/activate/list) | Y | diff --git a/src/Models/AccessCard.php b/src/Models/AccessCard.php index c822cb7..5787b4a 100644 --- a/src/Models/AccessCard.php +++ b/src/Models/AccessCard.php @@ -23,6 +23,17 @@ class AccessCard public ?string $file_data; public ?string $directInstallUrl; public ?string $direct_install_url; + public ?string $organizationName; + public ?string $organization_name; + public ?string $department; + public ?string $location; + public ?string $siteName; + public ?string $site_name; + public ?string $workstation; + public ?string $mailStop; + public ?string $mail_stop; + public ?string $companyAddress; + public ?string $company_address; public $details; public array $devices; public array $metadata; @@ -46,6 +57,17 @@ public function __construct(AccessGridClient $client, array $data) $this->file_data = $data['file_data'] ?? null; $this->directInstallUrl = $data['direct_install_url'] ?? null; $this->direct_install_url = $data['direct_install_url'] ?? null; + $this->organizationName = $data['organization_name'] ?? null; + $this->organization_name = $data['organization_name'] ?? null; + $this->department = $data['department'] ?? null; + $this->location = $data['location'] ?? null; + $this->siteName = $data['site_name'] ?? null; + $this->site_name = $data['site_name'] ?? null; + $this->workstation = $data['workstation'] ?? null; + $this->mailStop = $data['mail_stop'] ?? null; + $this->mail_stop = $data['mail_stop'] ?? null; + $this->companyAddress = $data['company_address'] ?? null; + $this->company_address = $data['company_address'] ?? null; $this->details = $data['details'] ?? null; $this->devices = $data['devices'] ?? []; $this->metadata = $data['metadata'] ?? []; diff --git a/src/Models/CredentialProfile.php b/src/Models/CredentialProfile.php new file mode 100644 index 0000000..c1f80e8 --- /dev/null +++ b/src/Models/CredentialProfile.php @@ -0,0 +1,37 @@ +client = $client; + $this->id = $data['id'] ?? null; + $this->aid = $data['aid'] ?? null; + $this->name = $data['name'] ?? null; + $this->appleId = $data['apple_id'] ?? null; + $this->apple_id = $data['apple_id'] ?? null; + $this->createdAt = $data['created_at'] ?? null; + $this->created_at = $data['created_at'] ?? null; + $this->cardStorage = $data['card_storage'] ?? null; + $this->card_storage = $data['card_storage'] ?? null; + $this->keys = $data['keys'] ?? []; + $this->files = $data['files'] ?? []; + } +} diff --git a/src/Models/LandingPage.php b/src/Models/LandingPage.php new file mode 100644 index 0000000..eb0edcd --- /dev/null +++ b/src/Models/LandingPage.php @@ -0,0 +1,33 @@ +client = $client; + $this->id = $data['id'] ?? null; + $this->name = $data['name'] ?? null; + $this->kind = $data['kind'] ?? null; + $this->passwordProtected = $data['password_protected'] ?? null; + $this->password_protected = $data['password_protected'] ?? null; + $this->logoUrl = $data['logo_url'] ?? null; + $this->logo_url = $data['logo_url'] ?? null; + $this->createdAt = $data['created_at'] ?? null; + $this->created_at = $data['created_at'] ?? null; + } +} diff --git a/src/Services/Console.php b/src/Services/Console.php index 25e1107..6f07557 100644 --- a/src/Services/Console.php +++ b/src/Services/Console.php @@ -6,17 +6,21 @@ use AccessGrid\Models\Template; use AccessGrid\Models\PassTemplatePair; use AccessGrid\Models\LedgerItem; +use AccessGrid\Models\LandingPage; +use AccessGrid\Models\CredentialProfile; use AccessGrid\Models\Webhook; class Console { private AccessGridClient $client; public HID $hid; + public CredentialProfiles $credentialProfiles; public function __construct(AccessGridClient $client) { $this->client = $client; $this->hid = new HID($client); + $this->credentialProfiles = new CredentialProfiles($client); } /** @@ -151,6 +155,39 @@ public function deleteWebhook(string $webhookId): void $this->client->delete("/v1/console/webhooks/{$webhookId}"); } + /** + * List all landing pages + * + * @return LandingPage[] + */ + public function listLandingPages(): array + { + $response = $this->client->get('/v1/console/landing-pages'); + + return array_map( + fn($item) => new LandingPage($this->client, $item), + $response + ); + } + + /** + * Create a landing page + */ + public function createLandingPage(array $data): LandingPage + { + $response = $this->client->post('/v1/console/landing-pages', $data); + return new LandingPage($this->client, $response); + } + + /** + * Update a landing page + */ + public function updateLandingPage(string $landingPageId, array $data): LandingPage + { + $response = $this->client->patch("/v1/console/landing-pages/{$landingPageId}", $data); + return new LandingPage($this->client, $response); + } + /** * Get iOS provisioning identifiers for preflight */ diff --git a/src/Services/CredentialProfiles.php b/src/Services/CredentialProfiles.php new file mode 100644 index 0000000..ba82d44 --- /dev/null +++ b/src/Services/CredentialProfiles.php @@ -0,0 +1,40 @@ +client = $client; + } + + /** + * List all credential profiles + * + * @return CredentialProfile[] + */ + public function list(): array + { + $response = $this->client->get('/v1/console/credential-profiles'); + + return array_map( + fn($item) => new CredentialProfile($this->client, $item), + $response + ); + } + + /** + * Create a credential profile + */ + public function create(array $data): CredentialProfile + { + $response = $this->client->post('/v1/console/credential-profiles', $data); + return new CredentialProfile($this->client, $response); + } +} diff --git a/tests/Models/AccessCardNewFieldsTest.php b/tests/Models/AccessCardNewFieldsTest.php new file mode 100644 index 0000000..db6a6ac --- /dev/null +++ b/tests/Models/AccessCardNewFieldsTest.php @@ -0,0 +1,52 @@ + 'card_123', + 'state' => 'active', + 'full_name' => 'John Doe', + 'organization_name' => 'Acme Corp', + 'department' => 'Engineering', + 'location' => 'San Francisco', + 'site_name' => 'HQ Building A', + 'workstation' => '4F-207', + 'mail_stop' => 'MS-401', + 'company_address' => '123 Main St, San Francisco, CA 94105', + ]; + + $card = new AccessCard($this->client, $data); + + $this->assertEquals('Acme Corp', $card->organization_name); + $this->assertEquals('Acme Corp', $card->organizationName); + $this->assertEquals('Engineering', $card->department); + $this->assertEquals('San Francisco', $card->location); + $this->assertEquals('HQ Building A', $card->site_name); + $this->assertEquals('HQ Building A', $card->siteName); + $this->assertEquals('4F-207', $card->workstation); + $this->assertEquals('MS-401', $card->mail_stop); + $this->assertEquals('MS-401', $card->mailStop); + $this->assertEquals('123 Main St, San Francisco, CA 94105', $card->company_address); + $this->assertEquals('123 Main St, San Francisco, CA 94105', $card->companyAddress); + } + + public function testCardNewFieldsNullWhenAbsent(): void + { + $card = new AccessCard($this->client, ['id' => 'card_456']); + + $this->assertNull($card->organization_name); + $this->assertNull($card->department); + $this->assertNull($card->location); + $this->assertNull($card->site_name); + $this->assertNull($card->workstation); + $this->assertNull($card->mail_stop); + $this->assertNull($card->company_address); + } +} diff --git a/tests/Models/CredentialProfileTest.php b/tests/Models/CredentialProfileTest.php new file mode 100644 index 0000000..257e426 --- /dev/null +++ b/tests/Models/CredentialProfileTest.php @@ -0,0 +1,54 @@ + 'cp_123', + 'aid' => 'F0394148', + 'name' => 'Main Office Profile', + 'apple_id' => 'apple_456', + 'created_at' => '2025-06-01T12:00:00Z', + 'card_storage' => 'desfire', + 'keys' => [ + ['label' => 'master', 'value' => 'abc123', 'ex_id' => 'key_1'], + ], + 'files' => [ + ['ex_id' => 'file_1', 'communication_settings' => 'plain'], + ], + ]; + + $profile = new CredentialProfile($this->client, $data); + + $this->assertEquals('cp_123', $profile->id); + $this->assertEquals('F0394148', $profile->aid); + $this->assertEquals('Main Office Profile', $profile->name); + $this->assertEquals('apple_456', $profile->apple_id); + $this->assertEquals('apple_456', $profile->appleId); + $this->assertEquals('2025-06-01T12:00:00Z', $profile->created_at); + $this->assertEquals('2025-06-01T12:00:00Z', $profile->createdAt); + $this->assertEquals('desfire', $profile->card_storage); + $this->assertEquals('desfire', $profile->cardStorage); + $this->assertCount(1, $profile->keys); + $this->assertCount(1, $profile->files); + } + + public function testConstructionWithMinimalData(): void + { + $profile = new CredentialProfile($this->client, ['id' => 'cp_456']); + + $this->assertEquals('cp_456', $profile->id); + $this->assertNull($profile->aid); + $this->assertNull($profile->name); + $this->assertNull($profile->apple_id); + $this->assertNull($profile->card_storage); + $this->assertEquals([], $profile->keys); + $this->assertEquals([], $profile->files); + } +} diff --git a/tests/Models/LandingPageTest.php b/tests/Models/LandingPageTest.php new file mode 100644 index 0000000..0fecfed --- /dev/null +++ b/tests/Models/LandingPageTest.php @@ -0,0 +1,45 @@ + 'lp_123', + 'name' => 'Miami Office', + 'kind' => 'universal', + 'password_protected' => true, + 'logo_url' => 'https://example.com/logo.png', + 'created_at' => '2025-06-01T12:00:00Z', + ]; + + $page = new LandingPage($this->client, $data); + + $this->assertEquals('lp_123', $page->id); + $this->assertEquals('Miami Office', $page->name); + $this->assertEquals('universal', $page->kind); + $this->assertTrue($page->password_protected); + $this->assertTrue($page->passwordProtected); + $this->assertEquals('https://example.com/logo.png', $page->logo_url); + $this->assertEquals('https://example.com/logo.png', $page->logoUrl); + $this->assertEquals('2025-06-01T12:00:00Z', $page->created_at); + $this->assertEquals('2025-06-01T12:00:00Z', $page->createdAt); + } + + public function testConstructionWithMinimalData(): void + { + $page = new LandingPage($this->client, ['id' => 'lp_456']); + + $this->assertEquals('lp_456', $page->id); + $this->assertNull($page->name); + $this->assertNull($page->kind); + $this->assertNull($page->password_protected); + $this->assertNull($page->logo_url); + $this->assertNull($page->created_at); + } +} diff --git a/tests/Services/CredentialProfilesTest.php b/tests/Services/CredentialProfilesTest.php new file mode 100644 index 0000000..1151d28 --- /dev/null +++ b/tests/Services/CredentialProfilesTest.php @@ -0,0 +1,73 @@ +expectRequest('GET', '/v1/console/credential-profiles', 200, [ + [ + 'id' => 'cp_123', + 'aid' => 'F0394148', + 'name' => 'Main Office Profile', + 'apple_id' => 'apple_456', + 'created_at' => '2025-06-01T12:00:00Z', + 'card_storage' => 'desfire', + 'keys' => [], + 'files' => [], + ], + ]); + + $profiles = $this->client->console->credentialProfiles->list(); + + $this->assertCount(1, $profiles); + $this->assertInstanceOf(CredentialProfile::class, $profiles[0]); + $this->assertEquals('cp_123', $profiles[0]->id); + $this->assertEquals('F0394148', $profiles[0]->aid); + $this->assertEquals('Main Office Profile', $profiles[0]->name); + } + + public function testListCredentialProfilesEmpty(): void + { + $this->expectRequest('GET', '/v1/console/credential-profiles', 200, []); + + $profiles = $this->client->console->credentialProfiles->list(); + + $this->assertCount(0, $profiles); + } + + public function testCreateCredentialProfile(): void + { + $this->expectRequest('POST', '/v1/console/credential-profiles', 200, [ + 'id' => 'cp_new', + 'aid' => 'F0394148', + 'name' => 'Main Office Profile', + 'apple_id' => 'apple_789', + 'created_at' => '2025-06-15T10:00:00Z', + 'card_storage' => 'desfire', + 'keys' => [ + ['label' => 'master', 'value' => 'abc123', 'ex_id' => 'key_1'], + ['label' => 'read', 'value' => 'def456', 'ex_id' => 'key_2'], + ], + 'files' => [], + ]); + + $profile = $this->client->console->credentialProfiles->create([ + 'name' => 'Main Office Profile', + 'app_name' => 'KEY-ID-main', + 'keys' => [ + ['value' => 'abc123'], + ['value' => 'def456'], + ], + ]); + + $this->assertInstanceOf(CredentialProfile::class, $profile); + $this->assertEquals('cp_new', $profile->id); + $this->assertEquals('F0394148', $profile->aid); + $this->assertCount(2, $profile->keys); + } +} diff --git a/tests/Services/LandingPagesTest.php b/tests/Services/LandingPagesTest.php new file mode 100644 index 0000000..941c53c --- /dev/null +++ b/tests/Services/LandingPagesTest.php @@ -0,0 +1,84 @@ +expectRequest('GET', '/v1/console/landing-pages', 200, [ + [ + 'id' => 'lp_123', + 'name' => 'Miami Office', + 'kind' => 'universal', + 'password_protected' => false, + 'logo_url' => null, + 'created_at' => '2025-06-01T12:00:00Z', + ], + ]); + + $pages = $this->client->console->listLandingPages(); + + $this->assertCount(1, $pages); + $this->assertInstanceOf(LandingPage::class, $pages[0]); + $this->assertEquals('lp_123', $pages[0]->id); + $this->assertEquals('Miami Office', $pages[0]->name); + $this->assertEquals('universal', $pages[0]->kind); + } + + public function testListLandingPagesEmpty(): void + { + $this->expectRequest('GET', '/v1/console/landing-pages', 200, []); + + $pages = $this->client->console->listLandingPages(); + + $this->assertCount(0, $pages); + } + + public function testCreateLandingPage(): void + { + $this->expectRequest('POST', '/v1/console/landing-pages', 200, [ + 'id' => 'lp_new', + 'name' => 'Miami Office Access Pass', + 'kind' => 'universal', + 'password_protected' => false, + 'created_at' => '2025-06-15T10:00:00Z', + ]); + + $page = $this->client->console->createLandingPage([ + 'name' => 'Miami Office Access Pass', + 'kind' => 'universal', + 'additional_text' => 'Welcome to the Miami Office', + 'bg_color' => '#f1f5f9', + 'allow_immediate_download' => true, + ]); + + $this->assertInstanceOf(LandingPage::class, $page); + $this->assertEquals('lp_new', $page->id); + $this->assertEquals('Miami Office Access Pass', $page->name); + } + + public function testUpdateLandingPage(): void + { + $this->expectRequest('PATCH', '/v1/console/landing-pages/lp_123', 200, [ + 'id' => 'lp_123', + 'name' => 'Updated Miami Office', + 'kind' => 'universal', + 'password_protected' => false, + 'created_at' => '2025-06-01T12:00:00Z', + ]); + + $page = $this->client->console->updateLandingPage('lp_123', [ + 'name' => 'Updated Miami Office', + 'additional_text' => 'Welcome!', + 'bg_color' => '#e2e8f0', + ]); + + $this->assertInstanceOf(LandingPage::class, $page); + $this->assertEquals('lp_123', $page->id); + $this->assertEquals('Updated Miami Office', $page->name); + } +}