Skip to content

Commit

Permalink
Touchups from read/write key adding, extra README info
Browse files Browse the repository at this point in the history
Bump version to 0.7.0. Not backwards compatible!
  • Loading branch information
joshed-io committed Apr 25, 2013
1 parent 43e94be commit f485673
Show file tree
Hide file tree
Showing 8 changed files with 56 additions and 47 deletions.
26 changes: 16 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ keen is tested with Ruby 1.8 and 1.9 on:
### Usage

Before making any API calls, you must supply keen-gem with a Project ID and one or both of your Write and Read Keys.
(If you need a Keen IO account, [sign up here](https://keen.io/) - it's free.)
(If you need a Keen IO account, [sign up here](https://keen.io/) - it's free.) The Write key is required for publishing
events, and the Read key is required for running queries.

The recommended way to do this is to set `KEEN_PROJECT_ID`, `KEEN_WRITE_KEY`, and `KEEN_READ_KEY` in your
environment. If you're using [foreman](http://ddollar.github.com/foreman/), add this to your `.env` file:
Expand Down Expand Up @@ -97,7 +98,7 @@ The Keen IO API provides rich querying capabilities against your event data set.

Queries require that a Read Key is provided. Just like project ID, we encourage that you set this as an environment variable:

KEEN_WRITE_KEY=your-write-key
KEEN_READ_KEY=yyyyyyyyyyyyyyyy

Here's are some examples of querying with keen-gem. Let's assume you've added some events to the "purchases" collection.

Expand Down Expand Up @@ -137,17 +138,17 @@ Detailed information on available parameters for each API resource can be found
To configure keen-gem in code, do as follows:

```ruby
Keen.project_id = 'your-project-id'
Keen.write_key = 'your-write-key'
Keen.read_key = 'your-read-key'
Keen.project_id = 'xxxxxxxxxxxxxxx'
Keen.write_key = 'yyyyyyyyyyyyyyy'
Keen.read_key = 'zzzzzzzzzzzzzzz'
```

You can also configure individual client instances as follows:

```ruby
keen = Keen::Client.new(:project_id => 'your-project-id',
:write_key => 'your-write-key',
:read_key => 'your-read-key')
keen = Keen::Client.new(:project_id => 'xxxxxxxxxxxxxxx',
:write_key => 'yyyyyyyyyyyyyyy',
:read_key => 'zzzzzzzzzzzzzzz')
```

#### em-synchrony
Expand All @@ -166,16 +167,21 @@ In this situation, the JSON event data is passed by encoding it base-64 and addi
The `beacon_url` method found on the `Keen::Client` does this for you. Here's an example:

```ruby
Keen.project_id = 'xxxxxx';
Keen.write_key = 'yyyyyy';
Keen.beacon_url("sign_ups", :recipient => "[email protected]")
# => "https://api.keen.io/3.0/projects/12345/events/email_opens?api_key=your_write_key&data=eyJyZWNpcGllbnQiOiJmb29AZm9vLmNvbSJ9"
# => "https://api.keen.io/3.0/projects/xxxxxx/events/email_opens?api_key=yyyyyy&data=eyJyZWNpcGllbnQiOiJmb29AZm9vLmNvbSJ9"
```

To track email opens, simply add an image to your email template that points to this URL.

### Changelog

##### 0.7.0
+ Add support for read and write scoped keys.
+ BREAKING CHANGE! Added support for read and write scoped keys to reflect the new Keen IO security architecture.
The advantage of scoped keys is finer grained permission control. Public clients that
publish events (like a web browser) require a key that can write but not read. On the other hand, private dashboards and
server-side querying processes require a Read key that should not be made public.

##### 0.6.1
+ Improved logging and exception handling.
Expand Down
5 changes: 3 additions & 2 deletions lib/keen/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ class Client
:verify_depth => 5,
:ca_file => File.expand_path("../../../config/cacert.pem", __FILE__) },
:api_async_http_options => {},
:api_headers => lambda { |sync_or_async|
:api_headers => lambda { |authorization, sync_or_async|
user_agent = "keen-gem, v#{Keen::VERSION}, #{sync_or_async}"
user_agent += ", #{RUBY_VERSION}, #{RUBY_PLATFORM}, #{RUBY_PATCHLEVEL}"
if defined?(RUBY_ENGINE)
user_agent += ", #{RUBY_ENGINE}"
end
{ "Content-Type" => "application/json",
"User-Agent" => user_agent }
"User-Agent" => user_agent,
"Authorization" => authorization }
}
}

Expand Down
14 changes: 4 additions & 10 deletions lib/keen/client/publishing_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,11 @@ def publish(event_collection, properties)
ensure_write_key!
check_event_data!(event_collection, properties)

headers = api_headers("sync")
headers.merge({:Authorization => self.write_key})

begin
response = Keen::HTTP::Sync.new(
api_host, api_port, api_sync_http_options).post(
:path => api_event_resource_path(event_collection),
:headers => headers,
:headers => api_headers(self.write_key, "sync"),
:body => MultiJson.encode(properties))
rescue Exception => http_error
raise HttpError.new("HTTP publish failure: #{http_error.message}", http_error)
Expand All @@ -54,17 +51,14 @@ def publish_async(event_collection, properties)
ensure_write_key!
check_event_data!(event_collection, properties)

headers = api_headers("async")
headers.merge({:Authorization => self.write_key})

deferrable = EventMachine::DefaultDeferrable.new

http_client = Keen::HTTP::Async.new(api_host, api_port, api_async_http_options)
http = http_client.post({
http = http_client.post(
:path => api_event_resource_path(event_collection),
:headers => headers,
:headers => api_headers(self.write_key, "async"),
:body => MultiJson.encode(properties)
})
)

if defined?(EM::Synchrony)
if http.error
Expand Down
5 changes: 1 addition & 4 deletions lib/keen/client/querying_methods.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

module Keen
class Client
module QueryingMethods
Expand Down Expand Up @@ -165,8 +164,6 @@ def query(query_name, event_collection, params)
ensure_project_id!
ensure_read_key!

params[:api_key] = self.read_key

if event_collection
params[:event_collection] = event_collection
end
Expand All @@ -177,7 +174,7 @@ def query(query_name, event_collection, params)
response = Keen::HTTP::Sync.new(
api_host, api_port, api_sync_http_options).get(
:path => "#{api_query_resource_path(query_name)}?#{query_params}",
:headers => api_headers("sync"))
:headers => api_headers(self.read_key, "sync"))
rescue Exception => http_error
raise HttpError.new("Couldn't perform #{query_name} on Keen IO: #{http_error.message}", http_error)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/keen/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Keen
VERSION = "0.6.1"
VERSION = "0.7.0"
end
18 changes: 14 additions & 4 deletions spec/keen/client/publishing_methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,18 @@

it "should raise an exception if client has no project_id" do
expect {
Keen::Client.new.publish(collection, event_properties)
}.to raise_error(Keen::ConfigurationError)
Keen::Client.new(
:write_key => "abcde"
).publish(collection, event_properties)
}.to raise_error(Keen::ConfigurationError, "Keen IO Exception: Project ID must be set")
end

