adobe_doc_api 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa70ac6119400a1d4e5f9f945b58d2c087221a6f94ce31f9a7e073450e021118
4
- data.tar.gz: 1941e4e7f1c4ec3b57a504bb92be4c3faaa3d7c4f4148987458e422822ff69a5
3
+ metadata.gz: 5d1e60f6687db22347aa8a8d772e066716ff6b9c9d5d9065ab0395543ab8808e
4
+ data.tar.gz: 40f43095bb7fb02dd9a8724941c823c3870965a40b313f9e580e9d919dc13def
5
5
  SHA512:
6
- metadata.gz: 381363a1c7b89c72d42f7c3847b0857489b23428b768451f507259e49c2ad6bb0399a12e10e435a19f5c029347332ca21ccd7ca5ebc37741ecb32dde06dea704
7
- data.tar.gz: 554a83e571dbe3d4844d250694c07cf8bfa0d545e290ed8e465c56c0f0a3df198ad94f1a6900a9fae5e43b681c344efb04e1ebd82cee168107234b1ca06003b3
6
+ metadata.gz: d47936fe2d22f56b0268c1e60019847a45b63ff35c5dac178e43fe5e597e68ccb9c3be16c293101410ccdc94dc9b4522c891852792d84a42bcd67c3b32a44288
7
+ data.tar.gz: 2c479317760c7ae45aa6dfd31ac6606cf71e7db98107cd9e1bbd308e19a017816aed99441420492ff400959f24fc4386285b26834c481872cc285f11f3f39b18
data/README.md CHANGED
@@ -18,7 +18,8 @@ Or install it yourself as:
18
18
 
19
19
  $ gem install adobe_doc_api
20
20
 
21
- ## Required ENV variables
21
+ ## Recommended ENV variables
22
+ *Client.new will fallback to use these variables if they are not passed in setup
22
23
  ```ruby
23
24
  ENV['adobe_org_id']
24
25
  ENV['adobe_tech_account_id']
@@ -30,22 +31,28 @@ ENV['adobe_client_secret']
30
31
 
31
32
  ```ruby
32
33
  key_path = "../full_path_to/private.key"
33
- doc_path = "../full_path_to/disclosure.docx"
34
- destination = "../full_path_to_output/output.docx"
34
+ template_path = "../full_path_to/disclosure.docx"
35
+ output_path = "../full_path_to_output/output.docx"
35
36
  json_data = { 'DocTag': 'Value', 'DocTag2': 'Value2'}
36
- client = AdobeDocApi::Client.new(private_key_path: key_path, destination_path: destination)
37
- client.submit(json: json_data, disclosure_file_path: doc_path)
38
- ```
39
37
 
40
- ## Development
38
+ client = AdobeDocApi::Client.new(private_key: key_path)
39
+ # Without ENV variables set
40
+ # client = AdobeDocApi::Client.new(private_key: key_path, client_id: ENV['adobe_client_id'], client_secret: ENV['adobe_client_secret']org_id: ENV['adobe_org_id'], tech_account_id: ENV['adobe_tech_account_id'], access_token: nil)
41
41
 
42
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
42
+ client.submit(json: json_data, template: template_path, output: output_path)
43
+ ```
43
44
 
44
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
45
+ ## Todo
46
+ - [x] Add multipart parsing to improve saving the file from the response
47
+ - [ ] Add documentation
45
48
 
46
49
  ## Contributing
47
50
 
48
- Bug reports and pull requests are welcome on GitHub at https://github.com/c-sonnier/adobe_doc_api.
51
+ 1. Fork it
52
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
53
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
54
+ 4. Push to the branch (`git push origin my-new-feature`)
55
+ 5. Create new Pull Request
49
56
 
50
57
  ## License
51
58
 
@@ -1,116 +1,139 @@
1
- require 'faraday'
2
- require 'faraday_middleware'
3
- require 'jwt'
4
- require 'openssl'
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+ require "jwt"
4
+ require "openssl"
5
5
 
6
6
  module AdobeDocApi
7
7
  class Client
8
- JWT_URL = 'https://ims-na1.adobelogin.com/ims/exchange/jwt/'.freeze
9
- API_ENDPOINT_URL = 'https://cpf-ue1.adobe.io'.freeze
10
- attr_reader :access_token, :output_ext, :output_format, :poll_url, :content_request
8
+ JWT_URL = "https://ims-na1.adobelogin.com/ims/exchange/jwt/".freeze
9
+ API_ENDPOINT_URL = "https://cpf-ue1.adobe.io".freeze
11
10
 
12
- def initialize(private_key_path:, destination_path:)
13
- @destination_path = destination_path
14
- @output_ext = File.extname(destination_path)
15
- @output_format = @output_ext =~ /docx/ ? 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' : 'application/pdf'
11
+ attr_reader :access_token, :location_url, :raw_response, :client_id, :client_secret, :org_id, :tech_account_id
16
12
 
