inkdit 0.0.1
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.
- data/.gitignore +7 -0
- data/Gemfile +4 -0
- data/README.md +79 -0
- data/Rakefile +9 -0
- data/inkdit.gemspec +33 -0
- data/lib/inkdit.rb +64 -0
- data/lib/inkdit/client.rb +87 -0
- data/lib/inkdit/contract.rb +72 -0
- data/lib/inkdit/entity.rb +37 -0
- data/lib/inkdit/form_contract.rb +32 -0
- data/lib/inkdit/resource.rb +41 -0
- data/lib/inkdit/signature.rb +34 -0
- data/lib/inkdit/signature_field.rb +31 -0
- data/lib/inkdit/version.rb +3 -0
- data/script/get_access_token.rb +61 -0
- data/spec/client_spec.rb +17 -0
- data/spec/contract_spec.rb +50 -0
- data/spec/form_contract_spec.rb +25 -0
- data/spec/spec_helper.rb +9 -0
- metadata +132 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# Inkdit API Client Library #
|
2
|
+
|
3
|
+
## Obtaining an API Key ##
|
4
|
+
|
5
|
+
To use the Inkdit API you need to [register your application](https://developer.inkdit.com/apps/register).
|
6
|
+
Once your application is registered, you'll be given an API key and a shared secret.
|
7
|
+
|
8
|
+
Inkdit::Config['api_key'] = '...'
|
9
|
+
Inkdit::Config['secret'] = '...'
|
10
|
+
|
11
|
+
## Obtaining an Access Token ##
|
12
|
+
|
13
|
+
Accessing the API requires an [OAuth 2](http://tools.ietf.org/html/draft-ietf-oauth-v2-22#section-4.1) access token.
|
14
|
+
This means the user needs to give your application authorization to access their account.
|
15
|
+
|
16
|
+
First, you redirect the user to the authorization code URL:
|
17
|
+
|
18
|
+
redirect_to Inkdit.authorization_code_url(scopes, redirect_uri)
|
19
|
+
|
20
|
+
`scopes` is the list of permissions that your application needs (see https://developer.inkdit.com/docs/read/Home).
|
21
|
+
When the user authorizes your application (or denies authorization), their browser will be redirected to `redirect_uri`.
|
22
|
+
|
23
|
+
If the authorization is successfully, a `code` parameter will be included when the user is redirected.
|
24
|
+
|
25
|
+
access_token = Inkdit.get_token(params[:code], redirect_uri)
|
26
|
+
|
27
|
+
You should store this token somewhere.
|
28
|
+
|
29
|
+
some_storage_function(current_user, access_token.token, access_token.refresh_token, access_token.expires_at)
|
30
|
+
|
31
|
+
For the full details (including what a client needs to do for security and error
|
32
|
+
handling), read the [OAuth 2 spec](htjp://tools.ietf.org/html/draft-ietf-oauth-v2-22).
|
33
|
+
|
34
|
+
## Using the API ##
|
35
|
+
|
36
|
+
At last, we're ready to go! Once everything is set up, you can create an `Inkdit::Client` using the access token you stored:
|
37
|
+
|
38
|
+
client = Inkdit::Client.new(:access_token => ..., :expires_at => ..., :refresh_token => ...)
|
39
|
+
|
40
|
+
The first thing to do is find out who this access token belongs to:
|
41
|
+
|
42
|
+
entity = client.get_entity
|
43
|
+
|
44
|
+
Then you might want to take a look at that entity's contracts:
|
45
|
+
|
46
|
+
entity.get_contracts
|
47
|
+
|
48
|
+
or create a new contract:
|
49
|
+
|
50
|
+
params = { :name => '...', :content => '...' }
|
51
|
+
contract = Inkdit::Contract.create(client, entity, params)
|
52
|
+
|
53
|
+
Given an `Inkdit::Contract`, you can list the signature fields:
|
54
|
+
|
55
|
+
contract.fetch!
|
56
|
+
contract.signatures
|
57
|
+
|
58
|
+
and sign an unsigned field:
|
59
|
+
|
60
|
+
signature_field.sign!
|
61
|
+
|
62
|
+
If you know the URL of a form contract, you can sign it:
|
63
|
+
|
64
|
+
form_contract = Inkdit::FormContract.new client, form_contract_url
|
65
|
+
form_contract.fetch!
|
66
|
+
form_contract.sign!
|
67
|
+
|
68
|
+
## Running the Specs ##
|
69
|
+
|
70
|
+
To run the specs you need a config.yml file containing your API key, shared secret, and access token.
|
71
|
+
Once you've obtained an API key, there's a demo application that can be used to obtain an access token:
|
72
|
+
|
73
|
+
./script/get_access_token.rb
|
74
|
+
|
75
|
+
Then visit http://localhost:4567/ in your browser.
|
76
|
+
|
77
|
+
Once you've entered your API key and shared scret, you will be prompted to log
|
78
|
+
into Inkdit and authorize the application. When the application is authorized, it will produce a file that you can copy and paste into config.yml.
|
79
|
+
|
data/Rakefile
ADDED
data/inkdit.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "inkdit/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "inkdit"
|
7
|
+
s.version = Inkdit::VERSION
|
8
|
+
s.authors = ["Brendan Taylor"]
|
9
|
+
s.email = ["brendan@inkdit.com"]
|
10
|
+
s.homepage = "https://developer.inkdit.com/"
|
11
|
+
s.summary = %q{A library for using the Inkdit API to sign, share and store contracts}
|
12
|
+
s.description = %q{This is both a working Ruby gem and a code sample that other client implementations can work from.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "inkdit"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
s.add_runtime_dependency "oauth2"
|
22
|
+
|
23
|
+
# documentation
|
24
|
+
s.add_development_dependency "yard"
|
25
|
+
|
26
|
+
# test dependencies
|
27
|
+
s.add_development_dependency "rspec"
|
28
|
+
s.add_development_dependency "vcr"
|
29
|
+
|
30
|
+
# dependencies for our access token getter
|
31
|
+
s.add_development_dependency "sinatra"
|
32
|
+
s.add_development_dependency "haml"
|
33
|
+
end
|
data/lib/inkdit.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require "inkdit/version"
|
2
|
+
|
3
|
+
require "inkdit/client"
|
4
|
+
require "inkdit/resource"
|
5
|
+
|
6
|
+
require "inkdit/entity"
|
7
|
+
|
8
|
+
require "inkdit/contract"
|
9
|
+
require "inkdit/form_contract"
|
10
|
+
|
11
|
+
require "inkdit/signature"
|
12
|
+
require "inkdit/signature_field"
|
13
|
+
|
14
|
+
require "oauth2"
|
15
|
+
|
16
|
+
module Inkdit
|
17
|
+
# global configuration for the client.
|
18
|
+
# this has to be set up for anything to work. It must have the keys:
|
19
|
+
#
|
20
|
+
# +api_key+:: your application's API key, obtained from https://developer.inkdit.com/
|
21
|
+
# +secret+:: your application's shared secret, obtained from https://developer.inkdit.com/
|
22
|
+
#
|
23
|
+
# It can optionally have the key:
|
24
|
+
#
|
25
|
+
# +debug+:: +true+ if you want HTTP requests and responses printed to stdout.
|
26
|
+
Config = {}
|
27
|
+
|
28
|
+
# @param [Array<String,Symbol>] scopes an Array of the permissions that your application needs (see https://developer.inkdit.com/docs/read/Home)
|
29
|
+
# @param [String] redirect_uri the URL that the user should be redirected to after the authorization code is obtained (OAuth 2 section 4.1.2)
|
30
|
+
#
|
31
|
+
# @return [String] the URL that the user should be sent to to obtain an authorization code. (OAuth 2 section 4.1.1)
|
32
|
+
def self.authorization_code_url(scopes, redirect_uri)
|
33
|
+
scopes = scopes.join('%20')
|
34
|
+
"https://inkdit.com/oauth?response_type=code&client_id=#{Config['api_key']}&scope=#{scopes}&redirect_uri=#{redirect_uri}"
|
35
|
+
end
|
36
|
+
|
37
|
+
# you probably don't want to call this.
|
38
|
+
#
|
39
|
+
# @return [OAuth2::Client]
|
40
|
+
def self.oauth_client # :nodoc:
|
41
|
+
api_key = Inkdit::Config['api_key']
|
42
|
+
secret = Inkdit::Config['secret']
|
43
|
+
|
44
|
+
opts = { :site => 'https://api.inkdit.com/',
|
45
|
+
:token_url => '/token',
|
46
|
+
:raise_errors => false
|
47
|
+
}
|
48
|
+
|
49
|
+
OAuth2::Client.new(api_key, secret, opts)
|
50
|
+
end
|
51
|
+
|
52
|
+
# requests an access token using an authorization code (OAuth 2 section 4.1.3/4.1.4)
|
53
|
+
#
|
54
|
+
# @param [String] authorization_code the authorization code Inkdit passed to your application
|
55
|
+
# @param [String] redirect_uri the redirect URI you used when requesting the authorization code
|
56
|
+
#
|
57
|
+
# @return [OAuth2::AccessToken]
|
58
|
+
def self.get_token(authorization_code, redirect_uri)
|
59
|
+
self.oauth_client.auth_code.get_token(authorization_code, :redirect_uri => redirect_uri)
|
60
|
+
end
|
61
|
+
|
62
|
+
# an exception that's raised when a request fails due to an invalid access token
|
63
|
+
class Unauthorized < Exception; end
|
64
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
module Inkdit
|
2
|
+
# a client that can issue authenticated HTTP requests to Inkdit's API.
|
3
|
+
class Client
|
4
|
+
# +token+ is a Hash containing the details of the access token returned by {Inkdit.get_token}.
|
5
|
+
# The keys +access_token+, +refresh_token+, and +expires_at+ are required.
|
6
|
+
def initialize(token)
|
7
|
+
# AccessToken#from_hash modifies the hash it's passed.
|
8
|
+
@access_token = OAuth2::AccessToken.from_hash(oauth_client, token.clone)
|
9
|
+
end
|
10
|
+
|
11
|
+
# @return [Entity] the entity that this access token belongs to
|
12
|
+
def get_entity
|
13
|
+
response = get '/v1/'
|
14
|
+
|
15
|
+
entity = response.parsed["entities"].first
|
16
|
+
Entity.new(self, entity)
|
17
|
+
end
|
18
|
+
|
19
|
+
# issue an HTTP +GET+.
|
20
|
+
# @return [OAuth2::Response]
|
21
|
+
def get(path)
|
22
|
+
request(:get, path, {})
|
23
|
+
end
|
24
|
+
|
25
|
+
# issue an HTTP +POST+.
|
26
|
+
# @return [OAuth2::Response]
|
27
|
+
def post(path, params)
|
28
|
+
request(:post, path, params)
|
29
|
+
end
|
30
|
+
|
31
|
+
# issue an HTTP +PUT+.
|
32
|
+
# @return [OAuth2::Response]
|
33
|
+
def put(path, params)
|
34
|
+
request(:put, path, params)
|
35
|
+
end
|
36
|
+
|
37
|
+
# issue an HTTP +DELETE+.
|
38
|
+
# @return [OAuth2::Response]
|
39
|
+
def delete(path, params)
|
40
|
+
request(:delete, path, {})
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def oauth_client
|
46
|
+
@_oauth_client ||= Inkdit.oauth_client
|
47
|
+
end
|
48
|
+
|
49
|
+
def when_debugging
|
50
|
+
yield if Inkdit::Config['debug']
|
51
|
+
end
|
52
|
+
|
53
|
+
# issue an HTTP request.
|
54
|
+
# @return [OAuth2::Response]
|
55
|
+
def request(method, path, params)
|
56
|
+
when_debugging do
|
57
|
+
puts '---'
|
58
|
+
puts "#{method.to_s.upcase} #{path}"
|
59
|
+
puts params.inspect
|
60
|
+
puts
|
61
|
+
end
|
62
|
+
|
63
|
+
response = @access_token.send(method, path, params)
|
64
|
+
|
65
|
+
when_debugging do
|
66
|
+
puts response.status
|
67
|
+
puts response.body
|
68
|
+
puts
|
69
|
+
end
|
70
|
+
|
71
|
+
checked_response(response)
|
72
|
+
end
|
73
|
+
|
74
|
+
# raises {Inkdit::Unauthorized} if the response indicates an invalid access token.
|
75
|
+
# @return [OAuth2::Response]
|
76
|
+
def checked_response(response)
|
77
|
+
if response.status == 401 and response.headers['www-authenticate'].match /invalid_token/
|
78
|
+
# wrong access token or the user rescinded the app's access.
|
79
|
+
# TODO: properly parse the header
|
80
|
+
# Bearer realm="api.inkdit.com", error="invalid_token"
|
81
|
+
raise Inkdit::Unauthorized
|
82
|
+
end
|
83
|
+
|
84
|
+
response
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Inkdit
|
2
|
+
# Represents an {https://developer.inkdit.com/docs/read/Contract_Description Inkdit Contract}.
|
3
|
+
class Contract < Resource
|
4
|
+
# create a new contract.
|
5
|
+
#
|
6
|
+
# @param [Client] client the client to create this contract with
|
7
|
+
# @param [Entity] owner the entity that will own this contract
|
8
|
+
# @param [Hash] params the details of the new contract.
|
9
|
+
# this should include the basic details required in a
|
10
|
+
# {https://developer.inkdit.com/docs/read/Contract_Description Contract Description}.
|
11
|
+
#
|
12
|
+
# @return [Contract] contract the newly created contract
|
13
|
+
def self.create(client, owner, params)
|
14
|
+
response = client.post owner.contracts_link, { :body => params.to_json, :headers => { 'Content-Type' => 'application/json' } }
|
15
|
+
self.new(client, response.parsed)
|
16
|
+
end
|
17
|
+
|
18
|
+
# @return [String] a human-readable name for the contract
|
19
|
+
def name
|
20
|
+
@params['name']
|
21
|
+
end
|
22
|
+
|
23
|
+
# @return [String] the contract's content
|
24
|
+
def content
|
25
|
+
@params['content']
|
26
|
+
end
|
27
|
+
|
28
|
+
# whether the cantract is a test contract or not
|
29
|
+
def test?
|
30
|
+
@params['test']
|
31
|
+
end
|
32
|
+
|
33
|
+
# the URL of this contract's HTML representation.
|
34
|
+
def html_link
|
35
|
+
@params['links']['html']
|
36
|
+
end
|
37
|
+
|
38
|
+
def sharing_link
|
39
|
+
@params['links']['shared-with']
|
40
|
+
end
|
41
|
+
|
42
|
+
# an opaque string indicating the version of the contract's content
|
43
|
+
def content_updated_at
|
44
|
+
@params['content_updated_at']
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Array<Signature,SignatureField>] this contract's signatures and unsigned signature fields
|
48
|
+
def signatures
|
49
|
+
@params['signatures'].map do |s|
|
50
|
+
if s['signed_by']
|
51
|
+
Inkdit::Signature.new(@client, s)
|
52
|
+
else
|
53
|
+
Inkdit::SignatureField.new(@client, self, s)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# @param [Entity] individual the individual who should be connected to the contract
|
59
|
+
# @param [Entity] entity the entity that the individual should be connected to the contract through
|
60
|
+
def share_with(individual, entity)
|
61
|
+
params = {
|
62
|
+
:individual => {
|
63
|
+
:url => individual
|
64
|
+
},
|
65
|
+
:entity => {
|
66
|
+
:url => entity
|
67
|
+
}
|
68
|
+
}
|
69
|
+
@client.post self.sharing_link, { :body => params.to_json, :headers => { 'Content-Type' => 'application/json' } }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Inkdit
|
2
|
+
# Represents an {https://developer.inkdit.com/docs/read/Entity_Description Inkdit Entity}.
|
3
|
+
class Entity < Resource
|
4
|
+
# this entity's type. +'individual'+ or +'organization'+.
|
5
|
+
def type
|
6
|
+
@params['type']
|
7
|
+
end
|
8
|
+
|
9
|
+
# this entity's human-readable name.
|
10
|
+
def label
|
11
|
+
@params['label']
|
12
|
+
end
|
13
|
+
|
14
|
+
# the URL of this entity's HTML representation.
|
15
|
+
def html_link
|
16
|
+
@params['links']['html']
|
17
|
+
end
|
18
|
+
|
19
|
+
# the URL of this entity's Contract Collection
|
20
|
+
def contracts_link
|
21
|
+
@params['links']['contracts']
|
22
|
+
end
|
23
|
+
|
24
|
+
# retrieves this entity's Contract Collection
|
25
|
+
# @return [Array<Contract>] the contracts in this entity's Contract Collection
|
26
|
+
def get_contracts
|
27
|
+
response = @client.get(contracts_link)
|
28
|
+
response.parsed['resources'].map do |contract_params|
|
29
|
+
Contract.new @client, contract_params
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def inspect
|
34
|
+
"#<Inkdit::Entity type=#{type} label=#{label}>"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Inkdit
|
2
|
+
# represents a form contract on Inkdit
|
3
|
+
class FormContract < Resource
|
4
|
+
# @return [String] a human-readable name for the contract
|
5
|
+
def name
|
6
|
+
@params['name']
|
7
|
+
end
|
8
|
+
|
9
|
+
# @return [String] the contract's content
|
10
|
+
def content
|
11
|
+
@params['content']
|
12
|
+
end
|
13
|
+
|
14
|
+
def signatures_link
|
15
|
+
@params['links']['signatures']
|
16
|
+
end
|
17
|
+
|
18
|
+
# an opaque string indicating the version of the form contract's content
|
19
|
+
def content_updated_at
|
20
|
+
@params['content_updated_at']
|
21
|
+
end
|
22
|
+
|
23
|
+
# sign this field as the user and entity associated with the current access token.
|
24
|
+
# @return [Signature] the newly-created signature
|
25
|
+
def sign!
|
26
|
+
params = { :if_updated_at => self.content_updated_at }
|
27
|
+
response = @client.post self.signatures_link, { :body => params.to_json, :headers => { 'Content-Type' => 'application/json' } }
|
28
|
+
|
29
|
+
Inkdit::Signature.new(@client, response.parsed)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Inkdit
|
2
|
+
# some resource (in the REST sense) in the system
|
3
|
+
class Resource
|
4
|
+
# the URL of this resource
|
5
|
+
attr_reader :url
|
6
|
+
|
7
|
+
# @param [Client] client the client to use this resource with
|
8
|
+
# @param [String,Hash] opts either the resource's URL, or a hash describing the resource
|
9
|
+
def initialize(client, opts)
|
10
|
+
@client = client
|
11
|
+
|
12
|
+
if opts.is_a? String
|
13
|
+
@url = opts
|
14
|
+
@params = []
|
15
|
+
else
|
16
|
+
self.params = opts
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# retrieve this resource using its URL.
|
21
|
+
def fetch!
|
22
|
+
response = @client.get(self.url)
|
23
|
+
@params = response.parsed
|
24
|
+
end
|
25
|
+
|
26
|
+
def ==(other_resource)
|
27
|
+
self.url == other_resource.url
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
"#<#{self.class.inspect} params=#{@params}>"
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def params=(params)
|
37
|
+
@params = params
|
38
|
+
@url = params['url']
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Inkdit
|
2
|
+
# Represents a signed {https://developer.inkdit.com/docs/read/Signature_Description signature placeholder}.
|
3
|
+
class Signature
|
4
|
+
def initialize(client, params)
|
5
|
+
@client = client
|
6
|
+
@params = params
|
7
|
+
end
|
8
|
+
|
9
|
+
# @return [Entity] the individual who signed this field
|
10
|
+
def signed_by
|
11
|
+
Inkdit::Entity.new(@client, @params['signed_by'])
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Entity] the entity this field was signed on behalf of
|
15
|
+
def on_behalf_of
|
16
|
+
Inkdit::Entity.new(@client, @params['on_behalf_of'])
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Time] when this field was signed
|
20
|
+
def signed_at
|
21
|
+
Time.parse(params['signed_at'])
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Contract] the contract this field is part of
|
25
|
+
def contract
|
26
|
+
return unless @params['contract']
|
27
|
+
Inkdit::Contract.new @client, @params['contract']
|
28
|
+
end
|
29
|
+
|
30
|
+
def inspect
|
31
|
+
"#<#{self.class.inspect} params=#{@params}>"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Inkdit
|
2
|
+
# Represents an unsigned {https://developer.inkdit.com/docs/read/Signature_Description signature placeholder}.
|
3
|
+
class SignatureField
|
4
|
+
def initialize(client, contract, params)
|
5
|
+
@client = client
|
6
|
+
@contract = contract
|
7
|
+
@params = params
|
8
|
+
end
|
9
|
+
|
10
|
+
# @return [String] the URL of this signature field
|
11
|
+
def url
|
12
|
+
@params['url']
|
13
|
+
end
|
14
|
+
|
15
|
+
# sign this field as the user and entity associated with the current access token.
|
16
|
+
# @return [Signature] the newly-created signature
|
17
|
+
def sign!
|
18
|
+
params = {
|
19
|
+
:if_updated_at => @contract.content_updated_at
|
20
|
+
}
|
21
|
+
|
22
|
+
response = @client.put self.url, { :body => params.to_json, :headers => { 'Content-Type' => 'application/json' } }
|
23
|
+
|
24
|
+
Inkdit::Signature.new(@client, response.parsed)
|
25
|
+
end
|
26
|
+
|
27
|
+
def inspect
|
28
|
+
"#<#{self.class.inspect} params=#{@params}>"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
|
5
|
+
require 'inkdit'
|
6
|
+
|
7
|
+
require 'sinatra'
|
8
|
+
require 'haml'
|
9
|
+
|
10
|
+
get '/' do
|
11
|
+
haml <<END
|
12
|
+
%div
|
13
|
+
%form{:action => '/get-auth-code'}
|
14
|
+
%p First, I need your details for accessing the Inkdit API:
|
15
|
+
%label
|
16
|
+
API Key:
|
17
|
+
%input{:name => 'api_key'}
|
18
|
+
%br
|
19
|
+
%label
|
20
|
+
Secret:
|
21
|
+
%input{:name => 'secret'}
|
22
|
+
%br
|
23
|
+
%input{:type => 'submit'}
|
24
|
+
END
|
25
|
+
end
|
26
|
+
|
27
|
+
get '/get-auth-code' do
|
28
|
+
Inkdit::Config['api_key'] = params[:api_key]
|
29
|
+
Inkdit::Config['secret'] = params[:secret]
|
30
|
+
|
31
|
+
redirect_url = url('/got-auth-code')
|
32
|
+
auth_code_url = Inkdit.authorization_code_url([:read, :write, :sign], redirect_url)
|
33
|
+
|
34
|
+
redirect auth_code_url
|
35
|
+
end
|
36
|
+
|
37
|
+
get '/got-auth-code' do
|
38
|
+
auth_code = params[:code]
|
39
|
+
|
40
|
+
@access_token = Inkdit.get_token(params[:code], url('/got-auth-code'))
|
41
|
+
|
42
|
+
config = {
|
43
|
+
'api_key' => Inkdit::Config['api_key'],
|
44
|
+
'secret' => Inkdit::Config['secret'],
|
45
|
+
'access_token' => {
|
46
|
+
'access_token' => @access_token.token,
|
47
|
+
'refresh_token' => @access_token.refresh_token,
|
48
|
+
'expires_at' => @access_token.expires_at
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
<<END
|
53
|
+
<p>
|
54
|
+
Ok, we've got a thing for you! Save the below as config.yml.
|
55
|
+
</p>
|
56
|
+
|
57
|
+
<pre>
|
58
|
+
#{config.to_yaml}
|
59
|
+
</pre>
|
60
|
+
END
|
61
|
+
end
|
data/spec/client_spec.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Inkdit::Client do
|
4
|
+
before do
|
5
|
+
@client = Inkdit::Client.new(Inkdit::Config['access_token'])
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'gives you information about the entity the client is authorized to access' do
|
9
|
+
entity = @client.get_entity
|
10
|
+
|
11
|
+
entity.type.should == 'individual'
|
12
|
+
entity.label.should == 'API Test'
|
13
|
+
|
14
|
+
entity.html_link.should_not be_nil
|
15
|
+
entity.contracts_link.should_not be_nil
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'time'
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Inkdit::Contract do
|
6
|
+
before do
|
7
|
+
@client = Inkdit::Client.new(Inkdit::Config['access_token'])
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'allows you to manipulate a contract' do
|
11
|
+
entity = @client.get_entity
|
12
|
+
|
13
|
+
contract_name = "API Test #{Time.now.iso8601}"
|
14
|
+
|
15
|
+
params = {
|
16
|
+
:name => contract_name,
|
17
|
+
:content => "<contract> <signature id='1' /></contract>",
|
18
|
+
:test => true
|
19
|
+
}
|
20
|
+
|
21
|
+
contract = Inkdit::Contract.create @client, entity, params
|
22
|
+
|
23
|
+
# the new contract should have the attributes we asked for
|
24
|
+
contract.name.should == contract_name
|
25
|
+
contract.should be_test
|
26
|
+
|
27
|
+
contract.html_link.should_not be_nil
|
28
|
+
contract.sharing_link.should_not be_nil
|
29
|
+
|
30
|
+
# the new contract should appear in the list of contracts
|
31
|
+
contracts = entity.get_contracts
|
32
|
+
contracts.find { |c| c.name == contract_name }.should_not be_nil
|
33
|
+
|
34
|
+
# the new contract can be signed
|
35
|
+
#
|
36
|
+
# first we need to fetch the full representation of the contract
|
37
|
+
# (since the other representation doesn't include signatures)
|
38
|
+
contract.fetch!
|
39
|
+
|
40
|
+
contract.signatures.length.should == 1
|
41
|
+
signature_field = contract.signatures.first
|
42
|
+
|
43
|
+
signature = signature_field.sign!
|
44
|
+
|
45
|
+
signature.signed_by.should == entity
|
46
|
+
signature.on_behalf_of.should == entity
|
47
|
+
end
|
48
|
+
|
49
|
+
pending 'sharing a contract (this is tough to do because it requires an entity URL)'
|
50
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Inkdit::FormContract do
|
4
|
+
before do
|
5
|
+
@client = Inkdit::Client.new(Inkdit::Config['access_token'])
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'allows you to sign a form contract' do
|
9
|
+
# this is the path of the Web 2.0 Information Superhighway API Demo
|
10
|
+
form_contract_path = '/v1/offers/x53c396c3de147b54'
|
11
|
+
|
12
|
+
form_contract = Inkdit::FormContract.new @client, form_contract_path
|
13
|
+
form_contract.fetch!
|
14
|
+
|
15
|
+
form_contract.name.should == 'Web 2.0 Information Superhighway API Demo'
|
16
|
+
form_contract.signatures_link.should_not be_nil
|
17
|
+
|
18
|
+
signature = form_contract.sign!
|
19
|
+
|
20
|
+
signature.signed_by.should_not be_nil
|
21
|
+
signature.on_behalf_of.should_not be_nil
|
22
|
+
|
23
|
+
signature.contract.name.should == 'Web 2.0 Information Superhighway API Demo'
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: inkdit
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Brendan Taylor
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-23 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: oauth2
|
16
|
+
requirement: &9892260 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *9892260
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: yard
|
27
|
+
requirement: &9891720 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *9891720
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
requirement: &9888300 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
type: :development
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *9888300
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: vcr
|
49
|
+
requirement: &9887800 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :development
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *9887800
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: sinatra
|
60
|
+
requirement: &9887080 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ! '>='
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '0'
|
66
|
+
type: :development
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *9887080
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: haml
|
71
|
+
requirement: &9886460 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: *9886460
|
80
|
+
description: This is both a working Ruby gem and a code sample that other client implementations
|
81
|
+
can work from.
|
82
|
+
email:
|
83
|
+
- brendan@inkdit.com
|
84
|
+
executables: []
|
85
|
+
extensions: []
|
86
|
+
extra_rdoc_files: []
|
87
|
+
files:
|
88
|
+
- .gitignore
|
89
|
+
- Gemfile
|
90
|
+
- Gemfile.lock
|
91
|
+
- README.md
|
92
|
+
- Rakefile
|
93
|
+
- inkdit.gemspec
|
94
|
+
- lib/inkdit.rb
|
95
|
+
- lib/inkdit/client.rb
|
96
|
+
- lib/inkdit/contract.rb
|
97
|
+
- lib/inkdit/entity.rb
|
98
|
+
- lib/inkdit/form_contract.rb
|
99
|
+
- lib/inkdit/resource.rb
|
100
|
+
- lib/inkdit/signature.rb
|
101
|
+
- lib/inkdit/signature_field.rb
|
102
|
+
- lib/inkdit/version.rb
|
103
|
+
- script/get_access_token.rb
|
104
|
+
- spec/client_spec.rb
|
105
|
+
- spec/contract_spec.rb
|
106
|
+
- spec/form_contract_spec.rb
|
107
|
+
- spec/spec_helper.rb
|
108
|
+
homepage: https://developer.inkdit.com/
|
109
|
+
licenses: []
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
none: false
|
116
|
+
requirements:
|
117
|
+
- - ! '>='
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
requirements: []
|
127
|
+
rubyforge_project: inkdit
|
128
|
+
rubygems_version: 1.8.10
|
129
|
+
signing_key:
|
130
|
+
specification_version: 3
|
131
|
+
summary: A library for using the Inkdit API to sign, share and store contracts
|
132
|
+
test_files: []
|