it "should raise an exception if client has no write_key" do
expect {
Keen::Client.new(
:project_id => "12345"
).publish(collection, event_properties)
}.to raise_error(Keen::ConfigurationError, "Keen IO Exception: Write Key must be set for sending events")
end
end

Expand Down Expand Up @@ -88,7 +98,7 @@
EM.run {
client.publish_async("foo bar", event_properties).callback {
begin
expect_keen_post(api_event_resource_url("foo%20bar"), event_properties, "async")
expect_keen_post(api_event_resource_url("foo%20bar"), event_properties, "async", write_key)
ensure
EM.stop
end
Expand Down Expand Up @@ -169,7 +179,7 @@

describe "beacon_url" do
it "should return a url with a base-64 encoded json param" do
client = Keen::Client.new({:project_id => project_id, :write_key => write_key})
client = Keen::Client.new(:project_id => project_id, :write_key => write_key)
client.beacon_url("sign_ups", { :name => "Bob" }).should ==
"https://api.keen.io/3.0/projects/12345/events/sign_ups?api_key=#{write_key}&data=eyJuYW1lIjoiQm9iIn0="
end
Expand Down
20 changes: 10 additions & 10 deletions spec/keen/client/querying_methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,13 @@ def query_url(query_name, query_params)
it "should require a project id" do
expect {
Keen::Client.new(:read_key => read_key).count("users", {})
}.to raise_error(Keen::ConfigurationError)
}.to raise_error(Keen::ConfigurationError, "Keen IO Exception: Project ID must be set")
end

it "should require a read key" do
expect {
Keen::Client.new(:project_id => project_id).count("users", {})
}.to raise_error(Keen::ConfigurationError)
}.to raise_error(Keen::ConfigurationError, "Keen IO Exception: Read Key must be set for queries")
end
end

