doorflow 1.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +27 -0
- data/LICENSE +21 -0
- data/README.md +356 -0
- data/bin/post-generate +47 -0
- data/doorflow.gemspec +55 -0
- data/lib/doorflow/auth/doorflow_auth.rb +308 -0
- data/lib/doorflow/auth/file_token_storage.rb +104 -0
- data/lib/doorflow/auth/token_storage.rb +106 -0
- data/lib/doorflow/auth.rb +32 -0
- data/lib/doorflow/client/account_proxy.rb +19 -0
- data/lib/doorflow/client/channels_proxy.rb +64 -0
- data/lib/doorflow/client/credential_types_proxy.rb +28 -0
- data/lib/doorflow/client/credentials_proxy.rb +73 -0
- data/lib/doorflow/client/events_proxy.rb +33 -0
- data/lib/doorflow/client/group_reservations_proxy.rb +46 -0
- data/lib/doorflow/client/groups_proxy.rb +51 -0
- data/lib/doorflow/client/notification_rules_proxy.rb +46 -0
- data/lib/doorflow/client/people_proxy.rb +69 -0
- data/lib/doorflow/client/reservations_proxy.rb +46 -0
- data/lib/doorflow/client/resource_proxy.rb +113 -0
- data/lib/doorflow/client/roles_proxy.rb +28 -0
- data/lib/doorflow/client/sites_proxy.rb +28 -0
- data/lib/doorflow/client.rb +263 -0
- data/lib/doorflow/errors/api_error.rb +28 -0
- data/lib/doorflow/errors/authentication_error.rb +23 -0
- data/lib/doorflow/errors/doorflow_error.rb +75 -0
- data/lib/doorflow/errors/forbidden_error.rb +22 -0
- data/lib/doorflow/errors/not_found_error.rb +22 -0
- data/lib/doorflow/errors/rate_limit_error.rb +65 -0
- data/lib/doorflow/errors/validation_error.rb +83 -0
- data/lib/doorflow/errors.rb +152 -0
- data/lib/doorflow/list_object.rb +125 -0
- data/lib/doorflow/resource.rb +153 -0
- data/lib/doorflow/resources/account.rb +66 -0
- data/lib/doorflow/resources/channel.rb +104 -0
- data/lib/doorflow/resources/credential.rb +73 -0
- data/lib/doorflow/resources/credential_type.rb +30 -0
- data/lib/doorflow/resources/event.rb +47 -0
- data/lib/doorflow/resources/group.rb +71 -0
- data/lib/doorflow/resources/group_reservation.rb +55 -0
- data/lib/doorflow/resources/notification_rule.rb +71 -0
- data/lib/doorflow/resources/person.rb +102 -0
- data/lib/doorflow/resources/reservation.rb +72 -0
- data/lib/doorflow/resources/role.rb +66 -0
- data/lib/doorflow/resources/site.rb +66 -0
- data/lib/doorflow/webhooks/event.rb +92 -0
- data/lib/doorflow/webhooks/handler.rb +206 -0
- data/lib/doorflow/webhooks/signature_verifier.rb +120 -0
- data/lib/doorflow/webhooks.rb +47 -0
- data/lib/doorflow-api/api/accounts_api.rb +79 -0
- data/lib/doorflow-api/api/admission_requests_api.rb +85 -0
- data/lib/doorflow-api/api/channels_api.rb +757 -0
- data/lib/doorflow-api/api/credential_types_api.rb +88 -0
- data/lib/doorflow-api/api/credentials_api.rb +458 -0
- data/lib/doorflow-api/api/events_api.rb +190 -0
- data/lib/doorflow-api/api/group_reservations_api.rb +225 -0
- data/lib/doorflow-api/api/groups_api.rb +79 -0
- data/lib/doorflow-api/api/notification_rules_api.rb +347 -0
- data/lib/doorflow-api/api/oauth_api.rb +360 -0
- data/lib/doorflow-api/api/people_api.rb +372 -0
- data/lib/doorflow-api/api/reservations_api.rb +225 -0
- data/lib/doorflow-api/api/roles_api.rb +79 -0
- data/lib/doorflow-api/api/sites_api.rb +88 -0
- data/lib/doorflow-api/api/sync_api.rb +79 -0
- data/lib/doorflow-api/api_client.rb +437 -0
- data/lib/doorflow-api/api_error.rb +63 -0
- data/lib/doorflow-api/api_model_base.rb +88 -0
- data/lib/doorflow-api/configuration.rb +399 -0
- data/lib/doorflow-api/models/account.rb +334 -0
- data/lib/doorflow-api/models/account_passport.rb +193 -0
- data/lib/doorflow-api/models/account_reseller.rb +192 -0
- data/lib/doorflow-api/models/account_sync.rb +214 -0
- data/lib/doorflow-api/models/account_user.rb +247 -0
- data/lib/doorflow-api/models/admission_request.rb +328 -0
- data/lib/doorflow-api/models/admission_request_door_controller.rb +159 -0
- data/lib/doorflow-api/models/admission_request_person.rb +159 -0
- data/lib/doorflow-api/models/admit_channel202_response.rb +192 -0
- data/lib/doorflow-api/models/admit_person202_response.rb +192 -0
- data/lib/doorflow-api/models/admit_person403_response.rb +157 -0
- data/lib/doorflow-api/models/admit_person_request.rb +165 -0
- data/lib/doorflow-api/models/auto_unlock_channel400_response.rb +156 -0
- data/lib/doorflow-api/models/auto_unlock_channel_request.rb +149 -0
- data/lib/doorflow-api/models/channel.rb +498 -0
- data/lib/doorflow-api/models/channel_auto_unlock.rb +159 -0
- data/lib/doorflow-api/models/channel_lockdown.rb +176 -0
- data/lib/doorflow-api/models/channel_lockdown_aux_lockdown.rb +148 -0
- data/lib/doorflow-api/models/channel_lockdown_card_lockdown.rb +148 -0
- data/lib/doorflow-api/models/channel_lockdown_rex_lockdown.rb +148 -0
- data/lib/doorflow-api/models/channel_mode.rb +212 -0
- data/lib/doorflow-api/models/channel_sync.rb +200 -0
- data/lib/doorflow-api/models/create_credential422_response.rb +157 -0
- data/lib/doorflow-api/models/create_credential422_response_errors.rb +173 -0
- data/lib/doorflow-api/models/credential.rb +336 -0
- data/lib/doorflow-api/models/credential_input.rb +164 -0
- data/lib/doorflow-api/models/credential_input_person_credential.rb +187 -0
- data/lib/doorflow-api/models/credential_type.rb +232 -0
- data/lib/doorflow-api/models/credential_update_input.rb +164 -0
- data/lib/doorflow-api/models/credential_update_input_person_credential.rb +165 -0
- data/lib/doorflow-api/models/delete_group_reservation200_response.rb +147 -0
- data/lib/doorflow-api/models/error.rb +192 -0
- data/lib/doorflow-api/models/event.rb +361 -0
- data/lib/doorflow-api/models/get_access_token200_response.rb +266 -0
- data/lib/doorflow-api/models/get_access_token400_response.rb +190 -0
- data/lib/doorflow-api/models/get_access_token401_response.rb +156 -0
- data/lib/doorflow-api/models/get_admission_request401_response.rb +156 -0
- data/lib/doorflow-api/models/get_admission_request403_response.rb +156 -0
- data/lib/doorflow-api/models/get_admission_request404_response.rb +156 -0
- data/lib/doorflow-api/models/get_admission_request500_response.rb +156 -0
- data/lib/doorflow-api/models/get_token_info200_response.rb +267 -0
- data/lib/doorflow-api/models/get_token_info200_response_application.rb +148 -0
- data/lib/doorflow-api/models/get_token_info401_response.rb +156 -0
- data/lib/doorflow-api/models/group.rb +284 -0
- data/lib/doorflow-api/models/group_reservation.rb +380 -0
- data/lib/doorflow-api/models/group_reservation_input.rb +164 -0
- data/lib/doorflow-api/models/group_reservation_input_group_reservation.rb +257 -0
- data/lib/doorflow-api/models/initiate_sync429_response.rb +243 -0
- data/lib/doorflow-api/models/list_events400_response.rb +156 -0
- data/lib/doorflow-api/models/lockdown_channel_request.rb +168 -0
- data/lib/doorflow-api/models/notification_rule.rb +324 -0
- data/lib/doorflow-api/models/notification_rule_actions_inner.rb +168 -0
- data/lib/doorflow-api/models/notification_rule_conditions_inner.rb +168 -0
- data/lib/doorflow-api/models/notification_rule_events_inner.rb +158 -0
- data/lib/doorflow-api/models/notification_rule_input.rb +254 -0
- data/lib/doorflow-api/models/person.rb +511 -0
- data/lib/doorflow-api/models/person_input.rb +413 -0
- data/lib/doorflow-api/models/reservation.rb +329 -0
- data/lib/doorflow-api/models/reservation_input.rb +281 -0
- data/lib/doorflow-api/models/revoke_token403_response.rb +156 -0
- data/lib/doorflow-api/models/role.rb +268 -0
- data/lib/doorflow-api/models/site.rb +254 -0
- data/lib/doorflow-api/models/site_site_ips_inner.rb +148 -0
- data/lib/doorflow-api/models/unlock_channel_request.rb +148 -0
- data/lib/doorflow-api/version.rb +15 -0
- data/lib/doorflow-api.rb +123 -0
- data/lib/doorflow.rb +171 -0
- data/spec/api/admission_requests_api_spec.rb +47 -0
- data/spec/api/channels_api_spec.rb +174 -0
- data/spec/api/credential_types_api_spec.rb +49 -0
- data/spec/api/group_reservations_api_spec.rb +75 -0
- data/spec/api/oauth_api_spec.rb +97 -0
- data/spec/doorflow/client_spec.rb +109 -0
- data/spec/doorflow_spec.rb +22 -0
- data/spec/fixtures/vcr_cassettes/channel/list.yml +70 -0
- data/spec/fixtures/vcr_cassettes/channel/retrieve.yml +131 -0
- data/spec/fixtures/vcr_cassettes/person/auto_pagination.yml +87 -0
- data/spec/fixtures/vcr_cassettes/person/create.yml +64 -0
- data/spec/fixtures/vcr_cassettes/person/delete.yml +125 -0
- data/spec/fixtures/vcr_cassettes/person/list.yml +90 -0
- data/spec/fixtures/vcr_cassettes/person/not_found.yml +62 -0
- data/spec/fixtures/vcr_cassettes/person/retrieve.yml +136 -0
- data/spec/fixtures/vcr_cassettes/person/update.yml +260 -0
- data/spec/fixtures/vcr_cassettes/person/validation_error.yml +62 -0
- data/spec/integration/channel_spec.rb +88 -0
- data/spec/integration/person_spec.rb +99 -0
- data/spec/models/account_passport_spec.rb +42 -0
- data/spec/models/account_reseller_spec.rb +60 -0
- data/spec/models/account_sync_spec.rb +52 -0
- data/spec/models/account_user_spec.rb +54 -0
- data/spec/models/admission_request_door_controller_spec.rb +42 -0
- data/spec/models/admission_request_person_spec.rb +42 -0
- data/spec/models/admission_request_spec.rb +82 -0
- data/spec/models/admit_channel202_response_spec.rb +46 -0
- data/spec/models/admit_person202_response_spec.rb +46 -0
- data/spec/models/admit_person403_response_spec.rb +42 -0
- data/spec/models/admit_person_request_spec.rb +36 -0
- data/spec/models/auto_unlock_channel400_response_spec.rb +42 -0
- data/spec/models/auto_unlock_channel_request_spec.rb +36 -0
- data/spec/models/channel_auto_unlock_spec.rb +42 -0
- data/spec/models/channel_lockdown_aux_lockdown_spec.rb +36 -0
- data/spec/models/channel_lockdown_card_lockdown_spec.rb +36 -0
- data/spec/models/channel_lockdown_rex_lockdown_spec.rb +36 -0
- data/spec/models/channel_lockdown_spec.rb +54 -0
- data/spec/models/channel_mode_spec.rb +52 -0
- data/spec/models/channel_sync_spec.rb +46 -0
- data/spec/models/create_credential422_response_errors_spec.rb +48 -0
- data/spec/models/create_credential422_response_spec.rb +42 -0
- data/spec/models/credential_input_person_credential_spec.rb +48 -0
- data/spec/models/credential_input_spec.rb +36 -0
- data/spec/models/credential_spec.rb +86 -0
- data/spec/models/credential_update_input_person_credential_spec.rb +36 -0
- data/spec/models/credential_update_input_spec.rb +36 -0
- data/spec/models/delete_group_reservation200_response_spec.rb +36 -0
- data/spec/models/error_spec.rb +42 -0
- data/spec/models/get_access_token200_response_spec.rb +66 -0
- data/spec/models/get_access_token400_response_spec.rb +46 -0
- data/spec/models/get_access_token401_response_spec.rb +42 -0
- data/spec/models/get_admission_request401_response_spec.rb +42 -0
- data/spec/models/get_admission_request403_response_spec.rb +42 -0
- data/spec/models/get_admission_request404_response_spec.rb +42 -0
- data/spec/models/get_admission_request500_response_spec.rb +42 -0
- data/spec/models/get_token_info200_response_application_spec.rb +36 -0
- data/spec/models/get_token_info200_response_spec.rb +66 -0
- data/spec/models/get_token_info401_response_spec.rb +42 -0
- data/spec/models/group_reservation_input_group_reservation_spec.rb +54 -0
- data/spec/models/group_reservation_input_spec.rb +36 -0
- data/spec/models/group_reservation_spec.rb +82 -0
- data/spec/models/initiate_sync429_response_spec.rb +52 -0
- data/spec/models/list_events400_response_spec.rb +42 -0
- data/spec/models/lockdown_channel_request_spec.rb +48 -0
- data/spec/models/notification_rule_actions_inner_spec.rb +48 -0
- data/spec/models/notification_rule_conditions_inner_spec.rb +48 -0
- data/spec/models/notification_rule_events_inner_spec.rb +42 -0
- data/spec/models/notification_rule_input_spec.rb +60 -0
- data/spec/models/person_input_spec.rb +180 -0
- data/spec/models/reservation_input_spec.rb +60 -0
- data/spec/models/revoke_token403_response_spec.rb +42 -0
- data/spec/models/site_site_ips_inner_spec.rb +36 -0
- data/spec/models/unlock_channel_request_spec.rb +36 -0
- data/spec/spec_helper.rb +160 -0
- metadata +495 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: f030947a7aa52576b9c64f55b185c888d43ff51c7257099a74db36608d2c14d3
|
|
4
|
+
data.tar.gz: 94aba11ea4616046c3e92d8a0bc6a71f61e2dc22ef3d229d5de07a3ff5c1aff3
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: f1793b621c409beca25bf87f90ff5246c3e1930219f75e57a503d873bf720f00c73e5b46f84c39ae498702a23de3d931c4d652e325a8d5134b410df55e9fcecc
|
|
7
|
+
data.tar.gz: a6488799e13203486b693ab13087489fedd35e998671133e5cb2956a761ede1f616bfe60a152493aa9f400135728e8929e517ac2d726c6cd9076d627f26dd7a6
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-02-04
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release of DoorFlow Ruby SDK for API v3
|
|
12
|
+
- OAuth 2.0 authentication with PKCE support
|
|
13
|
+
- Automatic token refresh with configurable buffer
|
|
14
|
+
- Token storage interface with `FileTokenStorage` reference implementation
|
|
15
|
+
- Stripe-style resource classes: Account, Channel, Credential, CredentialType, Event, Group, GroupReservation, NotificationRule, Person, Reservation, Role, Site
|
|
16
|
+
- Auto-pagination support with `.auto_paging_each`
|
|
17
|
+
- Webhook signature verification and event handling
|
|
18
|
+
- Full support for DoorFlow API v3 endpoints
|
|
19
|
+
- Comprehensive documentation and examples
|
|
20
|
+
|
|
21
|
+
### Technical
|
|
22
|
+
- Requires Ruby 3.2 or higher
|
|
23
|
+
- Built with OpenAPI Generator 7.18.0
|
|
24
|
+
- Uses Faraday for HTTP requests
|
|
25
|
+
- User-Agent: `doorflow-ruby/1.0.0`
|
|
26
|
+
|
|
27
|
+
[1.0.0]: https://github.com/doorflow/doorflow-sdk-ruby/releases/tag/v1.0.0
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 DoorFlow
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
# DoorFlow Ruby Gem
|
|
2
|
+
|
|
3
|
+
The DoorFlow Ruby gem provides convenient access to the DoorFlow API from
|
|
4
|
+
applications written in Ruby.
|
|
5
|
+
|
|
6
|
+
## Documentation
|
|
7
|
+
|
|
8
|
+
See the [DoorFlow API docs](https://developer.doorflow.com/docs) for API documentation.
|
|
9
|
+
|
|
10
|
+
## Requirements
|
|
11
|
+
|
|
12
|
+
Ruby 3.2 or later.
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Install the gem with:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
gem install doorflow-api
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Or add to your Gemfile:
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
gem 'doorflow-api'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
The gem uses OAuth 2.0 for authentication. You'll need to [create an OAuth application](https://developer.doorflow.com/applications)
|
|
31
|
+
in your DoorFlow developer account to get client credentials.
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
require 'doorflow-api'
|
|
35
|
+
|
|
36
|
+
# Set up OAuth authentication
|
|
37
|
+
auth = DoorFlow::Auth::DoorFlowAuth.new(
|
|
38
|
+
client_id: ENV['DOORFLOW_CLIENT_ID'],
|
|
39
|
+
client_secret: ENV['DOORFLOW_CLIENT_SECRET'],
|
|
40
|
+
redirect_uri: 'http://localhost:3000/callback',
|
|
41
|
+
storage: DoorFlow::Auth::FileTokenStorage.new('./tokens.json')
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# First run: redirect user to authorize
|
|
45
|
+
unless auth.authenticated?
|
|
46
|
+
url, state = auth.authorization_url
|
|
47
|
+
# Store state in session, redirect user to url
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# After callback: exchange code for tokens
|
|
51
|
+
auth.handle_callback(code: params[:code], state: params[:state], expected_state: session[:state])
|
|
52
|
+
|
|
53
|
+
# Create a client with your auth object
|
|
54
|
+
client = DoorFlow::Client.new(auth: auth)
|
|
55
|
+
|
|
56
|
+
# Now you can use the API
|
|
57
|
+
people = client.people.list
|
|
58
|
+
puts people.data.map { |p| p.email }
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### People
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
# List all people
|
|
65
|
+
people = client.people.list
|
|
66
|
+
|
|
67
|
+
# With pagination
|
|
68
|
+
people = client.people.list(page: 1, per_page: 50)
|
|
69
|
+
|
|
70
|
+
# Auto-paginate through all results
|
|
71
|
+
client.people.list.auto_paging_each do |person|
|
|
72
|
+
puts person.email
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Find by email
|
|
76
|
+
people = client.people.list(email: 'alice@example.com')
|
|
77
|
+
|
|
78
|
+
# Get a specific person
|
|
79
|
+
person = client.people.retrieve(123)
|
|
80
|
+
|
|
81
|
+
# Create a person
|
|
82
|
+
person = client.people.create(
|
|
83
|
+
first_name: 'Alice',
|
|
84
|
+
last_name: 'Smith',
|
|
85
|
+
email: 'alice@example.com',
|
|
86
|
+
group_ids: [1, 2]
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# Update a person
|
|
90
|
+
person.update(department: 'Engineering')
|
|
91
|
+
|
|
92
|
+
# Delete a person
|
|
93
|
+
person.delete
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Channels (Doors)
|
|
97
|
+
|
|
98
|
+
```ruby
|
|
99
|
+
# List all channels
|
|
100
|
+
channels = client.channels.list
|
|
101
|
+
|
|
102
|
+
# Get a specific channel
|
|
103
|
+
channel = client.channels.retrieve(123)
|
|
104
|
+
|
|
105
|
+
# Unlock a door momentarily
|
|
106
|
+
channel.admit
|
|
107
|
+
|
|
108
|
+
# Unlock for a specific person
|
|
109
|
+
channel.admit_person(person_id)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Credentials
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
# List credential types available in your account
|
|
116
|
+
types = client.credential_types.list
|
|
117
|
+
|
|
118
|
+
# List all credentials
|
|
119
|
+
credentials = client.credentials.list
|
|
120
|
+
|
|
121
|
+
# Create a card credential
|
|
122
|
+
credential = client.credentials.create(
|
|
123
|
+
person_id: 123,
|
|
124
|
+
credential_type_id: 1,
|
|
125
|
+
value: '12345678'
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
# Delete a credential
|
|
129
|
+
credential.delete
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Groups
|
|
133
|
+
|
|
134
|
+
```ruby
|
|
135
|
+
# List all groups
|
|
136
|
+
groups = client.groups.list
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Events
|
|
140
|
+
|
|
141
|
+
```ruby
|
|
142
|
+
# List recent access events
|
|
143
|
+
events = client.events.list(start_date: (Time.now - 86400).iso8601)
|
|
144
|
+
|
|
145
|
+
# Auto-paginate through events
|
|
146
|
+
client.events.list.auto_paging_each do |event|
|
|
147
|
+
puts "#{event.person_name} at #{event.channel_name}"
|
|
148
|
+
end
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## OAuth Authorization Flow
|
|
152
|
+
|
|
153
|
+
The `DoorFlow::Auth::DoorFlowAuth` class handles the OAuth 2.0 authorization code flow:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
require 'doorflow-api'
|
|
157
|
+
|
|
158
|
+
# Create auth instance (typically in an initializer)
|
|
159
|
+
auth = DoorFlow::Auth::DoorFlowAuth.new(
|
|
160
|
+
client_id: ENV['DOORFLOW_CLIENT_ID'],
|
|
161
|
+
client_secret: ENV['DOORFLOW_CLIENT_SECRET'],
|
|
162
|
+
redirect_uri: 'http://localhost:3000/callback',
|
|
163
|
+
storage: DoorFlow::Auth::FileTokenStorage.new('./tokens.json')
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
# 1. Generate authorization URL and redirect user
|
|
167
|
+
get '/auth/doorflow' do
|
|
168
|
+
url, state = auth.authorization_url
|
|
169
|
+
session[:oauth_state] = state
|
|
170
|
+
redirect url
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# 2. Handle callback after user authorizes
|
|
174
|
+
get '/callback' do
|
|
175
|
+
auth.handle_callback(
|
|
176
|
+
code: params[:code],
|
|
177
|
+
state: params[:state],
|
|
178
|
+
expected_state: session[:oauth_state]
|
|
179
|
+
)
|
|
180
|
+
redirect '/dashboard'
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# 3. Use the API - tokens refresh automatically
|
|
184
|
+
get '/dashboard' do
|
|
185
|
+
client = DoorFlow::Client.new(auth: auth)
|
|
186
|
+
people = client.people.list
|
|
187
|
+
erb :dashboard, locals: { people: people }
|
|
188
|
+
end
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Configuration
|
|
192
|
+
|
|
193
|
+
| Option | Default | Description |
|
|
194
|
+
| ------ | ------- | ----------- |
|
|
195
|
+
| `client_id` | Required | Your DoorFlow OAuth client ID |
|
|
196
|
+
| `client_secret` | `nil` | Your OAuth client secret (optional for PKCE) |
|
|
197
|
+
| `redirect_uri` | Required | The redirect URI registered with your OAuth application |
|
|
198
|
+
| `storage` | Required | Token storage implementation |
|
|
199
|
+
| `scopes` | `['account.person', 'account.channel.readonly', 'account.event.access.readonly']` | OAuth scopes to request |
|
|
200
|
+
| `refresh_buffer_seconds` | `300` | Seconds before expiry to trigger automatic token refresh |
|
|
201
|
+
| `base_path` | `'https://api.doorflow.com'` | DoorFlow API base URL |
|
|
202
|
+
|
|
203
|
+
## Token Storage
|
|
204
|
+
|
|
205
|
+
The gem includes `FileTokenStorage` for simple file-based token persistence:
|
|
206
|
+
|
|
207
|
+
```ruby
|
|
208
|
+
storage = DoorFlow::Auth::FileTokenStorage.new('./tokens.json')
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
For production, subclass `DoorFlow::Auth::TokenStorage` to use your preferred storage:
|
|
212
|
+
|
|
213
|
+
```ruby
|
|
214
|
+
class DatabaseTokenStorage < DoorFlow::Auth::TokenStorage
|
|
215
|
+
def initialize(user)
|
|
216
|
+
@user = user
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def load
|
|
220
|
+
return nil unless @user.doorflow_tokens
|
|
221
|
+
to_stored_tokens(@user.doorflow_tokens)
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def save(tokens)
|
|
225
|
+
@user.update!(doorflow_tokens: tokens.to_h)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def clear
|
|
229
|
+
@user.update!(doorflow_tokens: nil)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## PKCE for Public Clients
|
|
235
|
+
|
|
236
|
+
For applications that cannot securely store a client secret:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
auth = DoorFlow::Auth::DoorFlowAuth.new(
|
|
240
|
+
client_id: ENV['DOORFLOW_CLIENT_ID'],
|
|
241
|
+
redirect_uri: 'http://localhost:3000/callback',
|
|
242
|
+
storage: storage
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# Generate authorization URL with PKCE
|
|
246
|
+
url, state, code_verifier = auth.authorization_url(use_pkce: true)
|
|
247
|
+
|
|
248
|
+
# Store both state and code_verifier
|
|
249
|
+
session[:oauth_state] = state
|
|
250
|
+
session[:oauth_verifier] = code_verifier
|
|
251
|
+
|
|
252
|
+
# In callback handler
|
|
253
|
+
auth.handle_callback(
|
|
254
|
+
code: params[:code],
|
|
255
|
+
state: params[:state],
|
|
256
|
+
expected_state: session[:oauth_state],
|
|
257
|
+
code_verifier: session[:oauth_verifier]
|
|
258
|
+
)
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Webhooks
|
|
262
|
+
|
|
263
|
+
DoorFlow sends webhooks to notify your application of events:
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
handler = DoorFlow::Webhooks.handler(secret: ENV['DOORFLOW_WEBHOOK_SECRET'])
|
|
267
|
+
|
|
268
|
+
handler.on('Event.CREATE') do |event|
|
|
269
|
+
puts "Access event: #{event.resource_id}"
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
handler.on('PersonCredential.UPDATE') do |event|
|
|
273
|
+
puts "Credential updated: #{event.resource_id}"
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
handler.on('*') do |event|
|
|
277
|
+
# Catch-all for any event type
|
|
278
|
+
AuditLog.create!(event_data: event.to_h)
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# In your Rails controller
|
|
282
|
+
class WebhooksController < ApplicationController
|
|
283
|
+
skip_before_action :verify_authenticity_token
|
|
284
|
+
|
|
285
|
+
def create
|
|
286
|
+
handler.handle(
|
|
287
|
+
payload: request.raw_post,
|
|
288
|
+
signature: request.headers['X-DoorFlow-Signature'],
|
|
289
|
+
timestamp: request.headers['X-DoorFlow-Timestamp']
|
|
290
|
+
)
|
|
291
|
+
head :ok
|
|
292
|
+
rescue DoorFlow::Webhooks::SignatureError
|
|
293
|
+
head :unauthorized
|
|
294
|
+
end
|
|
295
|
+
end
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### Event Patterns
|
|
299
|
+
|
|
300
|
+
| Pattern | Description |
|
|
301
|
+
|---------|-------------|
|
|
302
|
+
| `Event.CREATE` | New access event |
|
|
303
|
+
| `Event.UPDATE` | Access event updated |
|
|
304
|
+
| `Event.DELETE` | Access event deleted |
|
|
305
|
+
| `PersonCredential.CREATE` | New credential added |
|
|
306
|
+
| `PersonCredential.UPDATE` | Credential updated |
|
|
307
|
+
| `PersonCredential.DELETE` | Credential deleted |
|
|
308
|
+
| `Person.*` | Any person action |
|
|
309
|
+
| `*` | Catch-all |
|
|
310
|
+
|
|
311
|
+
## Error Handling
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
begin
|
|
315
|
+
person = client.people.retrieve(99999)
|
|
316
|
+
rescue DoorFlow::ApiError => e
|
|
317
|
+
puts "API Error: #{e.message}"
|
|
318
|
+
puts "Status: #{e.code}"
|
|
319
|
+
end
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
## Thread Safety & Multi-Tenant Support
|
|
323
|
+
|
|
324
|
+
The `DoorFlow::Client` class is designed for thread safety and multi-tenant applications. Each client instance maintains its own configuration and authentication state:
|
|
325
|
+
|
|
326
|
+
```ruby
|
|
327
|
+
# Each tenant gets their own client instance
|
|
328
|
+
def doorflow_client
|
|
329
|
+
@doorflow_client ||= DoorFlow::Client.new(auth: current_tenant_auth)
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# Or pass auth to each request
|
|
333
|
+
client = DoorFlow::Client.new(auth: current_user.doorflow_auth)
|
|
334
|
+
people = client.people.list
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
For simple single-tenant applications, you can also use a static access token:
|
|
338
|
+
|
|
339
|
+
```ruby
|
|
340
|
+
# Using a static token (no auto-refresh)
|
|
341
|
+
client = DoorFlow::Client.new(access_token: 'your_token')
|
|
342
|
+
|
|
343
|
+
# Or set globally (not recommended for multi-threaded apps)
|
|
344
|
+
DoorFlow.access_token = 'your_token'
|
|
345
|
+
client = DoorFlow::Client.new
|
|
346
|
+
people = client.people.list # Uses global token
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
## Documentation
|
|
350
|
+
|
|
351
|
+
- [DoorFlow API Documentation](https://developer.doorflow.com/api-ref)
|
|
352
|
+
- [DoorFlow Developer Portal](https://developer.doorflow.com)
|
|
353
|
+
|
|
354
|
+
## License
|
|
355
|
+
|
|
356
|
+
MIT License - see [LICENSE](LICENSE) for details.
|
data/bin/post-generate
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Post-generation script to add Stripe-like wrappers
|
|
3
|
+
|
|
4
|
+
puts "🔧 Running post-generation enhancements..."
|
|
5
|
+
|
|
6
|
+
# Ensure the wrapper files exist
|
|
7
|
+
wrapper_files = [
|
|
8
|
+
'lib/doorflow.rb',
|
|
9
|
+
'lib/doorflow/configuration.rb',
|
|
10
|
+
'lib/doorflow/client.rb',
|
|
11
|
+
'lib/doorflow/resource.rb',
|
|
12
|
+
'lib/doorflow/list_object.rb',
|
|
13
|
+
'lib/doorflow/resources/account.rb',
|
|
14
|
+
'lib/doorflow/resources/channel.rb',
|
|
15
|
+
'lib/doorflow/resources/credential.rb',
|
|
16
|
+
'lib/doorflow/resources/credential_type.rb',
|
|
17
|
+
'lib/doorflow/resources/event.rb',
|
|
18
|
+
'lib/doorflow/resources/group.rb',
|
|
19
|
+
'lib/doorflow/resources/group_reservation.rb',
|
|
20
|
+
'lib/doorflow/resources/notification_rule.rb',
|
|
21
|
+
'lib/doorflow/resources/person.rb',
|
|
22
|
+
'lib/doorflow/resources/reservation.rb',
|
|
23
|
+
'lib/doorflow/resources/role.rb',
|
|
24
|
+
'lib/doorflow/resources/site.rb'
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
missing_files = wrapper_files.reject { |f| File.exist?(f) }
|
|
28
|
+
|
|
29
|
+
if missing_files.any?
|
|
30
|
+
puts "⚠️ Warning: Missing wrapper files:"
|
|
31
|
+
missing_files.each { |f| puts " - #{f}" }
|
|
32
|
+
puts ""
|
|
33
|
+
puts "These files should be committed to the repository."
|
|
34
|
+
puts "They provide the Stripe-like API wrapper."
|
|
35
|
+
else
|
|
36
|
+
puts "✅ All wrapper files present"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Clean up generated comments if desired
|
|
40
|
+
# Uncomment to remove "Generated by OpenAPI Generator" comments
|
|
41
|
+
# Dir.glob('lib/**/*.rb').each do |file|
|
|
42
|
+
# content = File.read(file)
|
|
43
|
+
# cleaned = content.gsub(/^#.*Generated by.*\n/, '')
|
|
44
|
+
# File.write(file, cleaned) if content != cleaned
|
|
45
|
+
# end
|
|
46
|
+
|
|
47
|
+
puts "✅ Post-generation complete!"
|
data/doorflow.gemspec
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
=begin
|
|
4
|
+
#DoorFlow API
|
|
5
|
+
|
|
6
|
+
#Access control and door management API for DoorFlow
|
|
7
|
+
|
|
8
|
+
The version of the OpenAPI document: 3.0
|
|
9
|
+
|
|
10
|
+
Generated by: https://openapi-generator.tech
|
|
11
|
+
Generator version: 7.18.0-SNAPSHOT
|
|
12
|
+
|
|
13
|
+
=end
|
|
14
|
+
|
|
15
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
16
|
+
require "doorflow-api/version"
|
|
17
|
+
|
|
18
|
+
Gem::Specification.new do |s|
|
|
19
|
+
s.name = "doorflow"
|
|
20
|
+
s.version = DoorFlow::VERSION
|
|
21
|
+
s.platform = Gem::Platform::RUBY
|
|
22
|
+
s.authors = ["NetNodes Ltd"]
|
|
23
|
+
s.email = ["support@netnodes.net"]
|
|
24
|
+
s.homepage = "https://www.doorflow.com"
|
|
25
|
+
s.summary = "Official Ruby client for DoorFlow API v3"
|
|
26
|
+
s.description = "Ruby SDK for the DoorFlow Access Control API"
|
|
27
|
+
s.license = "MIT"
|
|
28
|
+
s.required_ruby_version = ">= 3.2"
|
|
29
|
+
s.metadata = {
|
|
30
|
+
"homepage_uri" => "https://www.doorflow.com",
|
|
31
|
+
"documentation_uri" => "https://www.doorflow.com/docs",
|
|
32
|
+
"changelog_uri" => "https://github.com/doorflow/doorflow-sdk-ruby/blob/main/CHANGELOG.md",
|
|
33
|
+
"source_code_uri" => "https://github.com/doorflow/doorflow-sdk-ruby",
|
|
34
|
+
"bug_tracker_uri" => "https://github.com/doorflow/doorflow-sdk-ruby/issues",
|
|
35
|
+
"rubygems_mfa_required" => "true"
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
s.add_runtime_dependency 'faraday', '>= 1.0.1', '< 3.0'
|
|
39
|
+
s.add_runtime_dependency 'faraday-multipart', '~> 1.0'
|
|
40
|
+
s.add_runtime_dependency 'marcel', '~> 1.0'
|
|
41
|
+
s.add_runtime_dependency 'typhoeus', '~> 1.4'
|
|
42
|
+
|
|
43
|
+
s.add_development_dependency 'rspec', '~> 3.6', '>= 3.6.0'
|
|
44
|
+
s.add_development_dependency 'simplecov', '~> 0.22'
|
|
45
|
+
s.add_development_dependency 'simplecov-cobertura', '~> 2.1'
|
|
46
|
+
s.add_development_dependency 'webmock', '~> 3.18'
|
|
47
|
+
s.add_development_dependency 'vcr', '~> 6.1'
|
|
48
|
+
s.add_development_dependency 'yard', '~> 0.9'
|
|
49
|
+
s.add_development_dependency 'rdoc', '~> 6.7'
|
|
50
|
+
|
|
51
|
+
s.files = Dir.glob("{lib,bin}/**/*") + %w[README.md LICENSE CHANGELOG.md doorflow.gemspec]
|
|
52
|
+
s.test_files = Dir.glob("spec/**/*")
|
|
53
|
+
s.executables = []
|
|
54
|
+
s.require_paths = ["lib"]
|
|
55
|
+
end
|