Skip to content

Commit

Permalink
Merge pull request #7 from ARPC/document-merge-new-api
Browse files Browse the repository at this point in the history
Document merge new api
  • Loading branch information
DBugger32 authored Feb 7, 2023
2 parents df6d0d7 + 78f5e2f commit 38ffdbb
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 105 deletions.
4 changes: 2 additions & 2 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
pdfservices (0.1.2)
pdfservices (0.1.3)
http (~> 5.1)
json (~> 2.6)
jwt (>= 1.0, < 3.0)
Expand Down Expand Up @@ -31,7 +31,7 @@ GEM
domain_name (~> 0.5)
http-form_data (2.3.0)
json (2.6.2)
jwt (2.6.0)
jwt (2.7.0)
llhttp-ffi (0.4.0)
ffi-compiler (~> 1.0)
rake (~> 13.0)
Expand Down
134 changes: 71 additions & 63 deletions lib/pdfservices/document_merge/operation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
module PdfServices
module DocumentMerge
class Operation
ENDPOINT = "https://cpf-ue1.adobe.io/ops/:create?respondWith=%7B%22reltype%22%3A%20%22http%3A%2F%2Fns.adobe.com%2Frel%2Fprimary%22%7D"
DOCUMENT_GENERATION_ASSET_ID = "urn:aaid:cpf:Service-52d5db6097ed436ebb96f13a4c7bf8fb"
PRESIGNED_URL_ENDPOINT = "https://pdf-services.adobe.io/assets"
OPERATION_ENDPOINT = "https://pdf-services.adobe.io/operation/documentgeneration"
ASSETS_ENDPOINT = "https://pdf-services.adobe.io/assets"

