forked from gitcoinco/code_fund_ads
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: ✨ Conversion Pixel (gitcoinco#1310)
- Loading branch information
Eric Berry
authored
Jun 11, 2020
1 parent
bde75e7
commit 2fd3e8c
Showing
48 changed files
with
1,161 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
class PixelConversionsController < ApplicationController | ||
include Untrackable | ||
|
||
skip_before_action :verify_authenticity_token | ||
before_action :set_cors_headers | ||
before_action :set_no_caching_headers | ||
|
||
def create | ||
CreatePixelConversionJob.perform_later pixel_conversion_params.to_h | ||
head :accepted | ||
end | ||
|
||
private | ||
|
||
def pixel_conversion_params | ||
params.permit(:pixel_id, :impression_id, :test, metadata: {}).tap do |whitelisted| | ||
whitelisted[:conversion_referrer] = request.referrer | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
class PixelsController < ApplicationController | ||
before_action :authenticate_user! | ||
before_action :set_organization | ||
before_action :set_pixel, only: [:show, :edit, :update, :destroy] | ||
before_action :authorize_edit!, except: [:index] | ||
|
||
def index | ||
pixels = @organization.pixels.order(name: :asc) | ||
@pagy, @pixels = pagy(pixels) | ||
end | ||
|
||
def new | ||
@pixel = @organization.pixels.build(user: current_user) | ||
end | ||
|
||
def create | ||
@pixel = @organization.pixels.build(pixel_params) | ||
|
||
respond_to do |format| | ||
if @pixel.save | ||
format.html { redirect_to pixels_path(@organization), notice: "Pixel was successfully created" } | ||
format.json { render :show, status: :created, location: pixel_path(@organization, @pixel) } | ||
else | ||
format.html { render :new } | ||
format.json { render json: @pixel.errors, status: :unprocessable_entity } | ||
end | ||
end | ||
end | ||
|
||
def update | ||
@pixel.update(pixel_params) | ||
|
||
respond_to do |format| | ||
if @pixel.save | ||
format.html { redirect_to pixel_path(@organization, @pixel), notice: "Pixel was successfully updated." } | ||
format.json { render :show, status: :ok, location: pixel_path(@organization, @pixel) } | ||
else | ||
format.html { render :edit } | ||
format.json { render json: @pixel.errors, status: :unprocessable_entity } | ||
end | ||
end | ||
end | ||
|
||
def destroy | ||
respond_to do |format| | ||
if @pixel.destroy | ||
format.html { redirect_to pixels_path(@organization), notice: "Pixel was deleted successfully" } | ||
format.json { head :no_content } | ||
else | ||
format.html { redirect_to pixels_path(@organization), notice: @organization.errors.messages.to_s } | ||
format.json { render json: @organization_user.errors, status: :unprocessable_entity } | ||
end | ||
end | ||
end | ||
|
||
private | ||
|
||
def authorize_edit! | ||
unless authorized_user.can_edit_organization_users?(@organization) | ||
redirect_to organization_users_path(@organization), notice: "You do not have permission to update membership settings." | ||
end | ||
end | ||
|
||
def set_organization | ||
@organization = Current.organization | ||
end | ||
|
||
def set_pixel | ||
@pixel = @organization.pixels.find(params[:id]) | ||
end | ||
|
||
def pixel_params | ||
params.require(:pixel).permit(:description, :name, :value, :user_id).tap do |whitelisted| | ||
whitelisted[:user_id] = params[:pixel][:user_id] if authorized_user.can_admin_system? | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
module OrganizationPixelsHelper | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
module PixelConversionsHelper | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import ConversionTracker from '../src/conversion-tracker' | ||
|
||
window.CodeFund = new ConversionTracker(window.CodeFundConfig || {}) | ||
|
||
// Example | ||
// CodeFund.recordConversion('12345') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
export default class { | ||
constructor (config = {}) { | ||
const defaultConfig = { | ||
baseUrl: 'https://codefund.io', | ||
daysToLive: 30, | ||
localStorageKey: 'CodeFund.utm_impression', | ||
successStatuses: [200, 202] | ||
} | ||
const customConfig = config || {} | ||
this.config = { ...defaultConfig, ...customConfig } | ||
this.impressionId = this.urlParams.get('utm_impression') | ||
} | ||
|
||
// Notifies CodeFund of the pixelId being converted for the saved impression | ||
// TODO: update to use POST exclusively and support metadata | ||
recordConversion (pixelId, options = { test: false, metadata: {} }) { | ||
const { test, metadata } = options | ||
const url = `${this.baseUrl}/pixels/${pixelId}/impressions/${this.impressionId}?test=${test}` | ||
var xhr = new XMLHttpRequest() | ||
xhr.onreadystatechange = () => { | ||
if (xhr.readyState === 4) { | ||
if (!this.successStatuses.include(xhr.status)) | ||
console.log('CodeFund failed to record the conversion!', xhr.status) | ||
} | ||
} | ||
xhr.open('GET', url) | ||
xhr.send() | ||
} | ||
|
||
// Indicates if the passed date (represented as a string from localStorage) has expired based on daysToLive | ||
expired (createdAtDateString) { | ||
if (!createdAtDateString) return true | ||
const createdAt = new Date(Date.parse(createdAtDateString)) // 'Tue Jun 09 2020 14:33:59 GMT-0400 (EDT)' | ||
if (createdAt.getTime() !== createdAt.getTime()) return true // invalid date, getTime returned NaN but NaN never equals itself | ||
const expiresAt = new Date(createdAt.getTime()) | ||
expiresAt.setDate(expiresAt.getDate() + this.daysToLive) | ||
const today = new Date() | ||
return today > expiresAt | ||
} | ||
|
||
// Saves the impressionId to localStorage | ||
set impressionId (id) { | ||
if (!id) return localStorage.removeItem(this.localStorageKey) | ||
if (this.impressionId) return | ||
try { | ||
const createdAt = new Date() | ||
const data = { id, createdAt } | ||
return localStorage.setItem(this.localStorageKey, JSON.stringify(data)) | ||
} catch (ex) { | ||
console.log( | ||
`CodeFund failed to write the utm_impression id to localStorage! ${ex.message}` | ||
) | ||
} | ||
} | ||
|
||
// Fetches the impressionId from localStorage | ||
get impressionId () { | ||
try { | ||
const rawData = localStorage.getItem(this.localStorageKey) | ||
const data = JSON.parse(rawData) || {} | ||
const { id, createdAt } = data | ||
if (!this.expired(createdAt)) return id | ||
localStorage.removeItem(this.localStorageKey) | ||
} catch (ex) { | ||
console.log( | ||
`CodeFund failed to read the utm_impression value from localStorage! ${ex.message}` | ||
) | ||
} | ||
return null | ||
} | ||
|
||
get urlParams () { | ||
return new URL(window.location).searchParams | ||
} | ||
|
||
get baseUrl () { | ||
return this.config.baseUrl | ||
} | ||
|
||
get daysToLive () { | ||
return this.config.daysToLive | ||
} | ||
|
||
get localStorageKey () { | ||
return this.config.localStorageKey | ||
} | ||
|
||
get successStatuses () { | ||
return this.config.successStatuses | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
class CreatePixelConversionJob < ApplicationJob | ||
queue_as :default | ||
|
||
def perform(params = {}) | ||
ScoutApm::Transaction.ignore! if rand > (ENV["SCOUT_SAMPLE_RATE"] || 1).to_f | ||
|
||
Pixel.find_by(id: params[:pixel_id])&.record_conversion( | ||
params[:impression_id], | ||
conversion_referrer: params[:conversion_referrer], | ||
test: params[:test], | ||
metadata: params[:metadata] || {} | ||
) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.