mirror of https://github.com/docusealco/docuseal
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
13 KiB
369 lines
13 KiB
# frozen_string_literal: true
|
|
|
|
describe 'Templates API' do
|
|
let(:account) { create(:account, :with_testing_account) }
|
|
let(:testing_account) { account.testing_accounts.first }
|
|
let(:author) { create(:user, account:) }
|
|
let(:testing_author) { create(:user, account: testing_account) }
|
|
let(:folder) { create(:template_folder, account:) }
|
|
let(:template_preferences) { { 'request_email_subject' => 'Subject text', 'request_email_body' => 'Body Text' } }
|
|
|
|
describe 'GET /api/templates' do
|
|
it 'returns a list of templates' do
|
|
templates = [
|
|
create(:template, account:,
|
|
author:,
|
|
folder:,
|
|
external_id: SecureRandom.base58(10),
|
|
preferences: template_preferences),
|
|
create(:template, account:,
|
|
author:,
|
|
folder:,
|
|
external_id: SecureRandom.base58(10),
|
|
preferences: template_preferences)
|
|
].reverse
|
|
|
|
get '/api/templates', headers: { 'x-auth-token': author.access_token.token }
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
expect(response.parsed_body['pagination']).to eq(JSON.parse({
|
|
count: templates.size,
|
|
next: templates.last.id,
|
|
prev: templates.first.id
|
|
}.to_json))
|
|
expect(response.parsed_body['data']).to eq(JSON.parse(templates.map { |t| template_body(t) }.to_json))
|
|
end
|
|
end
|
|
|
|
describe 'GET /api/templates/:id' do
|
|
it 'returns a template' do
|
|
template = create(:template, account:,
|
|
author:,
|
|
folder:,
|
|
external_id: SecureRandom.base58(10),
|
|
preferences: template_preferences)
|
|
|
|
get "/api/templates/#{template.id}", headers: { 'x-auth-token': author.access_token.token }
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
expect(response.parsed_body).to eq(JSON.parse(template_body(template).to_json))
|
|
end
|
|
|
|
it 'returns an authorization error if test account API token is used with a production template' do
|
|
template = create(:template, account:,
|
|
author:,
|
|
folder:,
|
|
external_id: SecureRandom.base58(10),
|
|
preferences: template_preferences)
|
|
|
|
get "/api/templates/#{template.id}", headers: { 'x-auth-token': testing_author.access_token.token }
|
|
|
|
expect(response).to have_http_status(:forbidden)
|
|
expect(response.parsed_body).to eq(
|
|
JSON.parse({ error: "Template #{template.id} not found using testing API key; " \
|
|
'Use production API key to access production templates.' }.to_json)
|
|
)
|
|
end
|
|
|
|
it 'returns an authorization error if production account API token is used with a test template' do
|
|
template = create(:template, account: testing_account,
|
|
author: testing_author,
|
|
folder:,
|
|
external_id: SecureRandom.base58(10),
|
|
preferences: template_preferences)
|
|
|
|
get "/api/templates/#{template.id}", headers: { 'x-auth-token': author.access_token.token }
|
|
|
|
expect(response).to have_http_status(:forbidden)
|
|
expect(response.parsed_body).to eq(
|
|
JSON.parse({ error: "Template #{template.id} not found using production API key; " \
|
|
'Use testing API key to access testing templates.' }.to_json)
|
|
)
|
|
end
|
|
end
|
|
|
|
describe 'PUT /api/templates' do
|
|
let(:template) do
|
|
create(:template, account:,
|
|
author:,
|
|
folder:,
|
|
external_id: SecureRandom.base58(10),
|
|
preferences: template_preferences)
|
|
end
|
|
|
|
it 'updates a template' do
|
|
put "/api/templates/#{template.id}", headers: { 'x-auth-token': author.access_token.token }, params: {
|
|
name: 'Updated Template Name',
|
|
external_id: '123456'
|
|
}.to_json
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
|
|
template.reload
|
|
|
|
expect(template.name).to eq('Updated Template Name')
|
|
expect(template.external_id).to eq('123456')
|
|
expect(response.parsed_body).to eq(JSON.parse({
|
|
id: template.id,
|
|
updated_at: template.updated_at
|
|
}.to_json))
|
|
end
|
|
|
|
it "enables the template's shared link" do
|
|
expect do
|
|
put "/api/templates/#{template.id}", headers: { 'x-auth-token': author.access_token.token }, params: {
|
|
shared_link: true
|
|
}.to_json
|
|
end.to change { template.reload.shared_link }.from(false).to(true)
|
|
end
|
|
|
|
it "disables the template's shared link" do
|
|
template.update(shared_link: true)
|
|
|
|
expect do
|
|
put "/api/templates/#{template.id}", headers: { 'x-auth-token': author.access_token.token }, params: {
|
|
shared_link: false
|
|
}.to_json
|
|
end.to change { template.reload.shared_link }.from(true).to(false)
|
|
end
|
|
end
|
|
|
|
describe 'DELETE /api/templates/:id' do
|
|
it 'archives a template' do
|
|
template = create(:template, account:,
|
|
author:,
|
|
folder:,
|
|
external_id: SecureRandom.base58(10),
|
|
preferences: template_preferences)
|
|
|
|
delete "/api/templates/#{template.id}", headers: { 'x-auth-token': author.access_token.token }
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
|
|
template.reload
|
|
|
|
expect(template.archived_at).not_to be_nil
|
|
expect(response.parsed_body).to eq(JSON.parse({
|
|
id: template.id,
|
|
archived_at: template.archived_at
|
|
}.to_json))
|
|
end
|
|
end
|
|
|
|
describe 'POST /api/templates/pdf' do
|
|
let(:pdf_base64) { Base64.strict_encode64(Rails.root.join('spec/fixtures/sample-document.pdf').read) }
|
|
let(:base_params) do
|
|
{
|
|
name: 'Test Template',
|
|
documents: [{ name: 'sample-document.pdf', file: pdf_base64 }]
|
|
}
|
|
end
|
|
|
|
it 'creates a template with audience set as submitters_order' do
|
|
post '/api/templates/pdf',
|
|
headers: { 'x-auth-token': author.access_token.token },
|
|
params: base_params.merge(audience: 'manager_then_employee').to_json,
|
|
env: { 'CONTENT_TYPE' => 'application/json' }
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
|
|
template = Template.last
|
|
expect(template.preferences['submitters_order']).to eq('manager_then_employee')
|
|
end
|
|
|
|
it 'creates a template with simultaneous audience' do
|
|
post '/api/templates/pdf',
|
|
headers: { 'x-auth-token': author.access_token.token },
|
|
params: base_params.merge(audience: 'simultaneous').to_json,
|
|
env: { 'CONTENT_TYPE' => 'application/json' }
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
expect(Template.last.preferences['submitters_order']).to eq('simultaneous')
|
|
end
|
|
|
|
it 'ignores audience when not provided' do
|
|
post '/api/templates/pdf',
|
|
headers: { 'x-auth-token': author.access_token.token },
|
|
params: base_params.to_json,
|
|
env: { 'CONTENT_TYPE' => 'application/json' }
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
expect(Template.last.preferences['submitters_order']).to eq('single_sided')
|
|
end
|
|
|
|
it 'ignores an invalid audience value' do
|
|
post '/api/templates/pdf',
|
|
headers: { 'x-auth-token': author.access_token.token },
|
|
params: base_params.merge(audience: 'invalid_value').to_json,
|
|
env: { 'CONTENT_TYPE' => 'application/json' }
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
expect(Template.last.preferences['submitters_order']).to eq('single_sided')
|
|
end
|
|
|
|
it 'stores prefill attribute on fields' do
|
|
employee_uuid = SecureRandom.uuid
|
|
fields = [
|
|
{
|
|
uuid: SecureRandom.uuid,
|
|
submitter_uuid: employee_uuid,
|
|
name: 'First Name',
|
|
type: 'text',
|
|
prefill: 'employee_first_name',
|
|
areas: [{ x: 0.1, y: 0.1, w: 0.2, h: 0.03, page: 0 }]
|
|
}
|
|
]
|
|
|
|
post '/api/templates/pdf',
|
|
headers: { 'x-auth-token': author.access_token.token },
|
|
params: base_params.merge(
|
|
submitters: [{ name: 'Employee', uuid: employee_uuid }],
|
|
fields:
|
|
).to_json,
|
|
env: { 'CONTENT_TYPE' => 'application/json' }
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
|
|
stored_field = Template.last.fields.find { |f| f['name'] == 'First Name' }
|
|
expect(stored_field['prefill']).to eq('employee_first_name')
|
|
end
|
|
end
|
|
|
|
describe 'POST /api/templates/:id/clone' do
|
|
it 'clones a template' do
|
|
template = create(:template, account:,
|
|
author:,
|
|
folder:,
|
|
external_id: SecureRandom.base58(10),
|
|
preferences: template_preferences)
|
|
|
|
expect do
|
|
post "/api/templates/#{template.id}/clone", headers: { 'x-auth-token': author.access_token.token }, params: {
|
|
name: 'Cloned Template Name',
|
|
external_id: '123456'
|
|
}.to_json
|
|
end.to change(Template, :count)
|
|
|
|
expect(response).to have_http_status(:ok)
|
|
|
|
cloned_template = Template.last
|
|
|
|
expect(cloned_template.name).to eq('Cloned Template Name')
|
|
expect(cloned_template.external_id).to eq('123456')
|
|
expect(response.parsed_body).to eq(JSON.parse(clone_template_body(cloned_template).to_json))
|
|
end
|
|
|
|
context 'when cloning a template' do
|
|
it 'preserves partnership ownership' do
|
|
global_partnership = create(:partnership)
|
|
allow(ExportLocation).to receive(:global_partnership_id).and_return(global_partnership.id)
|
|
|
|
partnership_template = create(
|
|
:template,
|
|
partnership: global_partnership,
|
|
account: nil, author: create(:user, account: nil)
|
|
)
|
|
|
|
expect do
|
|
post "/api/templates/#{partnership_template.id}/clone",
|
|
headers: { 'x-auth-token': partnership_template.author.access_token.token }
|
|
end.to change(Template, :count)
|
|
|
|
cloned_template = Template.last
|
|
expect(cloned_template.partnership_id).to eq(partnership_template.partnership_id)
|
|
expect(cloned_template.account_id).to be_nil
|
|
end
|
|
|
|
it 'preserves account ownership' do
|
|
account_template = create(:template, account: account, author: author)
|
|
|
|
expect do
|
|
post "/api/templates/#{account_template.id}/clone",
|
|
headers: { 'x-auth-token': author.access_token.token }
|
|
end.to change(Template, :count)
|
|
|
|
cloned_template = Template.last
|
|
expect(cloned_template.account_id).to eq(account.id)
|
|
expect(cloned_template.partnership_id).to be_nil
|
|
end
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def template_body(template)
|
|
template_attachment_uuid = template.schema.first['attachment_uuid']
|
|
attachment = template.schema_documents.preload(:blob).find { |e| e.uuid == template_attachment_uuid }
|
|
first_page_blob =
|
|
ActiveStorage::Attachment.joins(:blob)
|
|
.where(blob: { filename: '0.png' })
|
|
.where(record_id: template.schema_documents.map(&:id),
|
|
record_type: 'ActiveStorage::Attachment',
|
|
name: :preview_images)
|
|
.preload(:blob)
|
|
.first
|
|
.blob
|
|
|
|
{
|
|
id: template.id,
|
|
slug: template.slug,
|
|
name: template.name,
|
|
fields: template.fields,
|
|
submitters: [
|
|
{
|
|
name: 'Employee',
|
|
uuid: template.submitters.first['uuid']
|
|
}
|
|
],
|
|
author: {
|
|
id: author.id,
|
|
first_name: author.first_name,
|
|
last_name: author.last_name,
|
|
email: author.email
|
|
},
|
|
documents: [
|
|
{
|
|
id: template.documents.first.id,
|
|
uuid: template.documents.first.uuid,
|
|
url: ActiveStorage::Blob.proxy_url(attachment.blob),
|
|
preview_image_url: ActiveStorage::Blob.proxy_url(first_page_blob),
|
|
filename: 'sample-document.pdf'
|
|
}
|
|
],
|
|
preferences: {
|
|
'request_email_subject' => 'Subject text',
|
|
'request_email_body' => 'Body Text',
|
|
'submitters_order' => 'single_sided'
|
|
},
|
|
schema: [
|
|
{
|
|
attachment_uuid: template_attachment_uuid,
|
|
name: 'sample-document'
|
|
}
|
|
],
|
|
shared_link: template.shared_link,
|
|
author_id: author.id,
|
|
archived_at: nil,
|
|
created_at: template.created_at,
|
|
updated_at: template.updated_at,
|
|
folder_id: folder.id,
|
|
folder_name: folder.name,
|
|
source: 'native',
|
|
external_id: template.external_id,
|
|
application_key: template.external_id # Backward compatibility
|
|
}
|
|
end
|
|
|
|
def clone_template_body(cloned_template)
|
|
body = template_body(cloned_template).merge(source: 'api')
|
|
body[:fields].each_with_index do |field, index|
|
|
field.merge!(
|
|
'submitter_uuid' => cloned_template.fields[index]['submitter_uuid'],
|
|
'uuid' => cloned_template.fields[index]['uuid']
|
|
)
|
|
end
|
|
|
|
body
|
|
end
|
|
end
|