How to test google analytics (garb) API with Rspec? - ruby-on-rails

I'm using the garb gem to pull some basic stats, like pageviews, from Google Analytics. Everything's working correctly but I can't figure out the best way to test my API calls. Here's a paired down version of my Analytics class:
class Analytics
extend Garb::Model
metrics :pageviews
dimensions :page_path
Username = 'username'
Password = 'password'
WebPropertyId = 'XX-XXXXXXX-X'
# Start a session with google analytics.
#
Garb::Session.login(Username, Password)
# Find the correct web property.
#
Property = Garb::Management::Profile.all.detect {|p| p.web_property_id == WebPropertyId}
# Returns the nubmer of pageviews for a given page.
#
def self.pageviews(path)
Analytics.results(Property, :filters => {:page_path.eql => path}).first.pageviews.to_i
end
# ... a bunch of other methods to pull stats from google analytics ...
end
Pretty simple. But beyond ensuring that the constants are set, I haven't been able to write effective tests. What's the best way to test something like this? Here are some of the problems:
I'd prefer not to actually hit the API in my tests. It's slow and requires an internet connection.
The stats obviously change all the time, making it difficult to set an expectation even if I do hit the API when testing.
I think I want a mock class? But I've never used that pattern before. Any help would be awesome, even just some links to get me on the right path.

Fakeweb is a good place to start. It can isolate your SUT from the network so that slow connections don't affect your tests.
It's hard to know what else to say without knowing more about Garb. Obviously you'll need to know the format of the data to be sent and received from the API, so you can make the appropriate mocks/stubs.

I would suggest creating a testing interface that mimicks the actual calls to the google API. The other option would be to use mocks to create sample data.
I agree that it's best to not hit the actual API, since this does not gain you anything. A call to the actual API might succeed one day and fail the next because the API owners change the response format. Since GA probably won't change it's versioned API I think it's safe to create an interface that you can use in your test environments for faster testing.

Related

Should I order GraphQL results server side or client side?

We are using graphql-ruby in one of our internal projects: A Rails API backend serving a React Native Web frontend. I'm curious as to what is considered best practice in regard to handling ordering of returned results.
One option I see is that we provide both order_direction and field_to_order_by arguments, and the client must explicitly state each for the query (providing defaults as well, of course).
One way to handle this would be
if (sort_column = args[:sort_by])
if (direction = args[:direction])
users = users.order(sort_column.to_sym => direction.to_sym)
else
users = users.order(sort_column.to_sym) # default sort order
end
end
Another option of course would be to provide all results in a pre-defined direction (ASC or DESC) and have the client itself reorder. This seems very inefficient, however. As there's a real dearth of information on how to approach this, I'm curious what's considered best practice.
Any help appreciated!
As best practice ordering results should be made as far down the server as possible (database).
But, as I understand you're questioning between ordering results on the server side (GraphQL API) or on the frontend side (React Native Application), so:
I would recommend providing the client application with an option to get the results in a specific order and handle the sorting on the Server API, that way the client application should only display the results without the need of spending time processing them.

How do I hide my API calls / routes from users of my Rails app?

I'm writing an app that make some calls to my API that have restrictions. If users were to figure out what these url routes were and the proper parameters and how to specify them, then they could exploit it right?
For example if casting a vote on something and I only want users to be able to cast one vote, a user knowing the route:
get '/castvote/' => 'votemanager#castvote'
could be problematic, could it not? Is it easy to figure out these API routes?
Does anyone know any ways to remove the possibility of this happening?
There is no way to hide AJAX calls - if nothing else, one just needs to open Developer Tools - Network panel, and simply see what was sent. Everything on clientside is an open book, if you just know how to read it.
Instead, do validation on serverside: in your example, record the votes and users that cast them; if a vote was already recorded by that user, don't let them do it again.
Your API should have authorization built into it. Only authorized users having specific access scopes should be allowed to consume your API. Checkout Doorkeeper and cancancan gems provided by the rails community.
As others have said, adding access_tokens/username/password authorisation is a good place to start. Also, if your application should only allow one vote per user, then this should be validated by your application logic on the server
This is a broader problem. There's no way to stop users from figuring out how voting works and trying to game it but there are different techniques used to make it harder. I list some solutions from least to most effective here:
Using a nonce or proof of work, in case of Rails this is implemented through authenticity token for non-GET requests. This will require user to at least load the page before voting, therefore limiting scripted replay attacks
Recording IP address or other identifiable information (i.e. browser fingerprinting). This will limit number of votes from a single device
Requiring signup. This is what other answers suggest
Requiring third-party login (i.e. Facebook, Twitter)
Require payment to cast a vote (like in tv talent shows)
None of those methods is perfect and you can quickly come up with ways to trick any of them.
The real question is what your threat model and how hard you want it to make for users to cast fake votes. From my practical experience requiring third-party login will ensure most votes are valid in typical use cases.

How to dispaly a holding screen whilst ActiveJob retrieves lots of data from an external API