Expand All @@ -51,13 +51,13 @@ def query_url(query_name, query_params)
let(:api_response) { { "result" => 1 } }

def test_query(extra_query_params="", extra_query_hash={})
expected_query_params = "?api_key=#{read_key}&event_collection=#{event_collection}"
expected_query_params = "?event_collection=#{event_collection}"
expected_query_params += extra_query_params
expected_url = query_url(query_name, expected_query_params)
stub_keen_get(expected_url, 200, :result => 1)
response = query.call(query_name, event_collection, extra_query_hash)
response.should == api_response["result"]
expect_keen_get(expected_url, "sync")
expect_keen_get(expected_url, "sync", read_key)
end

it "should call the API w/ proper headers and return the processed json response" do
Expand Down Expand Up @@ -98,35 +98,35 @@ def test_query(extra_query_params="", extra_query_hash={})
end

it "should raise a failed responses" do
query_params = "?api_key=#{read_key}&event_collection=#{event_collection}"
query_params = "?event_collection=#{event_collection}"
url = query_url(query_name, query_params)

stub_keen_get(url, 401, :error => {})
expect {
query.call(query_name, event_collection, {})
}.to raise_error(Keen::AuthenticationError)
expect_keen_get(url, "sync")
expect_keen_get(url, "sync", read_key)
end
end
end

describe "#count" do
it "should not require params" do
query_params = "?api_key=#{read_key}&event_collection=#{event_collection}"
query_params = "?event_collection=#{event_collection}"
url = query_url("count", query_params)
stub_keen_get(url, 200, :result => 10)
client.count(event_collection).should == 10
expect_keen_get(url, "sync")
expect_keen_get(url, "sync", read_key)
end
end

describe "#extraction" do
it "should not require params" do
query_params = "?api_key=#{read_key}&event_collection=#{event_collection}"
query_params = "?event_collection=#{event_collection}"
url = query_url("extraction", query_params)
stub_keen_get(url, 200, :result => { "a" => 1 } )
client.extraction(event_collection).should == { "a" => 1 }
expect_keen_get(url, "sync")
expect_keen_get(url, "sync", read_key)
end
end
end
13 changes: 7 additions & 6 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,28 +23,29 @@ def stub_keen_get(url, status, response_body)
stub_keen_request(:get, url, status, MultiJson.encode(response_body))
end

def expect_keen_request(method, url, body, sync_or_async_ua, extra_headers={})
def expect_keen_request(method, url, body, sync_or_async_ua, read_or_write_key)
user_agent = "keen-gem, v#{Keen::VERSION}, #{sync_or_async_ua}"
user_agent += ", #{RUBY_VERSION}, #{RUBY_PLATFORM}, #{RUBY_PATCHLEVEL}"
if defined?(RUBY_ENGINE)
user_agent += ", #{RUBY_ENGINE}"
end

headers = { "Content-Type" => "application/json",
"User-Agent" => user_agent }
headers.merge(extra_headers)
"User-Agent" => user_agent,
"Authorization" => read_or_write_key }

WebMock.should have_requested(method, url).with(
:body => body,
:headers => headers)

end

def expect_keen_get(url, sync_or_async_ua)
expect_keen_request(:get, url, "", sync_or_async_ua)
def expect_keen_get(url, sync_or_async_ua, read_key)
expect_keen_request(:get, url, "", sync_or_async_ua, read_key)
end

def expect_keen_post(url, event_properties, sync_or_async_ua, write_key)
expect_keen_request(:post, url, MultiJson.encode(event_properties), sync_or_async_ua, {:Authorization => write_key})
expect_keen_request(:post, url, MultiJson.encode(event_properties), sync_or_async_ua, write_key)
end

def api_event_resource_url(collection)
Expand Down

0 comments on commit f485673

Please sign in to comment.