Skip to content

Commit 739dd11

Browse files
committed
add moar mizzing tez
1 parent 2edd596 commit 739dd11

15 files changed

Lines changed: 390 additions & 59 deletions

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ jobs:
6565
-p 8108:8108 \
6666
--name typesense \
6767
-v /tmp/typesense-data:/data \
68+
-v /tmp/typesense-analytics-data:/analytics-data \
6869
typesense/typesense:${{ matrix.typesense}} \
6970
--api-key=xyz \
7071
--data-dir=/data \

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ open_api_typesense-*.tar
3030

3131
# Typesense local dev data folder.
3232
/typesense-data
33+
/typesense-analytics-data
3334

3435
# Misc.
3536
.elixir_ls

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@ services:
88
- 8108:8108 # typesense server
99
volumes:
1010
- ./typesense-data:/data
11+
- ./typesense-analytics-data:/analytics-data
1112
command: |
1213
--data-dir=/data
1314
--api-key=xyz
1415
--enable-search-analytics=true
1516
--analytics-dir=/analytics-data
1617
--analytics-flush-interval=60
18+
--analytics-minute-rate-limit=100
1719
--enable-cors
1820
typesense_dashboard:
1921
image: ghcr.io/bfritscher/typesense-dashboard:1.9.3

lib/open_api_typesense/operations/analytics.ex

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ defmodule OpenApiTypesense.Analytics do
7474
request: [{"application/json", {OpenApiTypesense.AnalyticsRuleSchema, :t}}],
7575
response: [
7676
{201, {OpenApiTypesense.AnalyticsRuleSchema, :t}},
77-
{400, {OpenApiTypesense.ApiResponse, :t}}
77+
{400, {OpenApiTypesense.ApiResponse, :t}},
78+
{404, {OpenApiTypesense.ApiResponse, :t}}
7879
],
7980
opts: opts
8081
})

priv/open_api.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1428,6 +1428,12 @@ paths:
14281428
application/json:
14291429
schema:
14301430
$ref: "#/components/schemas/ApiResponse"
1431+
'404':
1432+
description: Collection or field missing
1433+
content:
1434+
application/json:
1435+
schema:
1436+
$ref: "#/components/schemas/ApiResponse"
14311437
get:
14321438
tags:
14331439
- analytics

test/operations/analytics_test.exs

Lines changed: 276 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,285 @@ defmodule AnalyticsTest do
22
use ExUnit.Case, async: true
33