I have an application that makes API requests to salesforce using restforce.
Specifically the application finds a contact object, returns IDs for all related objects and then pulls the full record for every related object based on their ID.
This takes a long time for two reasons:
There are a lot of request to an external API, usually takes a few fractions of a second for each to reply and for some there can be +500 individual requests.
There is often a large amount of data being pulled back via each request.
All requests currently fall within the salesforce rest API limits but I'm getting timeout errors from my development server as it can take 5+ minutes for some of these requests to process.
Rails 4.2 - How best to handle this?
My question is how do I best get rails to handle this?
I can fire the API requests either from the controller (which definitely violates the skinny controllers) or from the view (via helper methods, which seems like a dodgy hack).
Ideally I'd like to get it running in a background job, but i'm unsure if I can just include all the authentication and other methods in a job in the same way I can include helper methods?
Even if I could get it to work in a background job, I'm unsure what best practice might be for the user experience. Ideally I'd like to route them to a page telling them to "hang tight, go get a coffee" with a progress bar, and then auto route them to the final page once the request is complete...
But I'm unsure how to generate a temporary display until a job has been completed?
Could anyone recommend any gems or strategies that might help me digest this problem?
You should definitely use a background job for this.
Give a database object to the job, which it will update to signal that is has finished, and maybe from time to time to indicate progress.
On the user side, simply tell them that the background job is working, with eventually a progress indicator, and display the result once the database object giving to the job tells you it's ready.

Structure of a rails app that uses third party API's

I'm new RoR and I can't seem to grasp how to structure my app.
I have an app that pulls data from Google Analytics using garb. After doing some number crunching with the data, the app will populate a Report model and display the report to the user.
Right now, I'm separating the Google Analytics logic using concerns. In my concerns folder, I have a GoogleAnalytics module that is responsible for pulling the data. The Report model includes the GoogleAnalytics module. Before the number crunching in the Report model takes place, I need to clean up and reformat the data. Should this be a responsibility of the GoogleAnalytics module or maybe a helper?
Is there a better practice for including third party services?
The reformatting should go on whatever is responsible for pulling the data from Google Analytics. None of the rest of your app should have to know the format of how Google Analytics returns it's data - the module should convert it into a sensible, standard interface and hide all of that from everyone else.
I would also strongly consider putting this stuff into a service object rather than a module. Including modules gets messy because you when you call a method on an object you don't know where that method is defined. I would only use this pattern if you were including the same module in lots of other models and it was a true DRY play.
A service object would look something like (depending on what params you need to use to pull the data):
class GoogleAnalyticsDataFetcher
attr_accessor :data
def new ga_id
#ga_id = ga_id
end
def fetch
#data = do_some_stuff
end
end
and then you could call it either from your controller or wrap it up inside the Report model somewhere. Then when you go GoogleAnalyticsDataFetcher.new(id).fetch it's incredibly obvious what is going on and where it is defined.

Using mixpanel to build custom analytics dashboard for users

I love graphs.
I'd love to get my hands on some data and make it look pretty. But alas, I'm a little lost on what would be considered best practice.
I've selected mixpanel (only as an example) as I seems wonderfully easy to track custom events, and doesn't have any subdomain limitation like Google Analytics.
Say I had 100-1000+ users who have an account (which is publicly facing), and I'm currently tracking the public interactions their pages get. With mixpanel, I can see the data which is lovely, and I've segmented it to individual accounts. So far, so good!
But then, I want to show my users this information. And here my head begins to hurt. Do I schedule a cron jobs, pulling in the data from mixpanel and writing it to their respective accounts? Or is there a better way? I've looked into mixpanel's api (I'm using Ruby), but they keep telling me I should use the javascript api. But in using JS, how does one prevent others getting the data (ie. what's stopping someone faking mixpanel api-posts in the console, or viewing my private key?).
What would you consider a practical solution in such a case?
You can achieve this by storing the user specific events of each user with a $bucket property attached which has a value unique to each user as explained in the mixpanel docs here Mixpanel docs. If you want to still use ruby to serve the events, have a look at Mixpanel's recommended ruby client libraries
mixpanel_client looks like the much maintained option of the 2 mentioned. If you go with that then you can serve user specific events as shown in the example below(which is also in the gem's readme):
data = client.request do
# Available options
resource 'events/properties'
event '["test-event"]'
name 'hello'
values '["uno", "dos"]'
timezone '-8'
type 'general'
unit 'hour'
interval 24
limit 5
bucket 'contents'
from_date '2011-08-11'
to_date '2011-08-12'
on 'properties["product_id"]'
where '1 in properties["product_id"]'
buckets '5'
end
You could try a service like Keen IO that will allow you to generate encrypted scoped write and read API keys. Keen IO is built for customizable and programmatic analytics features such as exposing analytics to your customers, where as MixPanel is more for exploring your data in their UI. The idea with an encrypted scoped key is they will never be able to access your account, only the data you want them to see. You could easily tag your events with a customer ID and then use the Scoped Keys to ensure that you only ever show customers their own data.
https://keen.io/docs/security/#scoped-key
Also, Keen IO has an "importer" which allows you to export your mixpanel events into your Keen IO database.

Resources