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.
@@ -0,0 +1,347 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Add lib to the load path if running directly
5
+ $LOAD_PATH.unshift File.expand_path('../lib', __dir__)
6
+
7
+ require 'anvil'
8
+ require 'anvil/env_loader'
9
+
10
+ # Load .env file if it exists
11
+ Anvil::EnvLoader.load(File.expand_path('../.env', __dir__))
12
+
13
+ # Example: Generate PDFs from HTML or Markdown
14
+ #
15
+ # This example shows how to generate PDFs from HTML/CSS or Markdown content
16
+
17
+ # Configure Anvil (will use ANVIL_API_KEY from .env)
18
+ Anvil.configure do |config|
19
+ config.api_key = ENV.fetch('ANVIL_API_KEY', nil)
20
+ config.environment = :development
21
+ end
22
+
23
+ # Example 1: Generate PDF from HTML
24
+ def generate_html_invoice
25
+ puts "\n๐Ÿ“„ Generating invoice from HTML..."
26
+
27
+ html = <<~HTML
28
+ <!DOCTYPE html>
29
+ <html>
30
+ <head>
31
+ <title>Invoice</title>
32
+ </head>
33
+ <body>
34
+ <div class="invoice-header">
35
+ <h1>INVOICE</h1>
36
+ <div class="invoice-number">#INV-2024-001</div>
37
+ <div class="invoice-date">Date: #{Date.today.strftime('%B %d, %Y')}</div>
38
+ </div>
39
+
40
+ <div class="company-info">
41
+ <h2>Acme Corporation</h2>
42
+ <p>123 Business Ave<br>San Francisco, CA 94102<br>Phone: (555) 123-4567</p>
43
+ </div>
44
+
45
+ <div class="bill-to">
46
+ <h3>Bill To:</h3>
47
+ <p>
48
+ John Doe<br>
49
+ XYZ Company<br>
50
+ 456 Client Street<br>
51
+ New York, NY 10001
52
+ </p>
53
+ </div>
54
+
55
+ <table class="invoice-items">
56
+ <thead>
57
+ <tr>
58
+ <th>Description</th>
59
+ <th>Quantity</th>
60
+ <th>Unit Price</th>
61
+ <th>Total</th>
62
+ </tr>
63
+ </thead>
64
+ <tbody>
65
+ <tr>
66
+ <td>Consulting Services</td>
67
+ <td>40</td>
68
+ <td>$150.00</td>
69
+ <td>$6,000.00</td>
70
+ </tr>
71
+ <tr>
72
+ <td>Software License</td>
73
+ <td>1</td>
74
+ <td>$2,500.00</td>
75
+ <td>$2,500.00</td>
76
+ </tr>
77
+ </tbody>
78
+ <tfoot>
79
+ <tr>
80
+ <td colspan="3">Subtotal</td>
81
+ <td>$8,500.00</td>
82
+ </tr>
83
+ <tr>
84
+ <td colspan="3">Tax (10%)</td>
85
+ <td>$850.00</td>
86
+ </tr>
87
+ <tr class="total">
88
+ <td colspan="3"><strong>Total</strong></td>
89
+ <td><strong>$9,350.00</strong></td>
90
+ </tr>
91
+ </tfoot>
92
+ </table>
93
+
94
+ <div class="footer">
95
+ <p>Payment due within 30 days. Thank you for your business!</p>
96
+ </div>
97
+ </body>
98
+ </html>
99
+ HTML
100
+
101
+ css = <<~CSS
102
+ body {
103
+ font-family: 'Helvetica Neue', Arial, sans-serif;
104
+ color: #333;
105
+ line-height: 1.6;
106
+ padding: 20px;
107
+ }
108
+
109
+ .invoice-header {
110
+ border-bottom: 2px solid #007bff;
111
+ padding-bottom: 20px;
112
+ margin-bottom: 30px;
113
+ }
114
+
115
+ h1 {
116
+ color: #007bff;
117
+ margin: 0;
118
+ font-size: 36px;
119
+ }
120
+
121
+ .invoice-number {
122
+ font-size: 18px;
123
+ color: #666;
124
+ margin-top: 10px;
125
+ }
126
+
127
+ .invoice-date {
128
+ font-size: 14px;
129
+ color: #666;
130
+ }
131
+
132
+ .company-info, .bill-to {
133
+ margin-bottom: 30px;
134
+ }
135
+
136
+ h2 {
137
+ color: #333;
138
+ font-size: 24px;
139
+ margin-bottom: 10px;
140
+ }
141
+
142
+ h3 {
143
+ color: #555;
144
+ font-size: 18px;
145
+ margin-bottom: 10px;
146
+ }
147
+
148
+ table {
149
+ width: 100%;
150
+ border-collapse: collapse;
151
+ margin: 30px 0;
152
+ }
153
+
154
+ th {
155
+ background-color: #007bff;
156
+ color: white;
157
+ padding: 12px;
158
+ text-align: left;
159
+ font-weight: bold;
160
+ }
161
+
162
+ td {
163
+ padding: 12px;
164
+ border-bottom: 1px solid #ddd;
165
+ }
166
+
167
+ tfoot td {
168
+ font-weight: bold;
169
+ border-top: 2px solid #007bff;
170
+ }
171
+
172
+ .total td {
173
+ font-size: 18px;
174
+ color: #007bff;
175
+ }
176
+
177
+ .footer {
178
+ margin-top: 40px;
179
+ padding-top: 20px;
180
+ border-top: 1px solid #ddd;
181
+ text-align: center;
182
+ color: #666;
183
+ font-size: 14px;
184
+ }
185
+ CSS
186
+
187
+ pdf = Anvil::PDF.generate_from_html(
188
+ html: html,
189
+ css: css,
190
+ title: 'Invoice #INV-2024-001'
191
+ )
192
+
193
+ filename = "invoice_#{Time.now.to_i}.pdf"
194
+ pdf.save_as(filename)
195
+
196
+ puts 'โœ… Invoice PDF generated!'
197
+ puts "๐Ÿ“„ Saved as: #{filename}"
198
+ puts "๐Ÿ“ Size: #{pdf.size_human}"
199
+ end
200
+
201
+ # Example 2: Generate PDF from Markdown
202
+ def generate_markdown_report
203
+ puts "\n๐Ÿ“„ Generating report from Markdown..."
204
+
205
+ markdown_content = [
206
+ {
207
+ heading: 'Annual Report 2024',
208
+ content: <<~MD
209
+ ## Executive Summary
210
+
211
+ This annual report provides a comprehensive overview of our company's performance
212
+ and achievements during the fiscal year 2024.
213
+
214
+ ### Key Highlights
215
+
216
+ - Revenue growth of **25%** year-over-year
217
+ - Expanded operations to **3 new markets**
218
+ - Launched **5 innovative products**
219
+ - Increased customer satisfaction to **95%**
220
+ MD
221
+ },
222
+ {
223
+ heading: 'Financial Performance',
224
+ content: <<~MD
225
+ ### Revenue Breakdown
226
+
227
+ | Quarter | Revenue | Growth |
228
+ |---------|------------|--------|
229
+ | Q1 2024 | $2.5M | +20% |
230
+ | Q2 2024 | $2.8M | +22% |
231
+ | Q3 2024 | $3.2M | +28% |
232
+ | Q4 2024 | $3.5M | +30% |
233
+ | **Total** | **$12M** | **+25%** |
234
+
235
+ ### Operating Expenses
236
+
237
+ Our operating expenses remained well-controlled throughout the year:
238
+
239
+ - Personnel: $4.2M
240
+ - Marketing: $1.8M
241
+ - R&D: $2.1M
242
+ - Operations: $1.4M
243
+ MD
244
+ },
245
+ {
246
+ heading: 'Future Outlook',
247
+ content: <<~MD
248
+ ## Strategic Initiatives for 2025
249
+
250
+ 1. **Digital Transformation**
251
+ - Implement AI-driven analytics
252
+ - Upgrade customer portal
253
+ - Automate key processes
254
+
255
+ 2. **Market Expansion**
256
+ - Enter European markets
257
+ - Strengthen presence in Asia
258
+ - Launch e-commerce platform
259
+
260
+ 3. **Product Innovation**
261
+ - Release next-gen product line
262
+ - Enhance mobile applications
263
+ - Develop SaaS offerings
264
+
265
+ ---
266
+
267
+ *This report was prepared by the Executive Team*
268
+ *Date: #{Date.today.strftime('%B %d, %Y')}*
269
+ MD
270
+ }
271
+ ]
272
+
273
+ pdf = Anvil::PDF.generate(
274
+ type: :markdown,
275
+ data: markdown_content,
276
+ title: 'Annual Report 2024',
277
+ page: {
278
+ margin_top: '2in',
279
+ margin_bottom: '1in',
280
+ margin_left: '1in',
281
+ margin_right: '1in'
282
+ }
283
+ )
284
+
285
+ filename = "report_#{Time.now.to_i}.pdf"
286
+ pdf.save_as(filename)
287
+
288
+ puts 'โœ… Report PDF generated!'
289
+ puts "๐Ÿ“„ Saved as: #{filename}"
290
+ puts "๐Ÿ“ Size: #{pdf.size_human}"
291
+ end
292
+
293
+ # Example 3: Simple markdown generation
294
+ def generate_simple_document
295
+ puts "\n๐Ÿ“„ Generating simple document..."
296
+
297
+ pdf = Anvil::PDF.generate_from_markdown(
298
+ <<~MD
299
+ # Welcome to Anvil
300
+
301
+ This is a simple example of generating a PDF from markdown.
302
+
303
+ ## Features
304
+
305
+ - Easy to use API
306
+ - Multiple format support
307
+ - Fast generation
308
+ - Professional output
309
+
310
+ ## Code Example
311
+
312
+ ```ruby
313
+ pdf = Anvil::PDF.generate_from_markdown(content)
314
+ pdf.save_as('output.pdf')
315
+ ```
316
+
317
+ Thank you for using Anvil!
318
+ MD
319
+ )
320
+
321
+ filename = "simple_#{Time.now.to_i}.pdf"
322
+ pdf.save_as(filename)
323
+
324
+ puts 'โœ… Simple PDF generated!'
325
+ puts "๐Ÿ“„ Saved as: #{filename}"
326
+ end
327
+
328
+ # Run examples
329
+ begin
330
+ puts '=' * 50
331
+ puts 'Anvil PDF Generation Examples'
332
+ puts '=' * 50
333
+
334
+ generate_html_invoice
335
+ generate_markdown_report
336
+ generate_simple_document
337
+
338
+ puts "\nโœ… All examples completed successfully!"
339
+ rescue Anvil::AuthenticationError => e
340
+ puts "โŒ Authentication failed: #{e.message}"
341
+ puts 'Please set your ANVIL_API_KEY environment variable'
342
+ rescue Anvil::Error => e
343
+ puts "โŒ Anvil error: #{e.message}"
344
+ rescue StandardError => e
345
+ puts "โŒ Unexpected error: #{e.message}"
346
+ puts e.backtrace.first(5)
347
+ end
@@ -0,0 +1,281 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'anvil'
6
+ require 'webrick'
7
+ require 'json'
8
+
9
+ # Example: Webhook verification and handling
10
+ #
11
+ # This example shows how to:
12
+ # 1. Set up a webhook endpoint
13
+ # 2. Verify webhook authenticity
14
+ # 3. Handle different webhook events
15
+ # 4. Process webhook data
16
+
17
+ # Configure Anvil with webhook token
18
+ Anvil.configure do |config|
19
+ config.api_key = ENV.fetch('ANVIL_API_KEY', nil)
20
+ config.webhook_token = ENV['ANVIL_WEBHOOK_TOKEN'] || 'your_webhook_token_here'
21
+ config.environment = :development
22
+ end
23
+
24
+ # Example webhook handler class
25
+ class WebhookHandler
26
+ def handle(webhook)
27
+ puts "\n๐Ÿ“จ Received webhook:"
28
+ puts " Action: #{webhook.action}"
29
+ puts " Timestamp: #{webhook.timestamp}"
30
+
31
+ # Handle different webhook types
32
+ case webhook.action
33
+ when 'signerComplete'
34
+ handle_signer_complete(webhook)
35
+ when 'signerUpdateStatus'
36
+ handle_signer_status_update(webhook)
37
+ when 'etchPacketComplete'
38
+ handle_packet_complete(webhook)
39
+ when 'weldCreate'
40
+ handle_workflow_created(webhook)
41
+ when 'weldComplete'
42
+ handle_workflow_complete(webhook)
43
+ when 'forgeComplete'
44
+ handle_webform_complete(webhook)
45
+ when 'documentGroupCreate'
46
+ handle_document_group_created(webhook)
47
+ when 'webhookTest'
48
+ handle_test_webhook(webhook)
49
+ else
50
+ puts " โš ๏ธ Unknown webhook action: #{webhook.action}"
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def handle_signer_complete(webhook)
57
+ puts 'โœ… Signer completed!'
58
+ puts " Signer: #{webhook.signer_name} (#{webhook.signer_email})"
59
+ puts " Signer ID: #{webhook.signer_eid}"
60
+ puts " Packet ID: #{webhook.packet_eid}"
61
+
62
+ # In a real app, you might:
63
+ # - Update database records
64
+ # - Send notification emails
65
+ # - Trigger next workflow step
66
+ end
67
+
68
+ def handle_signer_status_update(webhook)
69
+ puts '๐Ÿ“Š Signer status updated'
70
+ puts " Signer: #{webhook.signer_name}"
71
+ puts " New status: #{webhook.signer_status}"
72
+
73
+ case webhook.signer_status
74
+ when 'viewed'
75
+ puts ' ๐Ÿ‘€ Signer has viewed the document'
76
+ when 'signed'
77
+ puts ' โœ๏ธ Signer has signed'
78
+ end
79
+ end
80
+
81
+ def handle_packet_complete(webhook)
82
+ puts '๐ŸŽ‰ Signature packet complete!'
83
+ puts " Packet ID: #{webhook.packet_eid}"
84
+
85
+ # All signatures collected - download final documents
86
+ # packet = Anvil::Signature.find(webhook.packet_eid)
87
+ # download_signed_documents(packet)
88
+ end
89
+
90
+ def handle_workflow_created(webhook)
91
+ puts '๐Ÿ”ง Workflow created'
92
+ puts " Workflow ID: #{webhook.workflow_eid}"
93
+ end
94
+
95
+ def handle_workflow_complete(webhook)
96
+ puts 'โœ… Workflow complete!'
97
+ puts " Workflow ID: #{webhook.workflow_eid}"
98
+ end
99
+
100
+ def handle_webform_complete(webhook)
101
+ puts '๐Ÿ“ Webform completed'
102
+ puts " Webform ID: #{webhook.webform_eid}"
103
+ end
104
+
105
+ def handle_document_group_created(webhook)
106
+ puts '๐Ÿ“„ Document group created'
107
+ data = webhook.data
108
+ puts " Group ID: #{data[:documentGroupEid]}" if data[:documentGroupEid]
109
+ end
110
+
111
+ def handle_test_webhook(_webhook)
112
+ puts '๐Ÿงช Test webhook received!'
113
+ puts ' Your webhook endpoint is working correctly'
114
+ end
115
+ end
116
+
117
+ # Example: Simple webhook server for testing
118
+ class WebhookServer
119
+ def initialize(port: 4567)
120
+ @port = port
121
+ @handler = WebhookHandler.new
122
+ end
123
+
124
+ def start
125
+ server = WEBrick::HTTPServer.new(Port: @port)
126
+
127
+ # Mount webhook endpoint
128
+ server.mount_proc '/webhooks/anvil' do |req, res|
129
+ # Parse the webhook
130
+ webhook = Anvil::Webhook.new(
131
+ payload: req.body,
132
+ token: req.query['token'] || req.header['x-anvil-token']
133
+ )
134
+
135
+ # Verify the webhook
136
+ if webhook.valid?
137
+ puts 'โœ… Webhook verified successfully'
138
+
139
+ # Handle encrypted data if present
140
+ if webhook.encrypted?
141
+ puts '๐Ÿ” Webhook data is encrypted'
142
+ # To decrypt, you need your RSA private key:
143
+ # decrypted_data = webhook.decrypt('/path/to/private_key.pem')
144
+ end
145
+
146
+ # Process the webhook
147
+ @handler.handle(webhook)
148
+
149
+ # Return success response
150
+ res.status = 204 # No Content
151
+ else
152
+ puts 'โŒ Invalid webhook token!'
153
+ res.status = 401 # Unauthorized
154
+ res.body = 'Invalid token'
155
+ end
156
+ rescue Anvil::WebhookError => e
157
+ puts "โŒ Webhook error: #{e.message}"
158
+ res.status = 400
159
+ res.body = e.message
160
+ rescue StandardError => e
161
+ puts "โŒ Unexpected error: #{e.message}"
162
+ res.status = 500
163
+ res.body = 'Internal server error'
164
+ end
165
+
166
+ # Test endpoint
167
+ server.mount_proc '/test' do |_req, res|
168
+ res.body = 'Webhook server is running!'
169
+ end
170
+
171
+ puts "๐Ÿš€ Webhook server running on http://localhost:#{@port}"
172
+ puts "๐Ÿ“ Webhook endpoint: http://localhost:#{@port}/webhooks/anvil"
173
+ puts ' Configure this URL in your Anvil account settings'
174
+ puts "\nPress Ctrl+C to stop the server"
175
+
176
+ trap('INT') { server.shutdown }
177
+ server.start
178
+ end
179
+ end
180
+
181
+ # Example: Rails controller for webhooks
182
+ def rails_controller_example
183
+ puts <<~RUBY
184
+ # app/controllers/anvil_webhooks_controller.rb
185
+ class AnvilWebhooksController < ApplicationController
186
+ skip_before_action :verify_authenticity_token
187
+
188
+ def create
189
+ webhook = Anvil::Webhook.new(
190
+ payload: request.body.read,
191
+ token: params[:token]
192
+ )
193
+
194
+ if webhook.valid?
195
+ process_webhook(webhook)
196
+ head :no_content
197
+ else
198
+ Rails.logger.error "Invalid Anvil webhook token"
199
+ head :unauthorized
200
+ end
201
+ rescue Anvil::WebhookError => e
202
+ Rails.logger.error "Webhook error: \#{e.message}"
203
+ head :bad_request
204
+ end
205
+
206
+ private
207
+
208
+ def process_webhook(webhook)
209
+ # Process webhook based on action
210
+ case webhook.action
211
+ when 'signerComplete'
212
+ SignerCompleteJob.perform_later(webhook.data)
213
+ when 'etchPacketComplete'
214
+ PacketCompleteJob.perform_later(webhook.data)
215
+ # ... handle other webhook types
216
+ end
217
+ end
218
+ end
219
+
220
+ # config/routes.rb
221
+ post 'webhooks/anvil', to: 'anvil_webhooks#create'
222
+ RUBY
223
+ end
224
+
225
+ # Example: Create a test webhook
226
+ def create_test_webhook
227
+ puts "\n๐Ÿงช Creating test webhook..."
228
+
229
+ webhook = Anvil::Webhook.create_test(
230
+ action: 'signerComplete',
231
+ data: {
232
+ signerEid: 'test_signer_123',
233
+ packetEid: 'test_packet_456',
234
+ signerName: 'Test User',
235
+ signerEmail: 'test@example.com'
236
+ }
237
+ )
238
+
239
+ puts 'Test webhook created:'
240
+ puts " Action: #{webhook.action}"
241
+ puts " Valid: #{webhook.valid?}"
242
+
243
+ webhook
244
+ end
245
+
246
+ # Run the example
247
+ if __FILE__ == $PROGRAM_NAME
248
+ puts '=' * 50
249
+ puts 'Anvil Webhook Verification Example'
250
+ puts '=' * 50
251
+
252
+ choice = ARGV[0]
253
+
254
+ case choice
255
+ when 'server'
256
+ # Start webhook server
257
+ server = WebhookServer.new(port: 4567)
258
+ server.start
259
+
260
+ when 'test'
261
+ # Create and verify a test webhook
262
+ webhook = create_test_webhook
263
+ handler = WebhookHandler.new
264
+ handler.handle(webhook)
265
+
266
+ when 'rails'
267
+ # Show Rails controller example
268
+ puts "\n๐Ÿ“ฑ Rails Controller Example:"
269
+ puts '=' * 40
270
+ rails_controller_example
271
+
272
+ else
273
+ puts "\nUsage:"
274
+ puts " ruby #{__FILE__} server # Start webhook test server"
275
+ puts " ruby #{__FILE__} test # Create and handle test webhook"
276
+ puts " ruby #{__FILE__} rails # Show Rails controller example"
277
+ puts "\nEnvironment variables:"
278
+ puts ' ANVIL_API_KEY - Your Anvil API key'
279
+ puts ' ANVIL_WEBHOOK_TOKEN - Your webhook verification token'
280
+ end
281
+ end