44
alias OpenApiTypesense.Analytics
5+
alias OpenApiTypesense.AnalyticsRuleSchema
56
alias OpenApiTypesense.AnalyticsRulesRetrieveSchema
7+
alias OpenApiTypesense.ApiResponse
8+
alias OpenApiTypesense.Collections
9+
alias OpenApiTypesense.CollectionResponse
10+
alias OpenApiTypesense.AnalyticsEventCreateResponse
11+
12+
setup_all do
13+
recipe_name = "recipes"
14+
15+
recipe_schema =
16+
%{
17+
name: recipe_name,
18+
fields: [
19+
%{"name" => "recipe_name", "type" => "string"},
20+
%{"name" => "#{recipe_name}_id", "type" => "int32"},
21+
%{"name" => "description", "type" => "string"}
22+
],
23+
default_sorting_field: "#{recipe_name}_id"
24+
}
25+
|> Jason.encode_to_iodata!()
26+
27+
product_name = "products"
28+
29+
product_schema =
30+
%{
31+
name: product_name,
32+
fields: [
33+
%{"name" => "product_name", "type" => "string"},
34+
%{"name" => "#{product_name}_id", "type" => "int32"},
35+
%{"name" => "description", "type" => "string"},
36+
%{"name" => "title", "type" => "string"},
37+
%{"name" => "popularity", "type" => "int32", "optional" => true}
38+
],
39+
default_sorting_field: "#{product_name}_id"
40+
}
41+
|> Jason.encode_to_iodata!()
42+
43+
product_queries_name = "product_queries"
44+
45+
product_queries_schema =
46+
%{
47+
"name" => product_queries_name,
48+
"fields" => [
49+
%{"name" => "q", "type" => "string"},
50+
%{"name" => "count", "type" => "int32"},
51+
%{"name" => "downloads", "type" => "int32", "optional" => true}
52+
]
53+
}
54+
|> Jason.encode_to_iodata!()
55+
56+
nohits_queries_name = "no_hits_queries"
57+
58+
nohits_queries_schema =
59+
%{
60+
"name" => nohits_queries_name,
61+
"fields" => [
62+
%{"name" => "q", "type" => "string"},
63+
%{"name" => "count", "type" => "int32"}
64+
]
65+
}
66+
|> Jason.encode_to_iodata!()
67+
68+
recipe_nohits_queries_name = "recipe_no_hits_queries"
69+
70+
recipe_nohits_queries_schema =
71+
%{
72+
"name" => recipe_nohits_queries_name,
73+
"fields" => [
74+
%{"name" => "q", "type" => "string"},
75+
%{"name" => "count", "type" => "int32"}
76+
]
77+
}
78+
|> Jason.encode_to_iodata!()
79+
80+
[
81+
recipe_schema,
82+
product_schema,
83+
product_queries_schema,
84+
nohits_queries_schema,
85+
recipe_nohits_queries_schema
86+
]
87+
|> Enum.map(fn schema ->
88+
Collections.create_collection(schema)
89+
end)
90+
91+
on_exit(fn ->
92+
{:ok, %CollectionResponse{name: ^recipe_name}} =
93+
Collections.delete_collection(recipe_name)
94+
95+
{:ok, %CollectionResponse{name: ^product_name}} =
96+
Collections.delete_collection(product_name)
97+
98+
{:ok, %CollectionResponse{name: ^product_queries_name}} =
99+
Collections.delete_collection(product_queries_name)
100+
101+
{:ok, %CollectionResponse{name: ^nohits_queries_name}} =
102+
Collections.delete_collection(nohits_queries_name)
103+
104+
{:ok, %CollectionResponse{name: ^recipe_nohits_queries_name}} =
105+
Collections.delete_collection(recipe_nohits_queries_name)
106+
107+
{:ok, %AnalyticsRulesRetrieveSchema{rules: rules}} = Analytics.retrieve_analytics_rules()
108+
Enum.map(rules, &Analytics.delete_analytics_rule(&1.name))
109+
end)
110+
111+
:ok
112+
end
113+
114+
@tag ["27.1": true, "26.0": true, "0.25.2": true]
115+
test "error: create analytics rule with non-existent collection" do
116+
name = "products_missing_query"
117+
collection_name = "non_existent_collection"
118+
119+
body =
120+
%{
121+
"name" => name,
122+
"type" => "counter",
123+
"params" => %{
124+
"source" => %{
125+
"collections" => ["products"],
126+
"events" => [
127+
%{"type" => "click", "weight" => 1, "name" => "products_downloads_event"}
128+
]
129+
},
130+
"destination" => %{
131+
"collection" => collection_name,
132+
"counter_field" => "downloads"
133+
}
134+
}
135+
}
136+
|> Jason.encode_to_iodata!()
137+
138+
message = "Collection `#{collection_name}` not found."
139+
assert {:error, %ApiResponse{message: ^message}} = Analytics.create_analytics_rule(body)
140+
end
141+
142+
@tag ["27.1": true, "26.0": true, "0.25.2": true]
143+
test "success: upsert analytics rule" do
144+
name = "another_product_no_hits"
145+
146+
body =
147+
%{
148+
"type" => "nohits_queries",
149+
"params" => %{
150+
"source" => %{
151+
"collections" => ["recipes"]
152+
},
153+
"destination" => %{
154+
"collection" => "recipe_no_hits_queries"
155+
},
156+
"limit" => 1_000
157+
}
158+
}
159+
|> Jason.encode_to_iodata!()
160+
161+
assert {:ok, %AnalyticsRuleSchema{name: ^name}} = Analytics.upsert_analytics_rule(name, body)
162+
end
163+
164+
@tag ["27.1": true, "26.0": true, "0.25.2": true]
165+
test "error: create analytics rule with wrong field" do
166+
name = "products_test_query"
167+
field_name = "wrong_field"
168+
169+
body =
170+
%{
171+
"name" => name,
172+
"type" => "counter",
173+
"params" => %{
174+
"source" => %{
175+
"collections" => ["products"],
176+
"events" => [
177+
%{"type" => "click", "weight" => 1, "name" => "products_downloads_event"}
178+
]
179+
},
180+
"destination" => %{
181+
"collection" => "product_queries",
182+
"counter_field" => field_name
183+
}
184+
}
185+
}
186+
|> Jason.encode_to_iodata!()
187+
188+
assert {:error, %ApiResponse{message: _}} = Analytics.create_analytics_rule(body)
189+
end
6190

