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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions accessgrid.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,34 @@ type (

// Event represents an event in the event log
Event = models.Event

// Pagination represents pagination metadata in list responses
Pagination = models.Pagination

// TemplateInfo represents minimal template info within a PassTemplatePair
TemplateInfo = models.TemplateInfo

// PassTemplatePair represents a paired iOS and Android template configuration
PassTemplatePair = models.PassTemplatePair

// PassTemplatePairsResponse represents the response from listing pass template pairs
PassTemplatePairsResponse = models.PassTemplatePairsResponse

// ListPassTemplatePairsParams defines parameters for listing pass template pairs
ListPassTemplatePairsParams = models.ListPassTemplatePairsParams

// LedgerItemPassTemplate represents a pass template reference within a ledger item's access pass
LedgerItemPassTemplate = models.LedgerItemPassTemplate

// LedgerItemAccessPass represents an access pass reference within a ledger item
LedgerItemAccessPass = models.LedgerItemAccessPass

// LedgerItem represents a billing ledger item
LedgerItem = models.LedgerItem

// LedgerItemsResponse represents the response from listing ledger items
LedgerItemsResponse = models.LedgerItemsResponse

// ListLedgerItemsParams defines parameters for listing ledger items
ListLedgerItemsParams = models.ListLedgerItemsParams
)
82 changes: 82 additions & 0 deletions models/models.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ type Card struct {
Details interface{} `json:"details,omitempty"`
FileData string `json:"file_data,omitempty"`
DirectInstallURL string `json:"direct_install_url,omitempty"`
Temporary bool `json:"temporary"`
Devices []Device `json:"devices,omitempty"`
Metadata map[string]interface{} `json:"metadata,omitempty"`
CreatedAt time.Time `json:"created_at"`
Expand All @@ -56,6 +57,7 @@ type CardProvisionResponse struct {
URL string `json:"install_url"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
Temporary bool `json:"temporary"`
DirectInstallUrl string `json:"direct_install_url"`
Details []Card `json:"details"`
}
Expand All @@ -74,6 +76,7 @@ type ProvisionParams struct {
StartDate time.Time `json:"start_date"`
ExpirationDate time.Time `json:"expiration_date"`
EmployeePhoto string `json:"employee_photo"`
Temporary bool `json:"temporary,omitempty"`
}

// UpdateParams defines parameters for updating an existing card
Expand Down Expand Up @@ -172,3 +175,82 @@ type Event struct {
Timestamp time.Time `json:"timestamp"`
Details string `json:"details"`
}

// Pagination represents pagination metadata in list responses
type Pagination struct {
CurrentPage int `json:"current_page"`
PerPage int `json:"per_page,omitempty"`
TotalPages int `json:"total_pages"`
TotalCount int `json:"total_count,omitempty"`
}

// TemplateInfo represents minimal template info within a PassTemplatePair
type TemplateInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Platform string `json:"platform"`
}

// PassTemplatePair represents a paired iOS and Android template configuration
type PassTemplatePair struct {
ID string `json:"id"`
Name string `json:"name"`
CreatedAt time.Time `json:"created_at"`
IOSTemplate *TemplateInfo `json:"ios_template"`
AndroidTemplate *TemplateInfo `json:"android_template"`
}

// PassTemplatePairsResponse represents the response from listing pass template pairs
type PassTemplatePairsResponse struct {
PassTemplatePairs []PassTemplatePair `json:"pass_template_pairs"`
Pagination Pagination `json:"pagination"`
}

// ListPassTemplatePairsParams defines parameters for listing pass template pairs
type ListPassTemplatePairsParams struct {
Page int `json:"page,omitempty"`
PerPage int `json:"per_page,omitempty"`
}

// LedgerItemPassTemplate represents a pass template reference within a ledger item's access pass
type LedgerItemPassTemplate struct {
ID string `json:"id"`
Name string `json:"name"`
Protocol string `json:"protocol"`
Platform string `json:"platform"`
UseCase string `json:"use_case"`
}

// LedgerItemAccessPass represents an access pass reference within a ledger item
type LedgerItemAccessPass struct {
ID string `json:"id"`
FullName string `json:"full_name"`
State string `json:"state"`
Metadata map[string]interface{} `json:"metadata"`
UnifiedAccessPassExID string `json:"unified_access_pass_ex_id"`
PassTemplate *LedgerItemPassTemplate `json:"pass_template,omitempty"`
}

// LedgerItem represents a billing ledger item
type LedgerItem struct {
CreatedAt time.Time `json:"created_at"`
Amount float64 `json:"amount"`
ID string `json:"id"`
Kind string `json:"kind"`
Metadata map[string]interface{} `json:"metadata"`
AccessPass *LedgerItemAccessPass `json:"access_pass"`
}

// LedgerItemsResponse represents the response from listing ledger items
type LedgerItemsResponse struct {
LedgerItems []LedgerItem `json:"ledger_items"`
Pagination Pagination `json:"pagination"`
}

// ListLedgerItemsParams defines parameters for listing ledger items
type ListLedgerItemsParams struct {
Page int `json:"page,omitempty"`
PerPage int `json:"per_page,omitempty"`
StartDate *time.Time `json:"start_date,omitempty"`
EndDate *time.Time `json:"end_date,omitempty"`
}
81 changes: 81 additions & 0 deletions services/access_cards_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package services
import (
"context"
"errors"
"io"
"net/http"
"net/http/httptest"
"strings"
Expand Down Expand Up @@ -191,6 +192,86 @@ func TestAccessCardsService_CardStateOperations(t *testing.T) {
}
}

func TestAccessCardsService_ProvisionTemporary(t *testing.T) {
var capturedBody string
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
body, _ := io.ReadAll(r.Body)
capturedBody = string(body)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{
"id": "0xtemp1d",
"card_template_id": "0xd3adb00b5",
"full_name": "Temp Worker",
"state": "active",
"temporary": true,
"install_url": "https://accessgrid.com/install/0xtemp1d"
}`))
}))
defer server.Close()

