inkdit 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|