Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 16 additions & 14 deletions .github/workflows/test-and-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,24 @@ jobs:
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
services:
typesense:
image: typesense/typesense:28.0
ports:
- 8108:8108
volumes:
- /tmp/typesense-data:/data
- /tmp/typesense-analytics:/analytics
env:
TYPESENSE_API_KEY: xyz
TYPESENSE_DATA_DIR: /data
TYPESENSE_ENABLE_CORS: true
TYPESENSE_ANALYTICS_DIR: /analytics
TYPESENSE_ENABLE_SEARCH_ANALYTICS: true

steps:
- name: Start Typesense
run: |
docker run -d \
-p 8108:8108 \
--name typesense \
-v /tmp/typesense-data:/data \
-v /tmp/typesense-analytics-data:/analytics-data \
typesense/typesense:30.0.alpha1 \
--api-key=xyz \
--data-dir=/data \
--enable-search-analytics=true \
--analytics-dir=/analytics-data \
--analytics-flush-interval=60 \
--analytics-minute-rate-limit=50 \
--enable-cors

- name: Wait for Typesense
run: |
timeout 20 bash -c 'while [[ "$(curl -s -o /dev/null -w ''%{http_code}'' localhost:8108/health)" != "200" ]]; do sleep 1; done' || false
Expand Down
12 changes: 6 additions & 6 deletions examples/analytics_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@

# Drop pre-existing rule if any
try:
client.analytics.rules['top_queries'].delete()
client.analyticsV1.rules['top_queries'].delete()
except Exception as e:
pass

