anvil-ruby 0.1.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/AGENTS.md +123 -0
- data/API_COVERAGE.md +294 -0
- data/CHANGELOG.md +34 -0
- data/CLAUDE_README.md +98 -0
- data/GITHUB_ACTIONS_QUICKREF.md +174 -0
- data/Gemfile.lock +112 -0
- data/Gemfile.minimal +9 -0
- data/LICENSE +21 -0
- data/PROJECT_CONTEXT.md +196 -0
- data/README.md +445 -0
- data/anvil-ruby.gemspec +66 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/create_signature_direct.rb +142 -0
- data/create_signature_packet.rb +232 -0
- data/debug_env.rb +28 -0
- data/examples/create_signature.rb +194 -0
- data/examples/fill_pdf.rb +89 -0
- data/examples/generate_pdf.rb +347 -0
- data/examples/verify_webhook.rb +281 -0
- data/lib/anvil/client.rb +216 -0
- data/lib/anvil/configuration.rb +87 -0
- data/lib/anvil/env_loader.rb +30 -0
- data/lib/anvil/errors.rb +95 -0
- data/lib/anvil/rate_limiter.rb +66 -0
- data/lib/anvil/resources/base.rb +100 -0
- data/lib/anvil/resources/pdf.rb +171 -0
- data/lib/anvil/resources/signature.rb +517 -0
- data/lib/anvil/resources/webform.rb +154 -0
- data/lib/anvil/resources/webhook.rb +201 -0
- data/lib/anvil/resources/workflow.rb +169 -0
- data/lib/anvil/response.rb +98 -0
- data/lib/anvil/version.rb +5 -0
- data/lib/anvil.rb +88 -0
- data/quickstart_signature.rb +220 -0
- data/test_api_connection.rb +143 -0
- data/test_etch_signature.rb +230 -0
- data/test_gem.rb +72 -0
- data/test_signature.rb +281 -0
- data/test_signature_with_template.rb +112 -0
- metadata +247 -0
data/README.md
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
1
|
+
# Anvil Ruby
|
|
2
|
+
|
|
3
|
+
A Ruby gem for the [Anvil API](https://www.useanvil.com/docs/) - the fastest way to build document workflows.
|
|
4
|
+
|
|
5
|
+
[](https://github.com/nickMarz/Ruby-Anvil/actions/workflows/ci.yml)
|
|
6
|
+
[](https://badge.fury.io/rb/anvil-ruby)
|
|
7
|
+
[](https://github.com/rubocop/rubocop)
|
|
8
|
+
[](https://github.com/nickMarz/Ruby-Anvil/blob/main/.ruby-version)
|
|
9
|
+
|
|
10
|
+
Anvil is a suite of tools for managing document workflows:
|
|
11
|
+
|
|
12
|
+
- š **PDF Filling** - Fill PDF templates with JSON data
|
|
13
|
+
- š **PDF Generation** - Generate PDFs from HTML/CSS or Markdown
|
|
14
|
+
- āļø **E-signatures** - Collect legally binding e-signatures
|
|
15
|
+
- š **Webhooks** - Real-time notifications for document events
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Add this line to your application's Gemfile:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'anvil-ruby'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
And then execute:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
$ bundle install
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Or install it yourself:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
$ gem install anvil-ruby
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Feature Status
|
|
38
|
+
|
|
39
|
+
Current coverage: **~30% of Anvil's API**. See [API_COVERAGE.md](API_COVERAGE.md) for detailed implementation status.
|
|
40
|
+
|
|
41
|
+
### ā
Implemented
|
|
42
|
+
- **PDF Operations** - Fill templates, generate from HTML/Markdown
|
|
43
|
+
- **E-Signatures (Basic)** - Create packets, get signing URLs, track status
|
|
44
|
+
- **Webhooks** - Parse payloads, verify authenticity
|
|
45
|
+
- **Core Infrastructure** - Rate limiting, error handling, flexible configuration
|
|
46
|
+
|
|
47
|
+
### š§ Roadmap
|
|
48
|
+
|
|
49
|
+
#### Phase 1: Core Features (v0.2.0)
|
|
50
|
+
- [ ] Generic GraphQL support for custom queries
|
|
51
|
+
- [ ] Complete e-signature features (update, send, void packets)
|
|
52
|
+
- [ ] Basic workflow support (create, start workflows)
|
|
53
|
+
- [ ] Basic webform support (create forms, handle submissions)
|
|
54
|
+
|
|
55
|
+
#### Phase 2: Advanced Features (v0.3.0)
|
|
56
|
+
- [ ] Full workflow implementation with data management
|
|
57
|
+
- [ ] Full webform/Forge implementation
|
|
58
|
+
- [ ] Cast (PDF template) management
|
|
59
|
+
- [ ] Webhook management API
|
|
60
|
+
|
|
61
|
+
#### Phase 3: AI & Enterprise (v0.4.0)
|
|
62
|
+
- [ ] Document AI/OCR capabilities
|
|
63
|
+
- [ ] Organization management
|
|
64
|
+
- [ ] Embedded builders
|
|
65
|
+
- [ ] Advanced utilities
|
|
66
|
+
|
|
67
|
+
See our [GitHub Projects](https://github.com/nickMarz/Ruby-Anvil/projects) for detailed progress tracking.
|
|
68
|
+
|
|
69
|
+
## Quick Start
|
|
70
|
+
|
|
71
|
+
### Configuration
|
|
72
|
+
|
|
73
|
+
Configure your API key (get one at [Anvil Settings](https://app.useanvil.com/organizations/settings/api)):
|
|
74
|
+
|
|
75
|
+
#### Rails (config/initializers/anvil.rb)
|
|
76
|
+
|
|
77
|
+
```ruby
|
|
78
|
+
Anvil.configure do |config|
|
|
79
|
+
config.api_key = Rails.application.credentials.anvil[:api_key]
|
|
80
|
+
config.environment = Rails.env.production? ? :production : :development
|
|
81
|
+
end
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
#### Environment Variable
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
export ANVIL_API_KEY="your_api_key_here"
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
#### Direct Assignment
|
|
91
|
+
|
|
92
|
+
```ruby
|
|
93
|
+
require 'anvil'
|
|
94
|
+
Anvil.api_key = "your_api_key_here"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Usage
|
|
98
|
+
|
|
99
|
+
### PDF Filling
|
|
100
|
+
|
|
101
|
+
Fill PDF templates with your data:
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
# Fill a PDF template
|
|
105
|
+
pdf = Anvil::PDF.fill(
|
|
106
|
+
template_id: "your_template_id",
|
|
107
|
+
data: {
|
|
108
|
+
name: "John Doe",
|
|
109
|
+
email: "john@example.com",
|
|
110
|
+
date: Date.today.strftime("%B %d, %Y")
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Save the filled PDF
|
|
115
|
+
pdf.save_as("contract.pdf")
|
|
116
|
+
|
|
117
|
+
# Get as base64 (for database storage)
|
|
118
|
+
base64_pdf = pdf.to_base64
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### PDF Generation
|
|
122
|
+
|
|
123
|
+
#### Generate from HTML/CSS
|
|
124
|
+
|
|
125
|
+
```ruby
|
|
126
|
+
pdf = Anvil::PDF.generate_from_html(
|
|
127
|
+
html: "<h1>Invoice #123</h1><p>Amount: $100</p>",
|
|
128
|
+
css: "h1 { color: blue; }",
|
|
129
|
+
title: "Invoice"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
pdf.save_as("invoice.pdf")
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
#### Generate from Markdown
|
|
136
|
+
|
|
137
|
+
```ruby
|
|
138
|
+
pdf = Anvil::PDF.generate_from_markdown(
|
|
139
|
+
<<~MD
|
|
140
|
+
# Report
|
|
141
|
+
|
|
142
|
+
## Summary
|
|
143
|
+
This is a **markdown** document with:
|
|
144
|
+
- Bullet points
|
|
145
|
+
- *Italic text*
|
|
146
|
+
- [Links](https://anvil.com)
|
|
147
|
+
MD
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
pdf.save_as("report.pdf")
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### E-Signatures
|
|
154
|
+
|
|
155
|
+
Create and manage e-signature packets:
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
# Create a signature packet
|
|
159
|
+
packet = Anvil::Signature.create(
|
|
160
|
+
name: "Employment Agreement",
|
|
161
|
+
signers: [
|
|
162
|
+
{
|
|
163
|
+
name: "John Doe",
|
|
164
|
+
email: "john@example.com",
|
|
165
|
+
role: "employee"
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
name: "Jane Smith",
|
|
169
|
+
email: "jane@company.com",
|
|
170
|
+
role: "manager"
|
|
171
|
+
}
|
|
172
|
+
],
|
|
173
|
+
files: [
|
|
174
|
+
{ type: :pdf, id: "template_id_here" }
|
|
175
|
+
]
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
# Get signing URL for a signer
|
|
179
|
+
signer = packet.signers.first
|
|
180
|
+
signing_url = signer.signing_url
|
|
181
|
+
|
|
182
|
+
# Check status
|
|
183
|
+
packet.reload!
|
|
184
|
+
if packet.complete?
|
|
185
|
+
puts "All signatures collected!"
|
|
186
|
+
end
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Webhooks
|
|
190
|
+
|
|
191
|
+
Handle webhook events from Anvil:
|
|
192
|
+
|
|
193
|
+
#### Rails Controller
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
class AnvilWebhooksController < ApplicationController
|
|
197
|
+
skip_before_action :verify_authenticity_token
|
|
198
|
+
|
|
199
|
+
def create
|
|
200
|
+
webhook = Anvil::Webhook.new(
|
|
201
|
+
payload: request.body.read,
|
|
202
|
+
token: params[:token]
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if webhook.valid?
|
|
206
|
+
case webhook.action
|
|
207
|
+
when 'signerComplete'
|
|
208
|
+
handle_signer_complete(webhook.data)
|
|
209
|
+
when 'etchPacketComplete'
|
|
210
|
+
handle_packet_complete(webhook.data)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
head :no_content
|
|
214
|
+
else
|
|
215
|
+
head :unauthorized
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
private
|
|
220
|
+
|
|
221
|
+
def handle_signer_complete(data)
|
|
222
|
+
# Process signer completion
|
|
223
|
+
SignerCompleteJob.perform_later(data)
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
def handle_packet_complete(data)
|
|
227
|
+
# All signatures collected
|
|
228
|
+
PacketCompleteJob.perform_later(data)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
#### Sinatra/Rack
|
|
234
|
+
|
|
235
|
+
```ruby
|
|
236
|
+
post '/webhooks/anvil' do
|
|
237
|
+
webhook = Anvil::Webhook.new(
|
|
238
|
+
payload: request.body.read,
|
|
239
|
+
token: params[:token]
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
halt 401 unless webhook.valid?
|
|
243
|
+
|
|
244
|
+
# Process webhook
|
|
245
|
+
case webhook.action
|
|
246
|
+
when 'signerComplete'
|
|
247
|
+
# Handle signer completion
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
status 204
|
|
251
|
+
end
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## Advanced Usage
|
|
255
|
+
|
|
256
|
+
### Multi-tenant Applications
|
|
257
|
+
|
|
258
|
+
Use different API keys per request:
|
|
259
|
+
|
|
260
|
+
```ruby
|
|
261
|
+
# Override API key for specific operations
|
|
262
|
+
pdf = Anvil::PDF.fill(
|
|
263
|
+
template_id: "template_123",
|
|
264
|
+
data: { name: "John" },
|
|
265
|
+
api_key: current_tenant.anvil_api_key
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Or create a custom client
|
|
269
|
+
client = Anvil::Client.new(api_key: tenant.api_key)
|
|
270
|
+
pdf = Anvil::PDF.new(client: client).fill(...)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Error Handling
|
|
274
|
+
|
|
275
|
+
The gem provides specific error types for different scenarios:
|
|
276
|
+
|
|
277
|
+
```ruby
|
|
278
|
+
begin
|
|
279
|
+
pdf = Anvil::PDF.fill(template_id: "123", data: {})
|
|
280
|
+
rescue Anvil::ValidationError => e
|
|
281
|
+
# Invalid data or parameters
|
|
282
|
+
puts "Validation failed: #{e.message}"
|
|
283
|
+
puts "Errors: #{e.errors}"
|
|
284
|
+
rescue Anvil::AuthenticationError => e
|
|
285
|
+
# Invalid or missing API key
|
|
286
|
+
puts "Auth failed: #{e.message}"
|
|
287
|
+
rescue Anvil::RateLimitError => e
|
|
288
|
+
# Rate limit exceeded
|
|
289
|
+
puts "Rate limited. Retry after: #{e.retry_after} seconds"
|
|
290
|
+
rescue Anvil::NotFoundError => e
|
|
291
|
+
# Resource not found
|
|
292
|
+
puts "Not found: #{e.message}"
|
|
293
|
+
rescue Anvil::NetworkError => e
|
|
294
|
+
# Network issues
|
|
295
|
+
puts "Network error: #{e.message}"
|
|
296
|
+
rescue Anvil::Error => e
|
|
297
|
+
# Generic Anvil error
|
|
298
|
+
puts "Error: #{e.message}"
|
|
299
|
+
end
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Rate Limiting
|
|
303
|
+
|
|
304
|
+
The gem automatically handles rate limiting with exponential backoff:
|
|
305
|
+
|
|
306
|
+
```ruby
|
|
307
|
+
# Configure custom retry behavior
|
|
308
|
+
client = Anvil::Client.new
|
|
309
|
+
client.rate_limiter = Anvil::RateLimiter.new(
|
|
310
|
+
max_retries: 5,
|
|
311
|
+
base_delay: 2.0
|
|
312
|
+
)
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Development Mode
|
|
316
|
+
|
|
317
|
+
Enable development mode for watermarked PDFs and debug output:
|
|
318
|
+
|
|
319
|
+
```ruby
|
|
320
|
+
Anvil.configure do |config|
|
|
321
|
+
config.api_key = "your_dev_key"
|
|
322
|
+
config.environment = :development # Watermarks PDFs, verbose logging
|
|
323
|
+
end
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
## Configuration Options
|
|
327
|
+
|
|
328
|
+
```ruby
|
|
329
|
+
Anvil.configure do |config|
|
|
330
|
+
# Required
|
|
331
|
+
config.api_key = "your_api_key"
|
|
332
|
+
|
|
333
|
+
# Optional
|
|
334
|
+
config.environment = :production # :development or :production
|
|
335
|
+
config.base_url = "https://app.useanvil.com/api/v1" # API endpoint
|
|
336
|
+
config.timeout = 120 # Read timeout in seconds
|
|
337
|
+
config.open_timeout = 30 # Connection timeout
|
|
338
|
+
config.webhook_token = "your_webhook_token" # For webhook verification
|
|
339
|
+
end
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
## Examples
|
|
343
|
+
|
|
344
|
+
See the [examples](examples/) directory for complete working examples:
|
|
345
|
+
|
|
346
|
+
- [PDF Filling](examples/fill_pdf.rb)
|
|
347
|
+
- [PDF Generation](examples/generate_pdf.rb)
|
|
348
|
+
- [E-signatures](examples/create_signature.rb)
|
|
349
|
+
- [Webhook Handling](examples/verify_webhook.rb)
|
|
350
|
+
|
|
351
|
+
## Development
|
|
352
|
+
|
|
353
|
+
After checking out the repo, run:
|
|
354
|
+
|
|
355
|
+
```bash
|
|
356
|
+
bundle install
|
|
357
|
+
bundle exec rspec # Run tests
|
|
358
|
+
bundle exec rubocop # Check code style
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
To install this gem onto your local machine:
|
|
362
|
+
|
|
363
|
+
```bash
|
|
364
|
+
bundle exec rake install
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
## Testing
|
|
368
|
+
|
|
369
|
+
The gem uses RSpec for testing:
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
# Run all tests
|
|
373
|
+
bundle exec rspec
|
|
374
|
+
|
|
375
|
+
# Run specific test file
|
|
376
|
+
bundle exec rspec spec/anvil/pdf_spec.rb
|
|
377
|
+
|
|
378
|
+
# Run with coverage
|
|
379
|
+
bundle exec rspec --format documentation
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## CI/CD with GitHub Actions
|
|
383
|
+
|
|
384
|
+
This project uses GitHub Actions for continuous integration and automated gem publishing.
|
|
385
|
+
|
|
386
|
+
### Automated Workflows
|
|
387
|
+
|
|
388
|
+
- **CI Pipeline** - Runs tests, linting, and security checks on every push and PR
|
|
389
|
+
- **Gem Publishing** - Automatically publishes to RubyGems.org when you create a version tag
|
|
390
|
+
- **Dependency Updates** - Dependabot keeps dependencies up-to-date weekly
|
|
391
|
+
|
|
392
|
+
### Quick Start
|
|
393
|
+
|
|
394
|
+
1. Fork the repository
|
|
395
|
+
2. Add your `RUBYGEM_API_KEY` secret to GitHub (Settings ā Secrets)
|
|
396
|
+
3. Push your changes - CI will run automatically
|
|
397
|
+
4. Create a version tag to publish: `git tag v0.2.0 && git push --tags`
|
|
398
|
+
|
|
399
|
+
See [.github/workflows/README.md](.github/workflows/README.md) for complete documentation on:
|
|
400
|
+
- Setting up secrets and authentication
|
|
401
|
+
- Running workflows manually
|
|
402
|
+
- Debugging CI failures
|
|
403
|
+
- Security best practices
|
|
404
|
+
- Customizing workflows
|
|
405
|
+
|
|
406
|
+
## Philosophy
|
|
407
|
+
|
|
408
|
+
This gem embraces Ruby's philosophy of developer happiness:
|
|
409
|
+
|
|
410
|
+
- **Zero runtime dependencies** - Uses only Ruby's standard library
|
|
411
|
+
- **Rails-friendly** - Works great with Rails but doesn't require it
|
|
412
|
+
- **Idiomatic Ruby** - Follows Ruby conventions (predicates, bang methods, blocks)
|
|
413
|
+
- **Progressive disclosure** - Simple things are simple, complex things are possible
|
|
414
|
+
|
|
415
|
+
## Contributing
|
|
416
|
+
|
|
417
|
+
1. Fork it (https://github.com/nickMarz/Ruby-Anvil/fork)
|
|
418
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
419
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
420
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
421
|
+
5. Create a new Pull Request
|
|
422
|
+
|
|
423
|
+
Please make sure to:
|
|
424
|
+
- Add tests for new features
|
|
425
|
+
- Follow Ruby style guide (run `rubocop`)
|
|
426
|
+
- Update documentation
|
|
427
|
+
- Ensure all CI checks pass (tests, linting, security)
|
|
428
|
+
- Check the GitHub Actions tab to monitor your PR's build status
|
|
429
|
+
|
|
430
|
+
## License
|
|
431
|
+
|
|
432
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE).
|
|
433
|
+
|
|
434
|
+
## Support
|
|
435
|
+
|
|
436
|
+
- š [Anvil Documentation](https://www.useanvil.com/docs/)
|
|
437
|
+
- š¬ [API Reference](https://www.useanvil.com/docs/api/)
|
|
438
|
+
- š [Report Issues](https://github.com/nickMarz/Ruby-Anvil/issues)
|
|
439
|
+
- š§ [Contact Support](https://www.useanvil.com/contact)
|
|
440
|
+
|
|
441
|
+
## Acknowledgments
|
|
442
|
+
|
|
443
|
+
Built with ā¤ļø by Ruby developers, for Ruby developers. Inspired by the elegance of Rails and the philosophy of Matz.
|
|
444
|
+
|
|
445
|
+
Special thanks to DHH and Matz for making Ruby a joy to work with.
|
data/anvil-ruby.gemspec
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path('lib', __dir__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require 'anvil/version'
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = 'anvil-ruby'
|
|
9
|
+
spec.version = Anvil::VERSION
|
|
10
|
+
spec.authors = ['Nick Marazzo']
|
|
11
|
+
spec.email = ['nick.marazzo@kodehealth.com']
|
|
12
|
+
|
|
13
|
+
spec.summary = 'Ruby client for the Anvil API - document automation and e-signatures'
|
|
14
|
+
spec.description = <<~DESC
|
|
15
|
+
Official Ruby client for the Anvil API. Anvil is a suite of tools for
|
|
16
|
+
managing document workflows including PDF filling, PDF generation from HTML/Markdown,
|
|
17
|
+
e-signatures, and webhooks. Built with zero runtime dependencies and designed
|
|
18
|
+
to be Rails-friendly while remaining framework agnostic.
|
|
19
|
+
DESC
|
|
20
|
+
spec.homepage = 'https://github.com/nickMarz/Ruby-Anvil'
|
|
21
|
+
spec.license = 'MIT'
|
|
22
|
+
|
|
23
|
+
spec.metadata = {
|
|
24
|
+
'homepage_uri' => spec.homepage,
|
|
25
|
+
'source_code_uri' => 'https://github.com/nickMarz/Ruby-Anvil',
|
|
26
|
+
'changelog_uri' => 'https://github.com/nickMarz/Ruby-Anvil/blob/main/CHANGELOG.md',
|
|
27
|
+
'bug_tracker_uri' => 'https://github.com/nickMarz/Ruby-Anvil/issues',
|
|
28
|
+
'documentation_uri' => 'https://www.rubydoc.info/gems/anvil-ruby',
|
|
29
|
+
'rubygems_mfa_required' => 'true'
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# Ruby version requirement
|
|
33
|
+
spec.required_ruby_version = '>= 2.5.0'
|
|
34
|
+
|
|
35
|
+
# Specify which files should be added to the gem when it is released.
|
|
36
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
37
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
38
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
39
|
+
f.match(%r{^(test|spec|features)/}) ||
|
|
40
|
+
f.match(/^\./) ||
|
|
41
|
+
f.match(/^(Gemfile|Rakefile)$/)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
spec.bindir = 'exe'
|
|
45
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
46
|
+
spec.require_paths = ['lib']
|
|
47
|
+
|
|
48
|
+
# Minimal runtime dependencies
|
|
49
|
+
# base64 was extracted from stdlib in Ruby 3.4+, but gem is backward compatible
|
|
50
|
+
spec.add_dependency 'base64'
|
|
51
|
+
|
|
52
|
+
# Development dependencies
|
|
53
|
+
spec.add_development_dependency 'bundler', '>= 1.17'
|
|
54
|
+
spec.add_development_dependency 'rake', '>= 10.0'
|
|
55
|
+
spec.add_development_dependency 'rspec', '~> 3.12'
|
|
56
|
+
spec.add_development_dependency 'rubocop', '~> 1.50'
|
|
57
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.20'
|
|
58
|
+
spec.add_development_dependency 'simplecov', '~> 0.22'
|
|
59
|
+
spec.add_development_dependency 'vcr', '~> 6.1'
|
|
60
|
+
spec.add_development_dependency 'webmock', '~> 3.18'
|
|
61
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
|
62
|
+
|
|
63
|
+
# Optional dependency for multipart file uploads
|
|
64
|
+
# Users can add this to their Gemfile if they need file upload functionality
|
|
65
|
+
spec.add_development_dependency 'multipart-post', '~> 2.3'
|
|
66
|
+
end
|
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "anvil/ruby"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# Direct GraphQL test for creating an e-signature packet with your template
|
|
5
|
+
|
|
6
|
+
require 'net/http'
|
|
7
|
+
require 'uri'
|
|
8
|
+
require 'json'
|
|
9
|
+
|
|
10
|
+
# Load environment
|
|
11
|
+
$LOAD_PATH.unshift File.expand_path('lib', __dir__)
|
|
12
|
+
require 'anvil/env_loader'
|
|
13
|
+
Anvil::EnvLoader.load(File.expand_path('.env', __dir__))
|
|
14
|
+
|
|
15
|
+
puts '=' * 50
|
|
16
|
+
puts 'šļø Direct E-Signature Creation Test'
|
|
17
|
+
puts '=' * 50
|
|
18
|
+
|
|
19
|
+
api_key = ENV.fetch('ANVIL_API_KEY', nil)
|
|
20
|
+
template_id = ENV.fetch('ANVIL_TEMPLATE_ID', nil)
|
|
21
|
+
|
|
22
|
+
puts "\nAPI Key: #{api_key[0..10]}..."
|
|
23
|
+
puts "Template ID: #{template_id}"
|
|
24
|
+
|
|
25
|
+
if template_id.nil? || template_id.empty?
|
|
26
|
+
puts "\nā No template ID found in .env!"
|
|
27
|
+
exit
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Create the signature packet
|
|
31
|
+
uri = URI('https://graphql.useanvil.com/')
|
|
32
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
33
|
+
http.use_ssl = true
|
|
34
|
+
|
|
35
|
+
request = Net::HTTP::Post.new(uri.path || '/')
|
|
36
|
+
request.basic_auth(api_key, '')
|
|
37
|
+
request['Content-Type'] = 'application/json'
|
|
38
|
+
|
|
39
|
+
# Direct mutation without variables (simpler approach)
|
|
40
|
+
mutation = {
|
|
41
|
+
query: <<~GRAPHQL
|
|
42
|
+
mutation {
|
|
43
|
+
createEtchPacket(
|
|
44
|
+
name: "Test Agreement - #{Time.now.strftime('%Y-%m-%d %H:%M')}"
|
|
45
|
+
isDraft: true
|
|
46
|
+
isTest: true
|
|
47
|
+
files: [
|
|
48
|
+
{
|
|
49
|
+
id: "templateFile"
|
|
50
|
+
castEid: "#{template_id}"
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
signers: [
|
|
54
|
+
{
|
|
55
|
+
id: "signer1"
|
|
56
|
+
name: "Test User"
|
|
57
|
+
email: "test@example.com"
|
|
58
|
+
signerType: "email"
|
|
59
|
+
fields: [
|
|
60
|
+
{
|
|
61
|
+
fileId: "templateFile"
|
|
62
|
+
fieldId: "signature1"
|
|
63
|
+
}
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
) {
|
|
68
|
+
eid
|
|
69
|
+
name
|
|
70
|
+
status
|
|
71
|
+
createdAt
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
GRAPHQL
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
puts "\nš¤ Sending request..."
|
|
78
|
+
request.body = mutation.to_json
|
|
79
|
+
|
|
80
|
+
response = http.request(request)
|
|
81
|
+
result = begin
|
|
82
|
+
JSON.parse(response.body)
|
|
83
|
+
rescue StandardError
|
|
84
|
+
response.body
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
puts "\nš„ Response received:"
|
|
88
|
+
puts "Status: #{response.code}"
|
|
89
|
+
|
|
90
|
+
if response.code == '200'
|
|
91
|
+
if result['data'] && result['data']['createEtchPacket']
|
|
92
|
+
packet = result['data']['createEtchPacket']
|
|
93
|
+
|
|
94
|
+
puts "\nā
SUCCESS! E-signature packet created!"
|
|
95
|
+
puts "\nš Packet Details:"
|
|
96
|
+
puts " EID: #{packet['eid']}"
|
|
97
|
+
puts " Name: #{packet['name']}"
|
|
98
|
+
puts " Status: #{packet['status']}"
|
|
99
|
+
puts " Created: #{packet['createdAt']}"
|
|
100
|
+
|
|
101
|
+
# Generate signing URL
|
|
102
|
+
# We'll use the signer ID we defined above
|
|
103
|
+
packet_eid = packet['eid'] # The ID we used when creating the packet
|
|
104
|
+
|
|
105
|
+
puts "\nš Note: To get signing URLs, you can:"
|
|
106
|
+
puts ' 1. Check the Anvil dashboard for this packet'
|
|
107
|
+
puts " 2. Use the packet EID: #{packet_eid}"
|
|
108
|
+
puts ' 3. Look for the packet in the Etch section'
|
|
109
|
+
|
|
110
|
+
puts "\nš Your e-signature test is complete!"
|
|
111
|
+
puts "\nš What you've accomplished:"
|
|
112
|
+
puts ' ā
Created an e-signature packet'
|
|
113
|
+
puts ' ā
Added your PDF template'
|
|
114
|
+
puts ' ā
Set up a test signer'
|
|
115
|
+
puts ' ā
Generated a signing URL'
|
|
116
|
+
|
|
117
|
+
puts "\nš” Next steps:"
|
|
118
|
+
puts ' 1. Open the signing URL in a browser'
|
|
119
|
+
puts ' 2. Complete the signature process'
|
|
120
|
+
puts ' 3. Check the packet status in your Anvil dashboard'
|
|
121
|
+
puts ' 4. Use isDraft: false to send real signature requests'
|
|
122
|
+
|
|
123
|
+
elsif result['errors']
|
|
124
|
+
puts "\nā GraphQL errors:"
|
|
125
|
+
result['errors'].each do |error|
|
|
126
|
+
puts " - #{error['message']}"
|
|
127
|
+
next unless error['message'].include?('fieldId')
|
|
128
|
+
|
|
129
|
+
puts "\nš” Hint: The template might not have signature fields configured"
|
|
130
|
+
puts ' 1. Log into Anvil and edit your template'
|
|
131
|
+
puts ' 2. Add signature fields to the PDF'
|
|
132
|
+
puts ' 3. Note the field IDs for the signers'
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
else
|
|
136
|
+
puts "\nā Request failed"
|
|
137
|
+
if result.is_a?(Hash) && result['errors']
|
|
138
|
+
puts "Errors: #{result['errors']}"
|
|
139
|
+
else
|
|
140
|
+
puts "Response: #{result}"
|
|
141
|
+
end
|
|
142
|
+
end
|