Skip to content
Closed
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
70 changes: 70 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Lunogram Python SDK

Python SDK for the Lunogram Client API.

## Installation

```bash
pip install lunogram-sdk
```

## Usage

Every authenticated Client API endpoint is scoped to a **project**. You supply
the project UUID **once**, when constructing the client, and the SDK injects it
into every request path automatically. You never pass the project per call.

```python
from lunogram import Lunogram, random_user

client = Lunogram(
api_key="your-api-key",
project_id="11111111-2222-3333-4444-555555555555", # your project UUID
)

# Upsert a user
result = client.user.upsert(random_user())
print(result[0]) # API response

# Send a user event
client.user.events.post({
"identifier": [{"source": "default", "external_id": "user_123"}],
"name": "signed_in",
})

# Organizations work the same way
client.organization.upsert({
"identifier": [{"source": "default", "external_id": "org_123"}],
"name": "Acme Inc.",
})
```

> All SDK actions return a tuple: the API response on index `0`, and a possible
> error on index `1`.

## Project-scoped URLs

As of this release, every Client API path includes the project UUID as a path
segment:

```
/api/client/projects/<project_id>/...
```

| Resource | Path (under `/api/client/projects/<project_id>/`) |
| --- | --- |
| Users | `users` |
| User events | `users/events` |
| User scheduled | `users/scheduled` |
| User devices | `users/devices` |
| User inbox | `users/inbox` (`/count`, `/read`, `/archived`) |
| Organizations | `organizations` |
| Organization users | `organizations/users` |
| Organization events | `organizations/events` |
| Organization scheduled | `organizations/scheduled` |
| Organization inbox | `organizations/inbox` (`/count`, `/read`, `/archived`) |
| Push VAPID | `push/vapid` |
| Auth method sessions | `auth-methods/<auth_method_id>/sessions` |

Authentication is unchanged — the same API key / token is used. Only the URL
gained the project segment.
22 changes: 11 additions & 11 deletions src/lunogram/app/models/objects.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,34 @@
from typing import Literal

from ..http import httphandler
from ...utils.reference import client as reference_client, default_client
from ...utils.reference import client as reference_client

from src.lunogram.app.types.event import *
from src.lunogram.app.types.scheduled import *
from ..types.event import *
from ..types.scheduled import *

entities = Literal["user", "organization"]

# .events and .scheduled objects are defined seperately and later integrated with the corresponding entities

class events:
def __init__(self, api_key, entity: entities, reference: reference_client | None = None):
def __init__(self, api_key, reference: reference_client, entity: entities):
self.entity = entity
self.reference = reference or default_client
self.reference = reference
self.handler = httphandler(api_key)

def post(self, data: Event):
match(self.entity):
case 'user':
req = self.handler.post(self.reference.user.events, data)
case 'organization':
req = self.handler.post(self.reference.organization.events, data)

return req

class scheduled:
def __init__(self, api_key, entity: entities, reference: reference_client | None = None):
def __init__(self, api_key, reference: reference_client, entity: entities):
self.entity = entity
self.reference = reference or default_client
self.reference = reference
self.handler = httphandler(api_key)

def post(self, data: UpsertScheduled):
Expand All @@ -39,12 +39,12 @@ def post(self, data: UpsertScheduled):
req = self.handler.post(self.reference.organization.scheduled, data)

return req

def delete(self, data: DeleteScheduled):
match(self.entity):
case 'user':
req = self.handler.delete(self.reference.user.scheduled, data)
case 'organization':
req = self.handler.delete(self.reference.organization.scheduled, data)

return req
return req
23 changes: 12 additions & 11 deletions src/lunogram/app/models/organization.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
from src.lunogram.app.models.objects import events, scheduled
from src.lunogram.app.http import httphandler
from src.lunogram.app.types.organization import *
from .objects import events, scheduled
from ..http import httphandler
from ..types.organization import *

from ...utils.reference import client
from ...utils.reference import client as reference_client

class organization:
def __init__(self, api_key):
self.events = events(api_key, entity='organization')
self.scheduled = scheduled(api_key, entity='organization')
def __init__(self, api_key, reference: reference_client):
self.reference = reference
self.events = events(api_key, reference, entity='organization')
self.scheduled = scheduled(api_key, reference, entity='organization')
self.handler = httphandler(api_key)

def upsert(self, data: UpsertOrganization):
req = self.handler.post(client.organization.base, data)
req = self.handler.post(self.reference.organization.base, data)

return req

def delete(self, data: DeleteOrganization):
req = self.handler.delete(client.organization.base, data)
req = self.handler.delete(self.reference.organization.base, data)