c, _ := client.NewClient("test-account", "test-secret", client.WithBaseURL(server.URL))
service := NewAccessCardsService(c)

startDate, _ := time.Parse(time.RFC3339, "2025-01-01T00:00:00Z")
expDate, _ := time.Parse(time.RFC3339, "2025-01-02T00:00:00Z")

params := models.ProvisionParams{
CardTemplateID: "0xd3adb00b5",
EmployeeID: "tmp_001",
CardNumber: "99999",
FullName: "Temp Worker",
Email: "temp@example.com",
PhoneNumber: "+15551234567",
Classification: "contractor",
StartDate: startDate,
ExpirationDate: expDate,
Temporary: true,
}

ctx := context.Background()
card, err := service.Provision(ctx, params)
if err != nil {
t.Fatalf("Provision() error = %v", err)
}

// Verify temporary is sent in request body
if !strings.Contains(capturedBody, `"temporary":true`) {
t.Errorf("expected temporary:true in request body, got %s", capturedBody)
}

// Verify temporary is deserialized on response
if !card.Temporary {
t.Errorf("card.Temporary = %v, want true", card.Temporary)
}
}

func TestAccessCardsService_NonTemporaryCard(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{
"id": "0xc4rd1d",
"card_template_id": "0xd3adb00b5",
"full_name": "Regular Worker",
"state": "active",
"temporary": false
}`))
}))
defer server.Close()

c, _ := client.NewClient("test-account", "test-secret", client.WithBaseURL(server.URL))
service := NewAccessCardsService(c)

ctx := context.Background()
card, err := service.Get(ctx, "0xc4rd1d")
if err != nil {
t.Fatalf("Get() error = %v", err)
}

if card.Temporary {
t.Errorf("card.Temporary = %v, want false", card.Temporary)
}
}

func TestAccessCardsService_ErrorPropagation(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
Expand Down
54 changes: 54 additions & 0 deletions services/console.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,60 @@ func (s *ConsoleService) DeleteTemplate(ctx context.Context, templateID string)
return nil
}

// ListPassTemplatePairs retrieves pass template pairs
func (s *ConsoleService) ListPassTemplatePairs(ctx context.Context, params models.ListPassTemplatePairsParams) (*models.PassTemplatePairsResponse, error) {
var response models.PassTemplatePairsResponse

query := url.Values{}
if params.Page > 0 {
query.Add("page", fmt.Sprintf("%d", params.Page))
}
if params.PerPage > 0 {
query.Add("per_page", fmt.Sprintf("%d", params.PerPage))
}

u := url.URL{Path: "/v1/console/pass-template-pairs"}
if len(query) > 0 {
u.RawQuery = query.Encode()
}

err := s.client.Request(ctx, http.MethodGet, u.String(), nil, &response)
if err != nil {
return nil, fmt.Errorf("error listing pass template pairs: %w", err)
}
return &response, nil
}

// ListLedgerItems retrieves billing ledger items
func (s *ConsoleService) ListLedgerItems(ctx context.Context, params models.ListLedgerItemsParams) (*models.LedgerItemsResponse, error) {
var response models.LedgerItemsResponse

query := url.Values{}
if params.Page > 0 {
query.Add("page", fmt.Sprintf("%d", params.Page))
}
if params.PerPage > 0 {
query.Add("per_page", fmt.Sprintf("%d", params.PerPage))
}
if params.StartDate != nil {
query.Add("start_date", params.StartDate.Format(time.RFC3339))
}
if params.EndDate != nil {
query.Add("end_date", params.EndDate.Format(time.RFC3339))
}

u := url.URL{Path: "/v1/console/ledger-items"}
if len(query) > 0 {
u.RawQuery = query.Encode()
}

err := s.client.Request(ctx, http.MethodGet, u.String(), nil, &response)
if err != nil {
return nil, fmt.Errorf("error listing ledger items: %w", err)
}
return &response, nil
}

// EventLog retrieves event logs for a specific template
func (s *ConsoleService) EventLog(ctx context.Context, templateID string, filters models.EventLogFilters) ([]models.Event, error) {
var events []models.Event
Expand Down
Loading
Loading