recapi 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +8 -7
- data/lib/recapi.rb +25 -7
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28f52d6426fd83d0b14d106535a06fca9e6d29ac56b4a3b366ae31f8ca7e22df
|
|
4
|
+
data.tar.gz: 93361e6a7d6dcbbc86be19b92ccc7fb4bc3b3695f99fa701ad08cfea17ccb57f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 54a7bf72a2b956ed847fe57af69c3241af18c638a199a86aa3220b2f0726d495402a3faa4698e011f815f57881e5969090bf9047b28b60bc0b6d11767f88eacf
|
|
7
|
+
data.tar.gz: a7825355e8be66451b687bc342a8c9961f8b7f8bc4c04fb7c57a38442ab59ed12daf67be7b04a39cba4b7a76843c5faec071e35dbcc15a5b3a1aba6f5154ae4b
|
data/README.md
CHANGED
|
@@ -30,7 +30,7 @@ line with your address): save it as `lib/recapi.rb` and
|
|
|
30
30
|
`require Rails.root.join("lib/recapi").to_s` instead.
|
|
31
31
|
|
|
32
32
|
```sh
|
|
33
|
-
curl -fLo lib/recapi.rb https://api.<your-domain>/sdk/ruby/recapi-0.1.
|
|
33
|
+
curl -fLo lib/recapi.rb https://api.<your-domain>/sdk/ruby/recapi-0.1.9.rb
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
## Quick start
|
|
@@ -62,8 +62,9 @@ Ids may be strings or integers (an ActiveRecord id): they travel as strings.
|
|
|
62
62
|
|
|
63
63
|
## Calls
|
|
64
64
|
|
|
65
|
-
A refused argument (an invalid id or level) raises `ArgumentError`,
|
|
66
|
-
|
|
65
|
+
A refused argument (an invalid id or level) raises `ArgumentError`, except an
|
|
66
|
+
`item_id` or `context_item_id` the API would refuse, as a page often takes those
|
|
67
|
+
from a visitor's URL (see the table). An API failure is a `Recapi::Error` (`code`, `status`, `retry_after`, `retryable?`,
|
|
67
68
|
`outage?`). `recommend` and `popular` still raise on a bad request (400, 404,
|
|
68
69
|
413, other 3xx/4xx) or a reply that isn't the API's; an outage, a limit or a
|
|
69
70
|
refused key can start on a live site with no deploy, so they answer with a list
|
|
@@ -71,10 +72,10 @@ and a log line.
|
|
|
71
72
|
|
|
72
73
|
| Call | What it does |
|
|
73
74
|
|---|---|
|
|
74
|
-
| `track(item_id, level, user_id:, anon_id:, recommendation_id:)` | Records an interaction: `level` -10 to 10 or `:view` (3), `:cart` (5), `:purchase` (10), `:reject` (-10: never recommend the item to this user again until a positive event on it). The history changes at once; a background thread sends the event within 2 s (at once when a batch of 1,000 is full). |
|
|
75
|
+
| `track(item_id, level, user_id:, anon_id:, recommendation_id:)` | Records an interaction: `level` -10 to 10 or `:view` (3), `:cart` (5), `:purchase` (10), `:reject` (-10: never recommend the item to this user again until a positive event on it). The history changes at once; a background thread sends the event within 2 s (at once when a batch of 1,000 is full). An `item_id` the API would refuse (1-256 bytes of UTF-8, no control character; also `nil`, an Array or a Hash) drops the event with a log line: nothing raises, and the other events are sent as usual. A bad `level`, `user_id`, `anon_id` or `recommendation_id` still raises `ArgumentError`. |
|
|
75
76
|
| `flush` | Sends the queued events now, with retries (end of a job). A network error, 429 or 5xx is retried 3 times (0.5 s, 1 s, 2 s with jitter, or the `Retry-After`, at most 8 s), then the events stay queued and the sender backs off up to a minute; any other error drops them (logged). |
|
|
76
77
|
| `close` | Stops the sender and sends what is queued, one try per batch. It runs by itself at exit. |
|
|
77
|
-
| `recommend(user_id:, anon_id:, count:, placement:, tags_any:, tags_all:, exclude:, context_item_id:, discovery:)` | A `Recapi::Recommendations` (`recommendation_id`, `fallback?`, `items`). At most 1.5 s; on a network error, timeout, 5xx or 429 it returns popular items (`fallback?`, no id), or no item when those fail too: hide the slot; on a 401 or 403 no item at once. Any other failure (a bad request, a reply that isn't the API's) raises. |
|
|
78
|
+
| `recommend(user_id:, anon_id:, count:, placement:, tags_any:, tags_all:, exclude:, context_item_id:, discovery:)` | A `Recapi::Recommendations` (`recommendation_id`, `fallback?`, `items`). At most 1.5 s; on a network error, timeout, 5xx or 429 it returns popular items (`fallback?`, no id), or no item when those fail too: hide the slot; on a 401 or 403 no item at once. A `context_item_id` the API would refuse (also an Array or a Hash, as `params[:id]` gives for `?id[]=a`) isn't sent: you get the same popular list and a log line, with no recommendation request. Any other failure (a bad request, a reply that isn't the API's) raises. |
|
|
78
79
|
| `identify(anon_id, user_id)` | At login: the visitor's history and shown items become the user's. The user's first restore from recapi then leaves that visitor's browsing out, so it doesn't count twice. |
|
|
79
80
|
| `popular(count:, tags_any:, tags_all:)` | Popular items, for pages without a user. On a network error, timeout, 5xx, 429, 401 or 403: an empty list and a log line. Other failures as in `recommend`. |
|
|
80
81
|
| `upsert_items(items)` | Creates or replaces items (hashes of `item_id`, `title`, `tags`, `metadata`…), 1,000 per request, retried like events. Returns how many were stored. |
|
|
@@ -97,8 +98,8 @@ queue and sender at its first call. Proxies come from `HTTPS_PROXY`,
|
|
|
97
98
|
`HTTP_PROXY` and `NO_PROXY`, as `Net::HTTP` reads them. Failures are logged
|
|
98
99
|
to `logger:`, anything with `warn` and `error` (`Rails.logger`, a
|
|
99
100
|
`Logger`); without one they go to `$stderr` as `recapi: …` lines, with lost
|
|
100
|
-
events (a refused batch, rejected events) at most one line a
|
|
101
|
-
error code and for
|
|
101
|
+
events (a refused batch, rejected events, a dropped `item_id`) at most one line a
|
|
102
|
+
minute for each error code, for rejected events and for dropped ids, and `recommend`'s and `popular`'s failures
|
|
102
103
|
at most one line a minute for each error code, so a revoked key on a busy server
|
|
103
104
|
doesn't flood the log, and a full queue one line until a send works. A failed write to
|
|
104
105
|
`$stderr` (a full disk, a closed stream) is ignored. A logger you give gets
|
data/lib/recapi.rb
CHANGED
|
@@ -25,7 +25,7 @@ rescue LoadError
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
module Recapi
|
|
28
|
-
VERSION = "0.1.
|
|
28
|
+
VERSION = "0.1.9"
|
|
29
29
|
|
|
30
30
|
# Decayed user history, exactly as docs/history-spec.md (times in unix ms).
|
|
31
31
|
# A port of sdk/python's history.py, snapshot merging included.
|
|
@@ -487,14 +487,22 @@ module Recapi
|
|
|
487
487
|
# is queued. level is an integer -10 to 10 or :view (3), :cart (5),
|
|
488
488
|
# :purchase (10), :reject (-10: never recommend the item to this user
|
|
489
489
|
# again until a positive event on it). recommendation_id overrides the
|
|
490
|
-
# automatic attribution. Ids may be strings or integers.
|
|
490
|
+
# automatic attribution. Ids may be strings or integers. An item_id the API
|
|
491
|
+
# would refuse raises nothing: a page often takes it from a visitor's URL, so
|
|
492
|
+
# the event is dropped and logged instead.
|
|
491
493
|
def track(item_id, level, user_id: nil, anon_id: nil, recommendation_id: nil)
|
|
492
494
|
level = level_value(level)
|
|
493
|
-
item_id = id_string(item_id)
|
|
494
|
-
raise ArgumentError, "item_id #{ID_RULE}" unless History.id?(item_id)
|
|
495
|
-
|
|
496
495
|
user_id, anon_id, recommendation_id = check_ids(user_id: user_id, anon_id: anon_id,
|
|
497
496
|
recommendation_id: recommendation_id)
|
|
497
|
+
item_id = id_string(item_id)
|
|
498
|
+
# A crafted link must not fail the page. Nothing is kept: in the history it
|
|
499
|
+
# would make the API refuse every later list. The line never quotes the
|
|
500
|
+
# visitor's id.
|
|
501
|
+
unless History.id?(item_id)
|
|
502
|
+
log_lost(:item_id, "recapi: event dropped: item_id #{ID_RULE}")
|
|
503
|
+
return nil
|
|
504
|
+
end
|
|
505
|
+
|
|
498
506
|
t = now_ms
|
|
499
507
|
attributed = recommendation_id
|
|
500
508
|
key = key_for(user_id, anon_id)
|
|
@@ -554,8 +562,11 @@ module Recapi
|
|
|
554
562
|
# no id) and logs the error. If those fail too (recapi is down, or the plan's
|
|
555
563
|
# limit covers them), the list is empty: hide the slot. On a 401 or 403 (a
|
|
556
564
|
# revoked key, a suspended shop) it is empty at once, as popular would be
|
|
557
|
-
# refused too.
|
|
558
|
-
#
|
|
565
|
+
# refused too. A context_item_id the API would refuse (a page often takes it
|
|
566
|
+
# from a visitor's URL: Rails' params[:id] can be an Array or a Hash) is never
|
|
567
|
+
# sent: the answer is the same popular list. Any other failure raises: a bad
|
|
568
|
+
# request or a reply that isn't the API's can't start on a live site without
|
|
569
|
+
# a deploy.
|
|
559
570
|
def recommend(user_id: nil, anon_id: nil, count: 0, placement: nil, tags_any: nil, tags_all: nil,
|
|
560
571
|
exclude: nil, context_item_id: nil, discovery: nil)
|
|
561
572
|
deadline = monotonic + recommend_timeout
|
|
@@ -565,6 +576,13 @@ module Recapi
|
|
|
565
576
|
key = key_for(user_id, anon_id)
|
|
566
577
|
t = now_ms
|
|
567
578
|
state = key.nil? ? UserState.new : load(key)
|
|
579
|
+
unless context_item_id.nil? || context_item_id == "" || History.id?(context_item_id)
|
|
580
|
+
_, _, rejected = state.history.wire(t)
|
|
581
|
+
# The message never quotes the id: a visitor may have written it. nil:
|
|
582
|
+
# not skipped in the list either, as no item can have this id.
|
|
583
|
+
cause = Error.new("invalid_request", "context_item_id #{ID_RULE}")
|
|
584
|
+
return fallback(cause, count, with_rejected(exclude, rejected), nil, tags_any, tags_all)
|
|
585
|
+
end
|
|
568
586
|
if user_id && !state.restored && !state.history.empty? && !@api_key.start_with?(PUBLIC_KEY_PREFIX)
|
|
569
587
|
state = restore(key, user_id, state, deadline, t)
|
|
570
588
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: recapi
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- recapi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-11-
|
|
11
|
+
date: 2026-11-05 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'Ruby client for the recapi recommendation API: per-user history, event
|
|
14
14
|
batching, retries, popular fallback. Standard library only.'
|