# Create a new rule
create_response = client.analytics.rules.create({
create_response = client.analyticsV1.rules.create({
"name": "top_queries",
"type": "popular_queries",
"params": {
Expand All @@ -33,10 +33,10 @@
print(create_response)

# Try to fetch it back
print(client.analytics.rules['top_queries'].retrieve())
print(client.analyticsV1.rules['top_queries'].retrieve())

# Update the rule
update_response = client.analytics.rules.upsert('top_queries', {
update_response = client.analyticsV1.rules.upsert('top_queries', {
"name": "top_queries",
"type": "popular_queries",
"params": {
Expand All @@ -52,7 +52,7 @@
print(update_response)

# List all rules
print(client.analytics.rules.retrieve())
print(client.analyticsV1.rules.retrieve())

# Delete the rule
print(client.analytics.rules['top_queries'].delete())
print(client.analyticsV1.rules['top_queries'].delete())
40 changes: 6 additions & 34 deletions src/typesense/analytics.py
Original file line number Diff line number Diff line change
@@ -1,42 +1,14 @@
"""
This module provides functionality for managing analytics in Typesense.

Classes:
- Analytics: Handles operations related to analytics, including access to analytics rules.

Methods:
- __init__: Initializes the Analytics object.

The Analytics class serves as an entry point for analytics-related operations in Typesense,
currently providing access to AnalyticsRules.

For more information on analytics, refer to the Analytics & Query Suggestion
[documentation](https://typesense.org/docs/27.0/api/analytics-query-suggestions.html)

This module uses type hinting and is compatible with Python 3.11+ as well as earlier
versions through the use of the typing_extensions library.
"""
"""Client for Typesense Analytics module."""

from typesense.analytics_events import AnalyticsEvents
from typesense.analytics_rules import AnalyticsRules
from typesense.api_call import ApiCall


class Analytics(object):
"""
Class for managing analytics in Typesense.

This class provides access to analytics-related functionalities,
currently including operations on analytics rules.

Attributes:
rules (AnalyticsRules): An instance of AnalyticsRules for managing analytics rules.
"""
class Analytics:
"""Client for v30 Analytics endpoints."""

def __init__(self, api_call: ApiCall) -> None:
"""
Initialize the Analytics object.

Args:
api_call (ApiCall): The API call object for making requests.
"""
self.api_call = api_call
self.rules = AnalyticsRules(api_call)
self.events = AnalyticsEvents(api_call)
73 changes: 73 additions & 0 deletions src/typesense/analytics_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Client for Analytics events and status operations."""

import sys

if sys.version_info >= (3, 11):
import typing
else:
import typing_extensions as typing

from typesense.api_call import ApiCall
from typesense.types.analytics import (
AnalyticsEvent as AnalyticsEventSchema,
AnalyticsEventCreateResponse,
AnalyticsEventsResponse,
AnalyticsStatus,
)


class AnalyticsEvents:
events_path: typing.Final[str] = "/analytics/events"
flush_path: typing.Final[str] = "/analytics/flush"
status_path: typing.Final[str] = "/analytics/status"

def __init__(self, api_call: ApiCall) -> None:
self.api_call = api_call

def create(self, event: AnalyticsEventSchema) -> AnalyticsEventCreateResponse:
response: AnalyticsEventCreateResponse = self.api_call.post(
AnalyticsEvents.events_path,
body=event,
as_json=True,
entity_type=AnalyticsEventCreateResponse,
)
return response

def retrieve(
self,
*,
user_id: str,
name: str,
n: int,
) -> AnalyticsEventsResponse:
params: typing.Dict[str, typing.Union[str, int]] = {
"user_id": user_id,
"name": name,
"n": n,
}
response: AnalyticsEventsResponse = self.api_call.get(
AnalyticsEvents.events_path,
params=params,
as_json=True,
entity_type=AnalyticsEventsResponse,
)
return response

def flush(self) -> AnalyticsEventCreateResponse:
response: AnalyticsEventCreateResponse = self.api_call.post(
AnalyticsEvents.flush_path,
body={},
as_json=True,
entity_type=AnalyticsEventCreateResponse,
)
return response

def status(self) -> AnalyticsStatus:
response: AnalyticsStatus = self.api_call.get(
AnalyticsEvents.status_path,
as_json=True,
entity_type=AnalyticsStatus,
)
return response


107 changes: 17 additions & 90 deletions src/typesense/analytics_rule.py
Original file line number Diff line number Diff line change
@@ -1,104 +1,31 @@
"""
This module provides functionality for managing individual analytics rules in Typesense.

Classes:
- AnalyticsRule: Handles operations related to a specific analytics rule.

Methods:
- __init__: Initializes the AnalyticsRule object.
- _endpoint_path: Constructs the API endpoint path for this specific analytics rule.
- retrieve: Retrieves the details of this specific analytics rule.
- delete: Deletes this specific analytics rule.

The AnalyticsRule class interacts with the Typesense API to manage operations on a
specific analytics rule. It provides methods to retrieve and delete individual rules.

For more information on analytics, refer to the Analytics & Query Suggestion
[documentation](https://typesense.org/docs/27.0/api/analytics-query-suggestions.html)

This module uses type hinting and is compatible with Python 3.11+ as well as earlier
versions through the use of the typing_extensions library.
"""

import sys

if sys.version_info >= (3, 11):
import typing
else:
import typing_extensions as typing
"""Per-rule client for Analytics rules operations."""

from typesense.api_call import ApiCall
from typesense.types.analytics_rule import (
RuleDeleteSchema,
RuleSchemaForCounters,
RuleSchemaForQueries,
)
from typesense.types.analytics import AnalyticsRuleSchema


class AnalyticsRule:
"""
Class for managing individual analytics rules in Typesense.

This class provides methods to interact with a specific analytics rule,
including retrieving and deleting it.

Attributes:
api_call (ApiCall): The API call object for making requests.
rule_id (str): The ID of the analytics rule.
"""

def __init__(self, api_call: ApiCall, rule_id: str):
"""
Initialize the AnalyticsRule object.

Args:
api_call (ApiCall): The API call object for making requests.
rule_id (str): The ID of the analytics rule.
"""
def __init__(self, api_call: ApiCall, rule_name: str) -> None:
self.api_call = api_call
self.rule_id = rule_id
self.rule_name = rule_name

@property
def _endpoint_path(self) -> str:
from typesense.analytics_rules import AnalyticsRules

def retrieve(
self,
) -> typing.Union[RuleSchemaForQueries, RuleSchemaForCounters]:
"""
Retrieve this specific analytics rule.
return "/".join([AnalyticsRules.resource_path, self.rule_name])

Returns:
Union[RuleSchemaForQueries, RuleSchemaForCounters]:
The schema containing the rule details.
"""
response: typing.Union[RuleSchemaForQueries, RuleSchemaForCounters] = (
self.api_call.get(
self._endpoint_path,
entity_type=typing.Union[RuleSchemaForQueries, RuleSchemaForCounters],
as_json=True,
)
def retrieve(self) -> AnalyticsRuleSchema:
response: AnalyticsRule = self.api_call.get(
self._endpoint_path,
as_json=True,
entity_type=AnalyticsRule,
)
return response

def delete(self) -> RuleDeleteSchema:
"""
Delete this specific analytics rule.

Returns:
RuleDeleteSchema: The schema containing the deletion response.
"""
response: RuleDeleteSchema = self.api_call.delete(
def delete(self) -> AnalyticsRuleSchema:
response: AnalyticsRule = self.api_call.delete(
self._endpoint_path,
entity_type=RuleDeleteSchema,
entity_type=AnalyticsRule,
)

return response

@property
def _endpoint_path(self) -> str:
"""
Construct the API endpoint path for this specific analytics rule.

Returns:
str: The constructed endpoint path.
"""
from typesense.analytics_rules import AnalyticsRules

return "/".join([AnalyticsRules.resource_path, self.rule_id])
Loading