13
+ def initialize(private_key:, client_id: ENV["adobe_client_id"], client_secret: ENV["adobe_client_secret"], org_id: ENV["adobe_org_id"], tech_account_id: ENV["adobe_tech_account_id"], access_token: nil)
14
+ # TODO Need to validate if any params are missing and return error
15
+ @client_id = client_id
16
+ @client_secret = client_secret
17
+ @org_id = org_id
18
+ @tech_account_id = tech_account_id
19
+ @location_url = nil
20
+ @output_file_path = nil
21
+ @raw_response = nil
22
+ @access_token = access_token || get_access_token(private_key)
23
+ end
24
+
25
+ def get_access_token(private_key)
17
26
  jwt_payload = {
18
- 'iss' => ENV['adobe_org_id'],
19
- 'sub' => ENV['adobe_tech_account_id'],
20
- 'https://ims-na1.adobelogin.com/s/ent_documentcloud_sdk' => true,
21
- 'aud' => "https://ims-na1.adobelogin.com/c/#{ENV['adobe_client_id']}",
22
- 'exp' => (Time.now.utc + 500).to_i
27
+ "iss" => @org_id,
28
+ "sub" => @tech_account_id,
29
+ "https://ims-na1.adobelogin.com/s/ent_documentcloud_sdk" => true,
30
+ "aud" => "https://ims-na1.adobelogin.com/c/#{@client_id}",
31
+ "exp" => (Time.now.utc + 60).to_i
23
32
  }
24
33
 
25
- rsa_private = OpenSSL::PKey::RSA.new File.read(private_key_path)
26
- jwt_token = JWT.encode jwt_payload, rsa_private, 'RS256'
34
+ rsa_private = OpenSSL::PKey::RSA.new File.read(private_key)
35
+
36
+ jwt_token = JWT.encode jwt_payload, rsa_private, "RS256"
27
37
 
28
38
  connection = Faraday.new do |conn|
29
- conn.response :json, content_type: 'application/json'
39
+ conn.response :json, content_type: "application/json"
30
40
  end
31
-
32
41
  response = connection.post JWT_URL do |req|
33
- req.params['client_id'] = ENV['adobe_client_id']
34
- req.params['client_secret'] = ENV['adobe_client_secret']
35
- req.params['jwt_token'] = jwt_token
42
+ req.params["client_id"] = @client_id
43
+ req.params["client_secret"] = @client_secret
44
+ req.params["jwt_token"] = jwt_token
36
45
  end
37
46
 
38
- @access_token = response.body['access_token']
47
+ return response.body["access_token"]
39
48
 
40
49
  end
41
50
 
