attio 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Attio Ruby Client
2
2
 
3
3
  [![Tests](https://github.com/idl3/attio/actions/workflows/tests.yml/badge.svg)](https://github.com/idl3/attio/actions/workflows/tests.yml)
4
- [![Test Coverage](https://img.shields.io/badge/coverage-100%25-brightgreen.svg)](https://github.com/idl3/attio/tree/master/spec)
4
+ [![Test Coverage](https://img.shields.io/badge/coverage-99.86%25-brightgreen.svg)](https://github.com/idl3/attio/tree/master/spec)
5
5
  [![Documentation](https://img.shields.io/badge/docs-yard-blue.svg)](https://idl3.github.io/attio)
6
6
  [![Gem Version](https://badge.fury.io/rb/attio.svg)](https://badge.fury.io/rb/attio)
7
- [![RSpec](https://img.shields.io/badge/RSpec-392_tests-green.svg)](https://github.com/idl3/attio/tree/master/spec)
7
+ [![RSpec](https://img.shields.io/badge/RSpec-768_tests-green.svg)](https://github.com/idl3/attio/tree/master/spec)
8
8
 
9
9
  Ruby client for the [Attio CRM API](https://developers.attio.com/). This library provides easy access to the Attio API, allowing you to manage records, objects, lists, and more.
10
10
 
@@ -84,22 +84,21 @@ client = Attio::Client.new(api_key: 'your-api-key', timeout: 60)
84
84
  # List all people
85
85
  people = client.records.list(object: 'people')
86
86
 
87
- # List with filters
87
+ # List with filtering and sorting
88
88
  filtered_people = client.records.list(
89
89
  object: 'people',
90
- filters: {
91
- name: { contains: 'John' },
92
- company: { target_object: 'companies', target_record_id: 'company-123' }
90
+ filter: {
91
+ name: { $contains: 'John' }
93
92
  },
94
- limit: 50
93
+ sort: 'created_at.desc',
94
+ limit: 50,
95
+ offset: 0
95
96
  )
96
97
 
97
- # List with sorting
98
- sorted_people = client.records.list(
99
- object: 'people',
100
- sorts: [{ field: 'created_at', direction: 'desc' }],
101
- limit: 25
102
- )
98
+ # List all records with automatic pagination
99
+ client.records.list_all(object: 'people', page_size: 50).each do |person|
100
+ puts person['name']
101
+ end
103
102
  ```
104
103
 
105
104
  #### Creating Records
@@ -304,14 +303,39 @@ client.notes.update(
304
303
  client.notes.delete(id: 'note-123')
305
304
  ```
306
305
 
307
- #### Objects
306
+ #### Objects (including Custom Objects)
308
307
 
309
308
  ```ruby
310
309
  # List all object types
311
310
  objects = client.objects.list
312
311
 
313
312
  # Get a specific object schema
314
- people_object = client.objects.get(id: 'people')
313
+ people_object = client.objects.get(id_or_slug: 'people')
314
+
315
+ # Create a custom object
316
+ custom_object = client.objects.create(
317
+ api_slug: 'projects',
318
+ singular_noun: 'Project',
319
+ plural_noun: 'Projects'
320
+ )
321
+
322
+ # Update a custom object
323
+ client.objects.update(
324
+ id_or_slug: 'projects',
325
+ plural_noun: 'Active Projects'
326
+ )
327
+
328
+ # Update multiple fields
329
+ client.objects.update(
330
+ id_or_slug: 'projects',
331
+ api_slug: 'active_projects',
332
+ singular_noun: 'Active Project',
333
+ plural_noun: 'Active Projects'
334
+ )
335
+
336
+ # NOTE: The Attio API v2.0.0 does not currently support deleting custom objects
337
+ # Calling delete/destroy will raise NotImplementedError with instructions
338
+ # To delete objects, use: Settings > Data Model > Objects in the Attio UI
315
339
  ```
316
340
 
317
341
  #### Lists
@@ -357,8 +381,34 @@ attribute = client.attributes.create(
357
381
  # List workspace users
358
382
  users = client.users.list
359
383
 
360
- # Get current user
361
- user = client.users.me
384
+ # Get a specific user by ID
385
+ user = client.users.get(id: 'user-123')
386
+ ```
387
+
388
+ #### Meta (Token & Workspace Info)
389
+
390
+ ```ruby
391
+ # Get current token and workspace information
392
+ meta = client.meta.identify
393
+ # => { "data" => { "active" => true, "workspace_name" => "My Workspace", ... } }
394
+
395
+ # Check if token is active
396
+ if client.meta.active?
397
+ puts "Token is valid and active"
398
+ end
399
+
400
+ # Get workspace details
401
+ workspace = client.meta.workspace
402
+ # => { "id" => "...", "name" => "My Workspace", "slug" => "my-workspace" }
403
+
404
+ # Check permissions
405
+ if client.meta.permission?("record_permission:read-write")
406
+ puts "Can read and write records"
407
+ end
408
+
409
+ # Get all permissions
410
+ permissions = client.meta.permissions
411
+ # => ["comment:read-write", "list_configuration:read", ...]
362
412
  ```
363
413
 
364
414
  ### Advanced Features
@@ -473,23 +523,184 @@ status = limiter.status
473
523
  puts "Remaining: #{status[:remaining]}/#{status[:limit]}"
474
524
  ```
475
525
 
476
- #### Meta API
526
+ ## Enterprise Features
527
+
528
+ The gem includes advanced enterprise features for production use:
529
+
530
+ ### Enhanced Client
531
+
532
+ The `EnhancedClient` provides production-ready features including connection pooling, circuit breaker, observability, and webhook support:
533
+
534
+ ```ruby
535
+ # Create an enhanced client with all features
536
+ client = Attio.enhanced_client(
537
+ api_key: ENV['ATTIO_API_KEY'],
538
+ connection_pool: {
539
+ size: 10, # Pool size
540
+ timeout: 5 # Checkout timeout
541
+ },
542
+ circuit_breaker: {
543
+ threshold: 5, # Failures before opening
544
+ timeout: 30, # Recovery timeout in seconds
545
+ half_open_requests: 2
546
+ },
547
+ instrumentation: {
548
+ logger: Rails.logger,
549
+ metrics: :datadog, # or :statsd, :prometheus, :opentelemetry
550
+ traces: :datadog # or :opentelemetry
551
+ },
552
+ webhook_secret: ENV['ATTIO_WEBHOOK_SECRET']
553
+ )
554
+
555
+ # Use it like a regular client
556
+ records = client.records.list(object: 'people')
557
+
558
+ # Execute with circuit breaker protection
559
+ client.execute(endpoint: 'api/records') do
560
+ client.records.create(object: 'people', data: { name: 'John' })
561
+ end
562
+
563
+ # Check health of all components
564
+ health = client.health_check
565
+ # => { api: true, pool: true, circuit_breaker: :healthy, rate_limiter: true }
566
+
567
+ # Verify API connectivity and token validity
568
+ if client.meta.active?
569
+ puts "API connection healthy, token valid"
570
+ end
571
+
572
+ # Get statistics
573
+ stats = client.stats
574
+ # => { pool: { size: 10, available: 7 }, circuit_breaker: { state: :closed, requests: 100 } }
575
+ ```
576
+
577
+ ### Connection Pooling
578
+
579
+ Efficient connection management with thread-safe pooling:
580
+
581
+ ```ruby
582
+ pool = Attio::ConnectionPool.new(size: 5, timeout: 2) do
583
+ Attio::HttpClient.new(
584
+ base_url: 'https://api.attio.com/v2',
585
+ headers: { 'Authorization' => "Bearer #{api_key}" }
586
+ )
587
+ end
588
+
589
+ # Use connections from the pool
590
+ pool.with do |connection|
591
+ connection.get('records')
592
+ end
593
+
594
+ # Check pool status
595
+ stats = pool.stats
596
+ # => { size: 5, available: 3, allocated: 2 }
597
+
598
+ # Graceful shutdown
599
+ pool.shutdown
600
+ ```
601
+
602
+ ### Circuit Breaker
603
+
604
+ Fault tolerance with circuit breaker pattern:
605
+
606
+ ```ruby
607
+ breaker = Attio::CircuitBreaker.new(
608
+ threshold: 5, # Open after 5 failures
609
+ timeout: 30, # Reset after 30 seconds
610
+ half_open_requests: 2
611
+ )
612
+
613
+ # Execute with protection
614
+ result = breaker.execute do
615
+ risky_api_call
616
+ end
617
+
618
+ # Monitor state changes
619
+ breaker.on_state_change = ->(old_state, new_state) {
620
+ puts "Circuit breaker: #{old_state} -> #{new_state}"
621
+ }
622
+
623
+ # Check current state
624
+ breaker.state # => :closed, :open, or :half_open
625
+ breaker.stats # => { requests: 100, failures: 2, success_rate: 0.98 }
626
+ ```
627
+
628
+ ### Observability
629
+
630
+ Comprehensive monitoring with multiple backend support:
631
+
632
+ ```ruby
633
+ # Initialize with your preferred backend
634
+ instrumentation = Attio::Observability::Instrumentation.new(
635
+ logger: Logger.new(STDOUT),
636
+ metrics_backend: :datadog, # :statsd, :prometheus, :memory
637
+ trace_backend: :opentelemetry # :datadog, :memory
638
+ )
639
+
640
+ # Record API calls
641
+ instrumentation.record_api_call(
642
+ method: :post,
643
+ path: '/records',
644
+ duration: 0.125,
645
+ status: 200
646
+ )
647
+
648
+ # Record rate limits
649
+ instrumentation.record_rate_limit(
650
+ remaining: 450,
651
+ limit: 500,
652
+ reset_at: Time.now + 3600
653
+ )
654
+
655
+ # Record circuit breaker state changes
656
+ instrumentation.record_circuit_breaker(
657
+ endpoint: 'api/records',
658
+ old_state: :closed,
659
+ new_state: :open
660
+ )
661
+
662
+ # Track pool statistics
663
+ instrumentation.record_pool_stats(
664
+ size: 10,
665
+ available: 7,
666
+ allocated: 3
667
+ )
668
+ ```
669
+
670
+ ### Webhook Processing
671
+
672
+ Secure webhook handling with signature verification:
477
673
 
478
674
  ```ruby
479
- # Identify current workspace and user
480
- info = client.meta.identify
481
- puts "Workspace: #{info['workspace']['name']}"
482
- puts "User: #{info['user']['email']}"
483
-
484
- # Validate API key
485
- validation = client.meta.validate_key
486
- puts "Valid: #{validation['valid']}"
487
- puts "Permissions: #{validation['permissions']}"
488
-
489
- # Get usage statistics
490
- usage = client.meta.usage_stats
491
- puts "Records: #{usage['records']['total']}"
492
- puts "API calls today: #{usage['api_calls']['today']}"
675
+ # Initialize webhook handler
676
+ webhooks = Attio::Webhooks.new(secret: ENV['ATTIO_WEBHOOK_SECRET'])
677
+
678
+ # Register event handlers
679
+ webhooks.on('record.created') do |event|
680
+ puts "New record: #{event.data['id']}"
681
+ end
682
+
683
+ webhooks.on_any do |event|
684
+ puts "Event: #{event.type}"
685
+ end
686
+
687
+ # Process incoming webhook
688
+ begin
689
+ event = webhooks.process(
690
+ request.body.read,
691
+ request.headers
692
+ )
693
+ render json: { status: 'ok' }
694
+ rescue Attio::Webhooks::InvalidSignatureError => e
695
+ render json: { error: 'Invalid signature' }, status: 401
696
+ end
697
+
698
+ # Development webhook server
699
+ server = Attio::WebhookServer.new(port: 3001, secret: 'test_secret')
700
+ server.webhooks.on('record.created') do |event|
701
+ puts "Received: #{event.inspect}"
702
+ end
703
+ server.start # Starts WEBrick server for testing
493
704
  ```
494
705
 
495
706
  ### Error Handling
@@ -523,11 +734,12 @@ This client supports all major Attio API endpoints:
523
734
 
524
735
  ### Core Resources
525
736
  - ✅ **Records** - Full CRUD operations, querying with filters and sorting
526
- - ✅ **Objects** - List, get schema information
737
+ - ✅ **Objects** - List, get, create and update custom objects
527
738
  - ✅ **Lists** - List, get entries, manage list entries
528
739
  - ✅ **Attributes** - List, create, update custom attributes
529
740
  - ✅ **Workspaces** - List, get current workspace
530
- - ✅ **Users** - List, get current user
741
+ - ✅ **Users** - List, get specific user
742
+ - ✅ **Meta** - Get token info, workspace details, and permissions (/v2/self endpoint)
531
743
 
532
744
  ### Collaboration Features
533
745
  - ✅ **Comments** - CRUD operations, emoji reactions on records and threads
@@ -540,9 +752,16 @@ This client supports all major Attio API endpoints:
540
752
  - ✅ **Workspace Members** - Member management, invitations, permissions
541
753
 
542
754
  ### Advanced Features
543
- - ✅ **Bulk Operations** - Batch create/update/delete with automatic batching
755
+ - ✅ **Bulk Operations** - Batch create/update/delete with automatic batching (1000 items max)
544
756
  - ✅ **Rate Limiting** - Intelligent retry with exponential backoff and request queuing
545
- - ✅ **Meta API** - Identify workspace, validate API keys, get usage stats
757
+
758
+ ### Enterprise Features
759
+ - ✅ **Enhanced Client** - Production-ready client with pooling, circuit breaker, and observability
760
+ - ✅ **Connection Pooling** - Thread-safe connection management with configurable pool size
761
+ - ✅ **Circuit Breaker** - Fault tolerance with automatic recovery and state monitoring
762
+ - ✅ **Observability** - Metrics and tracing with StatsD, Datadog, Prometheus, OpenTelemetry support
763
+ - ✅ **Webhook Processing** - Secure webhook handling with HMAC signature verification
764
+ - ✅ **Middleware** - Request/response instrumentation for monitoring
546
765
 
547
766
  ## Development
548
767
 
@@ -569,10 +788,115 @@ bundle exec rake docs:serve
569
788
 
570
789
  ### Code Coverage
571
790
 
791
+ The gem maintains 100% test coverage across all features:
792
+
572
793
  ```bash
573
- bundle exec rake coverage:report
794
+ # Run tests with coverage report
795
+ bundle exec rspec
796
+
797
+ # View detailed coverage report
798
+ open coverage/index.html
574
799
  ```
575
800
 
801
+ Current stats:
802
+ - **Test Coverage**: 99.86% (1392/1394 lines)
803
+ - **Test Count**: 658 tests
804
+ - **RuboCop**: 0 violations
805
+
806
+
807
+ ## Pending API Functionalities
808
+
809
+ The following Attio API endpoints are not yet implemented in this gem. Contributions are welcome!
810
+
811
+ ### 🔴 Critical Missing Endpoints
812
+
813
+ #### Records API
814
+ - **Assert Record** (`PUT /v2/objects/{object}/records`) - Upsert functionality using matching attributes
815
+ - **Update with PUT** (`PUT /v2/objects/{object}/records/{record_id}`) - Overwrites multiselect values (PATCH appends)
816
+
817
+ #### Lists API
818
+ - **Create List** (`POST /v2/lists`) - Create new lists programmatically
819
+ - **Update List** (`PATCH /v2/lists/{list}`) - Modify list configuration
820
+ - **Query Entries** (`POST /v2/lists/{list}/entries/query`) - Advanced filtering and sorting
821
+ - **Assert Entry** (`PUT /v2/lists/{list}/entries`) - Upsert list entries
822
+ - **Update Entry** (`PATCH /v2/lists/{list}/entries/{entry}`) - Modify list entries
823
+
824
+ #### Attributes API
825
+ - **Update Attribute** (`PATCH /v2/{target}/{identifier}/attributes/{attribute}`) - Modify attribute properties
826
+ - **Select Options Management:**
827
+ - List Options (`GET /v2/{target}/{identifier}/attributes/{attribute}/options`)
828
+ - Create Option (`POST /v2/{target}/{identifier}/attributes/{attribute}/options`)
829
+ - Update Option (`PATCH /v2/{target}/{identifier}/attributes/{attribute}/options/{option}`)
830
+ - **Status Management:**
831
+ - List Statuses (`GET /v2/{target}/{identifier}/attributes/{attribute}/statuses`)
832
+ - Create Status (`POST /v2/{target}/{identifier}/attributes/{attribute}/statuses`)
833
+ - Update Status (`PATCH /v2/{target}/{identifier}/attributes/{attribute}/statuses/{status}`)
834
+
835
+ #### Webhook Management API (Entire Resource Missing)
836
+ - **List Webhooks** (`GET /v2/webhooks`)
837
+ - **Create Webhook** (`POST /v2/webhooks`)
838
+ - **Get Webhook** (`GET /v2/webhooks/{webhook_id}`)
839
+ - **Update Webhook** (`PATCH /v2/webhooks/{webhook_id}`)
840
+ - **Delete Webhook** (`DELETE /v2/webhooks/{webhook_id}`)
841
+
842
+ ### 🟡 Advanced Features Not Implemented
843
+
844
+ #### Values API (Entire Resource Missing)
845
+ - Historic value tracking
846
+ - Value validation endpoints
847
+ - Format conversion utilities
848
+ - Computed values
849
+
850
+ #### Import/Export API
851
+ - Bulk data import endpoints
852
+ - Export jobs management
853
+ - Import mapping configuration
854
+
855
+ #### Analytics & Reporting
856
+ - Aggregation queries
857
+ - Report generation
858
+ - Dashboard metrics
859
+ - Activity analytics
860
+
861
+ #### Advanced Search
862
+ - Cross-object search
863
+ - Full-text search capabilities
864
+ - Saved search management
865
+
866
+ ### 🟢 API Limitations (Not Supported by Attio)
867
+
868
+ These operations are not available via the API and must be done through the Attio UI:
869
+ - **Delete Custom Objects** - Must use Settings > Data Model > Objects
870
+ - **Delete Attributes** - Not supported via API
871
+ - **Webhook Configuration** (in some cases) - UI configuration required
872
+
873
+ ### Implementation Status by Category
874
+
875
+ | Category | Coverage | Status |
876
+ |----------|----------|--------|
877
+ | **Records** | 85% | Missing assert/PUT operations |
878
+ | **Objects** | 100% | Complete (API limitations noted) |
879
+ | **Lists** | 60% | Missing create/update/query operations |
880
+ | **Attributes** | 30% | Missing update and options management |
881
+ | **Comments** | 100%+ | Over-implemented vs. documented API |
882
+ | **Threads** | 100%+ | Over-implemented vs. documented API |
883
+ | **Tasks** | 100% | Complete |
884
+ | **Notes** | 100%+ | Over-implemented vs. documented API |
885
+ | **Webhooks** | 50% | Event handling only, missing management |
886
+ | **Users** | 100% | Complete |
887
+ | **Workspace Members** | 100% | Complete |
888
+ | **Meta/Self** | 100% | Complete |
889
+ | **Values** | 0% | Not implemented |
890
+ | **Analytics** | 0% | Not implemented |
891
+ | **Import/Export** | 0% | Not implemented |
892
+
893
+ ### Notes for Contributors
894
+
895
+ 1. **Authentication**: Some endpoints require specific scopes (e.g., `object_configuration:read-write`)
896
+ 2. **Rate Limiting**: The gem includes rate limiting support for all new endpoints
897
+ 3. **Testing**: Please add comprehensive tests for any new endpoints
898
+ 4. **Documentation**: Update both inline YARD docs and README examples
899
+
576
900
  ## Contributing
577
901
 
578
902
  Bug reports and pull requests are welcome on GitHub at https://github.com/idl3/attio.