def initialize(credentials = nil, template_path = nil, json_data_for_merge = nil, output_format = nil)
@credentials = credentials
Expand All @@ -18,85 +19,92 @@ def initialize(credentials = nil, template_path = nil, json_data_for_merge = nil
@output_format = output_format
end

def execute
form = {
contentAnalyzerRequests: build_content_analyzer_requests.to_json,
InputFile0: build_input_file
}
response = api.post(ENDPOINT, form: form)
if response.status == 202
document_url = response.headers["Location"]
poll_document_result(document_url)
def get_presigned_url
response = api.post(PRESIGNED_URL_ENDPOINT, json: {mediaType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document"})
if response.status == 200
JSON.parse(response.body.to_s)
else
Result.new(nil, "Unexpected response status: #{response.status}")
Result.new(nil, "Unexpected response status from get presigned url: #{response.status}")
end
end

private
def upload_asset(template_path)
presigned_url = get_presigned_url
upload_uri = presigned_url["uploadUri"]
asset_id = presigned_url["assetID"]
aws = HTTP.headers({"Content-Type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document"})
response = aws.put(upload_uri, body: File.open(template_path))
if response.status == 200
asset_id
else
Result.new(nil, "Unexpected response status from asset upload: #{response.status}")
end
end

def build_content_analyzer_requests
{
"cpf:engine": {
"repo:assetId": DOCUMENT_GENERATION_ASSET_ID
},
"cpf:inputs": {
documentIn: {
"dc:format": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"cpf:location": "InputFile0"
},
params: {
"cpf:inline": {
outputFormat: @output_format,
jsonDataForMerge: @json_data_for_merge
}
}
},
"cpf:outputs": {
documentOut: {
"dc:format": mime_type(@output_format),
"cpf:location": "multipartLabel"
}
}
}
def delete_the_asset(asset_id)
api.delete("#{ASSETS_ENDPOINT}/#{asset_id}")
end

def build_headers
def execute
asset_id = upload_asset(@template_path)
response = api.post(OPERATION_ENDPOINT, json: {
assetID: asset_id,
outputFormat: @output_format,
jsonDataForMerge: @json_data_for_merge
})
if response.status == 201
document_url = response.headers["location"]
poll_document_result(document_url, asset_id)
else
Result.new(nil, "Unexpected response status from document merge endpoint: #{response.status}\nasset_id: #{asset_id}")
end
end

private

def api_headers
{
Authorization: "Bearer #{JwtProvider.get_jwt(@credentials)}",
"x-api-key": @credentials.client_id,
Prefer: "respond-async,wait=0"
"Content-Type": "application/json"
}
end

def build_input_file
HTTP::FormData::File.new(@template_path)
end

def api
@api ||= HTTP.headers(build_headers)
@api ||= HTTP.headers(api_headers)
end

def poll_document_result(url)
def poll_document_result(url, original_asset_id)
sleep(1)
document_response = api.get(url)
case document_response.content_type.mime_type
when "application/json"
poll_document_result(url)
when "multipart/mixed"
Result.from_multipart_response(document_response)
else
Result.new(nil, "Unexpected response content type: #{document_response.content_type.mime_type}; status: #{document_response.status}")
end
end

def mime_type(format)
case format
when :docx
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
when :pdf
"application/pdf"
response = api.get(url)
if response.status == 200
json_response = JSON.parse(response.body.to_s)
document_merge_asset_id = json_response&.[]("asset")&.[]("assetID")
case json_response["status"]
when "in progress"
poll_document_result(url, original_asset_id)
when "done"
# download_the_asset
response = HTTP.get(json_response["asset"]["downloadUri"])
# delete the assets
delete_the_asset(original_asset_id) if !original_asset_id.nil?
delete_the_asset(document_merge_asset_id) if !document_merge_asset_id.nil?
# return the result
Result.new(response.body, nil)
when "failed"
# delete the original asset
message = json_response["error"]["message"]
delete_the_asset(original_asset_id) if !original_asset_id.nil?
Result.new(nil, message)
else
# delete the original asset
delete_the_asset(original_asset_id) if original_asset_id.present?
Result.new(nil, "Unexpected status from polling: #{json_response["status"]}")
end
else
"text/plain"
# delete the original asset
delete_the_asset(original_asset_id) if original_asset_id.present?
Result.new(nil, "Unexpected response status from polling: #{json_response["status"]}")
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/pdfservices/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module PdfServices
VERSION = "0.1.2"
VERSION = "0.1.3"
end
Binary file added test/fixtures/files/merge_done.pdf
Binary file not shown.
7 changes: 7 additions & 0 deletions test/fixtures/merge_request_done.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"status": "done",
"asset": {
"assetID": "merged:asset-id",
"downloadUri": "https://documentmerge.file.url"
}
}
24 changes: 1 addition & 23 deletions test/fixtures/merge_request_in_progress.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,3 @@
{
"cpf:status": {
"completed": false,
"type": "",
"title": "In Progress",
"status": 202
},
"cpf:engine": {
"repo:assetId": "urn:aaid:cpf:Service-52d5db6097ed436ebb96f13a4c7bf8fb"
},
"cpf:inputs": {
"documentIn": {
"dc:format": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
"cpf:location": "InputFile0"
},
"params": {
"cpf:inline": {
"outputFormat": "pdf",
"jsonDataForMerge": {
"message": "World"
}
}
}
}
"status": "in progress"
}
Binary file removed test/fixtures/merge_request_response.multipart
Binary file not shown.
6 changes: 5 additions & 1 deletion test/fixtures/ocr_done.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"status": "in progress"
"status": "done",
"asset": {
"assetID": "ocr'd:asset-id",
"downloadUri": "https://ocr.file.url"
}
}
6 changes: 1 addition & 5 deletions test/fixtures/ocr_in_progress.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
{
"status": "done",
"asset": {
"assetID": "ocr'd:asset-id",
"downloadUri": "https://ocr.file.url"
}
"status": "in progress"
}
21 changes: 21 additions & 0 deletions test/integration_spikes/document_merge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# frozen_string_literal: true

require "lib/pdfservices"

credentials = ::PdfServices::CredentialsBuilder.new
.with_client_id(ENV["PDF_SERVICES_CLIENT_ID"])
.with_client_secret(ENV["PDF_SERVICES_CLIENT_SECRET"])
.with_organization_id(ENV["PDF_SERVICES_ORGANIZATION_ID"])
.with_account_id(ENV["PDF_SERVICES_ACCOUNT_ID"])
.with_private_key(ENV["PDF_SERVICES_PRIVATE_KEY"])
.build

json = {message: "World"}
word_file = File.join(Dir.pwd, "test", "fixtures", "files", "sample_template.docx")
operation = ::PdfServices::DocumentMerge::Operation.new(credentials, word_file, json, "pdf")

result = operation.execute

puts(result.error)

result.save_as_file("tmp/document_merge_result.pdf")
58 changes: 48 additions & 10 deletions test/pdf_services_sdk/test_document_merge.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,27 +31,65 @@ def test_it_works
private

def stub_valid_response_sequence
# get JWT for requests
stub_request(:post, "https://ims-na1.adobelogin.com/ims/exchange/jwt/")
.to_return(status: 200, body: json_fixture("valid_jwt_response"))

stub_request(:post, "https://cpf-ue1.adobe.io/ops/:create?respondWith=%7B%22reltype%22:%20%22http://ns.adobe.com/rel/primary%22%7D")
# get a presigned url to upload the source pdf
stub_request(:post, "https://pdf-services.adobe.io/assets")
.with(headers: secured_headers)
.to_return(
status: 202,
headers: {"Location" => "https://cpf-ue1.adobe.io/ops/id/some-document-token"}.merge(json_headers),
body: json_fixture("merge_request_in_progress")
status: 200,
headers: json_headers,
body: json_fixture("presigned_upload_url_response")
)

stub_request(:get, "https://cpf-ue1.adobe.io/ops/id/some-document-token")
.to_return(status: 202, headers: json_headers, body: json_fixture("merge_request_in_progress"))
.to_return(status: 202, headers: json_headers, body: json_fixture("merge_request_in_progress"))
.to_return(status: 200, headers: multipart_headers, body: multipart_fixture("merge_request_response"))
# upload the source template
stub_request(:put, "https://a.presigned.url").to_return(status: 200)

# request the Document Merge operation
stub_request(:post, "https://pdf-services.adobe.io/operation/documentgeneration")
.with(headers: secured_headers)
.to_return(
status: 201,
headers: {"location" => "https://some.polling.url"}.merge(json_headers)
)

# poll for the result
stub_request(:get, "https://some.polling.url")
.with(headers: secured_headers)
.to_return(status: 200, headers: json_headers, body: json_fixture("merge_request_in_progress"))
.to_return(status: 200, headers: json_headers, body: json_fixture("merge_request_in_progress"))
.to_return(status: 200, headers: json_headers, body: json_fixture("merge_request_done"))

# download the merged pdf
stub_request(:get, "https://documentmerge.file.url")
.to_return(status: 200, headers: pdf_headers, body: file_fixture("merge_done.pdf"))

# delete the original asset
stub_request(:delete, "https://pdf-services.adobe.io/assets/urn:a-real-long-asset-asset-id")
.with(headers: secured_headers)
.to_return(status: 200, body: "", headers: {})

# delete the merged asset
stub_request(:delete, "https://pdf-services.adobe.io/assets/merged:asset-id")
.with(headers: secured_headers)
.to_return(status: 200, body: "", headers: {})
end

def secured_headers
{
Authorization: "Bearer fake1.fake2.fake3",
"Content-Type": "application/json",
"X-Api-Key": "123someclientid"
}
end

def json_headers
{"Content-Type" => "application/json;charset=UTF-8"}
end

def multipart_headers
{"Content-Type" => "multipart/mixed; boundary=Boundary_962026_575353369_1661875317362;charset=UTF-8"}
def pdf_headers
{"Content-Type" => "application/pdf"}
end
end

0 comments on commit 38ffdbb

Please sign in to comment.