return req
return req
19 changes: 10 additions & 9 deletions src/lunogram/app/models/user.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
from objects import events, scheduled
from .objects import events, scheduled
from ..http import httphandler

from ...utils.reference import client
from src.lunogram.app.types.user import *
from ...utils.reference import client as reference_client
from ..types.user import *

class user:
def __init__(self, api_key):
self.events = events(api_key, entity='user')
self.scheduled = scheduled(api_key, entity='user')
def __init__(self, api_key, reference: reference_client):
self.reference = reference
self.events = events(api_key, reference, entity='user')
self.scheduled = scheduled(api_key, reference, entity='user')
self.handler = httphandler(api_key)

def upsert(self, data: UpsertUser):
req = self.handler.post(client.user.base, data)
req = self.handler.post(self.reference.user.base, data)

return req

def delete(self, data: DeleteUser):
req = self.handler.delete(client.user.base, data)
req = self.handler.delete(self.reference.user.base, data)

return req
return req
52 changes: 28 additions & 24 deletions src/lunogram/app/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,70 +8,74 @@
# .events and .scheduled objects are defined seperately and later integrated with the corresponding entities

class events:
def __init__(self, api_key, entity: entities):
def __init__(self, api_key, reference: client, entity: entities):
self.entity = entity
self.reference = reference
self.handler = httphandler(api_key)

def post(self, data):
match(self.entity):
case 'user':
req = self.handler.post(client.user.events, data)
req = self.handler.post(self.reference.user.events, data)
case 'organization':
req = self.handler.post(client.organization.events, data)
req = self.handler.post(self.reference.organization.events, data)

return req

class scheduled:
def __init__(self, api_key, entity: entities):
def __init__(self, api_key, reference: client, entity: entities):
self.entity = entity
self.reference = reference
self.handler = httphandler(api_key)

def post(self, data):
match(self.entity):
case 'user':
req = self.handler.post(client.user.scheduled, data)
req = self.handler.post(self.reference.user.scheduled, data)
case 'organization':
req = self.handler.post(client.organization.scheduled, data)
req = self.handler.post(self.reference.organization.scheduled, data)

return req

def delete(self, data):
match(self.entity):
case 'user':
req = self.handler.delete(client.user.scheduled, data)
req = self.handler.delete(self.reference.user.scheduled, data)
case 'organization':
req = self.handler.delete(client.organization.scheduled, data)
req = self.handler.delete(self.reference.organization.scheduled, data)

return req

class user:
def __init__(self, api_key):
self.events = events(api_key, entity='user')
self.scheduled = scheduled(api_key, entity='user')
def __init__(self, api_key, reference: client):
self.reference = reference
self.events = events(api_key, reference, entity='user')
self.scheduled = scheduled(api_key, reference, entity='user')
self.handler = httphandler(api_key)

def upsert(self, data):
req = self.handler.post(client.user.base, data)
req = self.handler.post(self.reference.user.base, data)

return req

def delete(self, data):
req = self.handler.delete(client.user.base, data)
req = self.handler.delete(self.reference.user.base, data)

return req

class organization:
def __init__(self, api_key):
self.events = events(api_key, entity='organization')
self.scheduled = scheduled(api_key, entity='organization')
def __init__(self, api_key, reference: client):
self.reference = reference
self.events = events(api_key, reference, entity='organization')
self.scheduled = scheduled(api_key, reference, entity='organization')
self.handler = httphandler(api_key)

def upsert(self, data):
req = self.handler.post(client.organization.base, data)
req = self.handler.post(self.reference.organization.base, data)

return req

def delete(self, data):
req = self.handler.delete(client.organization.base, data)
req = self.handler.delete(self.reference.organization.base, data)

return req
return req
18 changes: 13 additions & 5 deletions src/lunogram/client.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from random import randint

from .utils import seed
from .utils.reference import client as Reference
from .app import user, organization

class Lunogram:
def __init__(self, api_key):
self.user = user(api_key)
self.organization = organization(api_key)
def __init__(self, api_key, project_id):
# Every Client API endpoint is scoped to a project. The project UUID is
# supplied once here and injected into every request path automatically;
# callers never pass it per request.
self.reference = Reference(project_id)
self.user = user(api_key, self.reference)
self.organization = organization(api_key, self.reference)

# Seeder data to generate a random user should you need it, this is mainly for testing purposes
def random_user():
Expand Down Expand Up @@ -41,8 +46,11 @@ def random_user():
def main() -> None:
"""
Example use ->

client = Lunogram("Your API key here")

client = Lunogram("Your API key here", "your-project-uuid-here")

> The project UUID is required and is injected into every Client API path,
> e.g. /api/client/projects/<project_id>/users

> All sdk actions return a list with the api response on index 0, possible errors on index 1

Expand Down
Loading