42
- def submit(json:, disclosure_file_path:)
43
- @content_request = {
51
+ def submit(json:, template:, output:)
52
+ @output = output
53
+ output_format = /docx/.match?(File.extname(@output)) ? "application/vnd.openxmlformats-officedocument.wordprocessingml.document" : "application/pdf"
54
+
55
+ content_request = {
44
56
  "cpf:engine": {
45
- "repo:assetId": 'urn:aaid:cpf:Service-52d5db6097ed436ebb96f13a4c7bf8fb'
57
+ "repo:assetId": "urn:aaid:cpf:Service-52d5db6097ed436ebb96f13a4c7bf8fb"
46
58
  },
47
59
  "cpf:inputs": {
48
- "documentIn": {
49
- "dc:format": 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
50
- "cpf:location": 'InputFile0'
60
+ documentIn: {
61
+ "dc:format": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
62
+ "cpf:location": "InputFile0"
51
63
  },
52
- "params": {
64
+ params: {
53
65
  "cpf:inline": {
54
- "outputFormat": @output_ext.delete('.'),
55
- "jsonDataForMerge": json
66
+ outputFormat: File.extname(@output).delete("."),
67
+ jsonDataForMerge: json
56
68
  }
57
69
  }
58
70
  },
59
71
  "cpf:outputs": {
60
- "documentOut": {
61
- "dc:format": @output_format.to_s,
62
- "cpf:location": 'multipartLabel'
72
+ documentOut: {
73
+ "dc:format": output_format.to_s,
74
+ "cpf:location": "multipartLabel"
63
75
  }
64
76
  }
65
77
  }.to_json
66
78
 
67
79
  connection = Faraday.new API_ENDPOINT_URL do |conn|
68
- conn.request :authorization, 'Bearer', @access_token
69
- conn.headers['x-api-key'] = ENV['adobe_client_id']
80
+ conn.request :authorization, "Bearer", @access_token
81
+ conn.headers["x-api-key"] = @client_id
70
82
  conn.request :multipart
71
83
  conn.request :url_encoded
72
- conn.response :json, content_type: 'application/json'
84
+ conn.response :json, content_type: "application/json"
73
85
  end
74
86
 
75
- payload = {'contentAnalyzerRequests' => content_request}
76
- payload[:InputFile0] = Faraday::FilePart.new(disclosure_file_path, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document')
77
- res = connection.post('/ops/:create', payload)
78
- @poll_url = res.headers['location']
79
- poll_for_file(@poll_url)
87
+ payload = {"contentAnalyzerRequests" => content_request}
88
+ payload[:InputFile0] = Faraday::FilePart.new(template, "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
89
+ res = connection.post("/ops/:create", payload)
90
+ status_code = res.body["cpf:status"]["status"].to_i
91
+ @location_url = res.headers["location"]
92
+ raise Error.new(status_code: status_code, msg: res.body["cpf:status"]) unless status_code == 202
93
+ poll_for_file(@location_url)
80
94
  end
81
95
 
96
+ private
97
+
82
98
  def poll_for_file(url)
83
- poll = Faraday.new do |conn|
84
- conn.request :authorization, 'Bearer', @access_token
85
- conn.headers['x-api-key'] = ENV['adobe_client_id']
99
+ connection = Faraday.new do |conn|
100
+ conn.request :authorization, "Bearer", @access_token
101
+ conn.headers["x-api-key"] = @client_id
86
102
  end
87
103
  counter = 0
88
104
  loop do
89
105
  sleep(6)
90
- poll_response = poll.get(url)
106
+ response = connection.get(url)
91
107
  counter += 1
92
- if poll_response.body.include?('"cpf:status":{"completed":true,"type":"","status":200}')
93
- write_to_file(poll_response)
94
- break
108
+ if response.body.include?('"cpf:status":{"completed":true,"type":"","status":200}')
109
+ @raw_response = response
110
+ return write_to_file(response.body)
111
+ else
112
+ status = JSON.parse(response.body)["cpf:status"]
113
+ raise Error.new(status_code: status["status"], msg: status) if status["status"] != 202
95
114
  end
96
115
  break if counter > 10
97
- rescue StandardError => e
98
- # Here we can log if there is a failure from Adobe's response i.e. "Failed to complete"
116
+ rescue => e
117
+ # Raise other exceptions
99
118
  raise(e)
100
119
  end
101
120
  end
102
121
 
103
- def write_to_file(response)
104
- temp_file = Tempfile.new([Time.now.to_i.to_s, @output_ext])
105
- temp_file.write response.body
106
- temp_file.rewind
107
- # Read in the raw response and remove the
108
- my_array = IO.readlines(temp_file.path)
109
- my_array.pop
110
- array = my_array.drop(9)
111
- arry = array.join('')
112
- File.open(@destination_path, 'wb') { |f| f.write arry.chomp}
113
- temp_file.close!
122
+ def write_to_file(response_body)
123
+ line_index = []
124
+ lines = response_body.split("\r\n")
125
+ lines.each_with_index do |line, i|
126
+ next if line.include?("--Boundary_") || line.match?(/^Content-(Type|Disposition):/) || line.empty? || JSON.parse(line.force_encoding("UTF-8").to_s)
127
+ rescue
128
+ line_index << i
129
+ end
130
+ if line_index.length == 1
131
+ File.open(@output, "wb") { |f| f.write lines.at(line_index[0])}
132
+ true
133
+ else
134
+ false
135
+ end
114
136
  end
137
+
115
138
  end
116
- end
139
+ end
@@ -1,3 +1,26 @@
1
1
  module AdobeDocApi
2
- class Error < StandardError; end
3
- end
2
+
3
+ class Error < StandardError
4
+ attr_reader :status_code
5
+
6
+ def initialize(status_code:, msg:)
7
+ super
8
+ @status_code = status_code
9
+ @msg = msg
10
+ end
11
+
12
+ def message
13
+ case @status_code
14
+ when 200 then "The operation has failed due to some reason before the wait time provided in the Prefer header specified in the request (if Prefer header was not specified then the wait time is 59s by default), for detailed error refer cpf:status field inside the response body. Expect 200 HTTP status code only when respond-async,wait=0 is NOT specified in the Prefer Header value while creating the request. Refer the response body structure from GET call 200 response."
15
+ when 201 then "The operation is completed successfully before the wait time provided in the Prefer header specified in the request (if Prefer header was not specified then the wait time is 59s by default). Expect 201 HTTP status code only when respond-async,wait=0 is NOT specified in the Prefer Header value while creating the request. Refer the response body structure from GET call 200 response."
16
+ when 400 then "#{@msg["title"]} : #{@msg["report"]["error_code"]}"
17
+ when 408 then "Request Timed Out. Some operation has timed out due to client issue."
18
+ when 429 then "Caller doesn't have sufficient quota for this operation."
19
+ when 500 then "Internal Server Error. The server has encountered an error and is unable to process your request at this time."
20
+ else
21
+ @msg
22
+ end
23
+ end
24
+ end
25
+
26
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AdobeDocApi
4
- VERSION = "0.1.1"
4
+ VERSION = "0.1.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adobe_doc_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Sonnier