Class: LinkIO::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/linkio/client.rb

Overview

The core deep linking engine. Framework-agnostic: it consumes a Request and returns a Result, and exposes plain methods for pending links and referrals. This is the Ruby port of the Node.js LinkIO class.

Constant Summary collapse

PENDING_TTL_MS =

7 days, in milliseconds.

7 * 24 * 60 * 60 * 1000

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config = nil, **attributes) ⇒ Client

Returns a new instance of Client.

Parameters:



16
17
18
19
20
21
22
23
24
25
26
# File 'lib/linkio/client.rb', line 16

def initialize(config = nil, **attributes)
  @config =
    if config.is_a?(Configuration)
      config
    elsif config.is_a?(Hash)
      Configuration.new(config)
    else
      Configuration.new(attributes)
    end
  @config.validate!
end

Instance Attribute Details

#configLinkIO::Configuration (readonly)



13
14
15
# File 'lib/linkio/client.rb', line 13

def config
  @config
end

Instance Method Details

#apple_app_site_associationHash

Returns the apple-app-site-association document (one detail entry per configured app target).

Returns:

  • (Hash)

    the apple-app-site-association document (one detail entry per configured app target)



116
117
118
119
120
121
122
# File 'lib/linkio/client.rb', line 116

def apple_app_site_association
  details = config.apps.values.map do |app|
    { "appID" => app.apple_app_id, "paths" => ["*"] }
  end

  { "applinks" => { "apps" => [], "details" => details } }
end

Returns the assetlinks.json document (one entry per app-target/fingerprint pair).

Returns:

  • (Array<Hash>)

    the assetlinks.json document (one entry per app-target/fingerprint pair)



126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/linkio/client.rb', line 126

def asset_links
  config.apps.values.flat_map do |app|
    app.android_sha256_fingerprints.map do |fingerprint|
      {
        "relation" => ["delegate_permission/common.handle_all_urls"],
        "target" => {
          "namespace" => "android_app",
          "package_name" => app.android_package_name,
          "sha256_cert_fingerprints" => [fingerprint]
        }
      }
    end
  end
end

Handle an incoming deep link request. Selects the app target for the request's role (see LinkIO::Configuration#role_param), stores a pending link (by IP fingerprint, and by device id when present) for deferred deep linking, then returns the appropriate response for the detected platform.

On mobile, it renders a smart-redirect page that opens the role's app and falls back to that role's store (or the role's fallback_url if set). On web it redirects to the role's fallback_url when configured, otherwise returns a JSON message.

Parameters:

Returns:



40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/linkio/client.rb', line 40

def handle_deep_link(request)
  platform = Platform.detect(request.user_agent)
  params = request.params
  app = config.app_for(params[config.role_param])

  persist_pending_link(request, params)

  case platform
  when Platform::IOS, Platform::ANDROID
    platform_result(app: app, params: params, platform: platform)
  else
    web_result(app, params)
  end
end

Retrieve (and consume) a pending link by device id.

Parameters:

  • device_id (String)

Returns:



59
60
61
62
63
64
65
# File 'lib/linkio/client.rb', line 59

def pending_link(device_id)
  data = config.storage.get_pending_link(device_id)
  return nil if data.nil?

  config.storage.delete_pending_link(device_id)
  DeepLinkData.new(url: data.url, params: data.params, deferred: true)
end

Retrieve (and consume) a pending link by client IP fingerprint. Used for deferred deep linking after app install. Uses IP-only matching so it works even when the browser and app send different User-Agents.

Parameters:

  • ip (String)
  • _user_agent (String, nil) (defaults to: nil)

    accepted for API parity; unused

Returns:



74
75
76
77
78
79
80
81
# File 'lib/linkio/client.rb', line 74

def pending_link_by_fingerprint(ip, _user_agent = nil)
  fingerprint = Utils.generate_ip_fingerprint(ip)
  data = config.storage.get_pending_link_by_fingerprint(fingerprint)
  return nil if data.nil?

  config.storage.delete_pending_link_by_fingerprint(fingerprint)
  DeepLinkData.new(url: data.url, params: data.params, deferred: true)
end

#referral_for_user(user_id) ⇒ LinkIO::ReferralData?

Parameters:

  • user_id (String)

Returns:



110
111
112
# File 'lib/linkio/client.rb', line 110

def referral_for_user(user_id)
  config.storage.get_referral_by_referee(user_id)
end

#referrals(referrer_id) ⇒ Array<LinkIO::ReferralData>

Parameters:

  • referrer_id (String)

Returns:



104
105
106
# File 'lib/linkio/client.rb', line 104

def referrals(referrer_id)
  config.storage.get_referrals_by_referrer(referrer_id)
end

#track_referral(referral_code, referee_id, metadata = nil) ⇒ void

This method returns an undefined value.

Record a referral. The referral code doubles as the referrer id, matching the Node.js backend.

Parameters:

  • referral_code (String)
  • referee_id (String)
  • metadata (Hash, nil) (defaults to: nil)


90
91
92
93
94
95
96
97
98
99
100
# File 'lib/linkio/client.rb', line 90

def track_referral(referral_code, referee_id,  = nil)
  referral = ReferralData.new(
    referrer_id: referral_code,
    referee_id: referee_id,
    referral_code: referral_code,
    timestamp: now_ms,
    metadata: 
  )
  config.storage.save_referral(referral)
  nil
end

#well_known(path) ⇒ LinkIO::Result

Serve an /.well-known/* verification file.

Parameters:

  • path (String)

    the request path

Returns:



145
146
147
148
149
150
151
152
153
154
# File 'lib/linkio/client.rb', line 145

def well_known(path)
  case path
  when "/.well-known/apple-app-site-association"
    Result.json(apple_app_site_association)
  when "/.well-known/assetlinks.json"
    Result.json(asset_links)
  else
    Result.new(type: :html, status: 404, body: "Not Found", headers: { "content-type" => "text/plain" })
  end
end