7191
@tag ["27.1": true, "26.0": true, "0.25.2": true]
8192
test "success: list analytics rules" do
9-
assert {:ok, %AnalyticsRulesRetrieveSchema{}} = Analytics.retrieve_analytics_rules()
193+
assert {:ok, %AnalyticsRulesRetrieveSchema{rules: rules}} =
194+
Analytics.retrieve_analytics_rules()
195+
196+
assert length(rules) >= 0
197+
end
198+
199+
@tag ["27.1": true, "26.0": true, "0.25.2": true]
200+
test "success: create analytics rule" do
201+
name = "product_queries_aggregation"
202+
203+
body =
204+
%{
205+
"name" => name,
206+
"type" => "popular_queries",
207+
"params" => %{
208+
"source" => %{
209+
"collections" => ["products"]
210+
},
211+
"destination" => %{
212+
"collection" => "product_queries"
213+
},
214+
"limit" => 1_000
215+
}
216+
}
217+
|> Jason.encode_to_iodata!()
218+
219+
assert {:ok, %AnalyticsRuleSchema{name: ^name}} = Analytics.create_analytics_rule(body)
220+
end
221+
222+
@tag ["27.1": true, "26.0": true, "0.25.2": true]
223+
test "success: retrieve analytics rule" do
224+
name = "product_no_hits"
225+
226+
body =
227+
%{
228+
"name" => name,
229+
"type" => "nohits_queries",
230+
"params" => %{
231+
"source" => %{
232+
"collections" => ["products"]
233+
},
234+
"destination" => %{
235+
"collection" => "no_hits_queries"
236+
},
237+
"limit" => 1_000
238+
}
239+
}
240+
|> Jason.encode_to_iodata!()
241+
242+
assert {:ok, %AnalyticsRuleSchema{name: ^name}} = Analytics.create_analytics_rule(body)
243+
assert {:ok, %AnalyticsRuleSchema{name: ^name}} = Analytics.retrieve_analytics_rule(name)
244+
end
245+
246+
@tag ["27.1": true, "26.0": true, "0.25.2": true]
247+
test "success: create analytics event" do
248+
name = "product_downloads"
249+
250+
body =
251+
%{
252+
"name" => name,
253+
"type" => "counter",
254+
"params" => %{
255+
"source" => %{
256+
"collections" => ["products"],
257+
"events" => [
258+
%{"type" => "click", "weight" => 1, "name" => "products_downloads_event"}
259+
]
260+
},
261+
"destination" => %{
262+
"collection" => "product_queries",
263+
"counter_field" => "downloads"
264+
}
265+
}
266+
}
267+
|> Jason.encode_to_iodata!()
268+
269+
assert {:ok, %AnalyticsRuleSchema{name: ^name}} = Analytics.create_analytics_rule(body)
270+
271+
name = "products_downloads_event"
272+
273+
body =
274+
%{
275+
"name" => name,
276+
"type" => "click",
277+
"data" => %{
278+
"doc_id" => "2468",
279+
"user_id" => "9903"
280+
}
281+
}
282+
|> Jason.encode_to_iodata!()
283+
284+
assert {:ok, %AnalyticsEventCreateResponse{ok: true}} = Analytics.create_analytics_event(body)
10285
end
11286
end

test/operations/collections_test.exs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ defmodule CollectionsTest do
3232

3333
assert {:ok, %CollectionResponse{name: ^name}} =
3434
schema
35-
|> Jason.encode!()
35+
|> Jason.encode_to_iodata!()
3636
|> Collections.create_collection()
3737
end
3838

@@ -56,11 +56,11 @@ defmodule CollectionsTest do
5656
],
5757
default_sorting_field: name <> "_id"
5858
}
59-
|> Jason.encode!()
59+
|> Jason.encode_to_iodata!()
6060

6161
assert {:ok, %CollectionResponse{name: ^name}} = Collections.create_collection(schema)
6262

63-
body = Jason.encode!(%{fields: [%{name: "price", drop: true}]})
63+
body = Jason.encode_to_iodata!(%{fields: [%{name: "price", drop: true}]})
6464

6565
assert {:ok, %CollectionUpdateSchema{}} =
6666
Collections.update_collection(name, body)
@@ -87,7 +87,7 @@ defmodule CollectionsTest do
8787
test "success: upsert an alias", %{schema: schema, alias_name: alias_name} do
8888
collection_name = schema.name
8989

90-
body = Jason.encode!(%{"collection_name" => collection_name})
90+
body = Jason.encode_to_iodata!(%{"collection_name" => collection_name})
9191

9292
assert {:ok, %CollectionAlias{collection_name: ^collection_name, name: ^alias_name}} =
9393
Collections.upsert_alias(alias_name, body)

0 commit comments

Comments
 (0)