Merge branch 'dev' into 'main'
WIP See merge request trustcafe/trustcafe-api-wrapper!5
This commit is contained in:
commit
81680d91b1
14 changed files with 289 additions and 39 deletions
103
documentation.md
103
documentation.md
|
|
@ -1,18 +1,107 @@
|
|||
# Basic usage (without an .env)
|
||||
Here's how to make a post to the homepage (aka Maintrunk)
|
||||
|
||||
## Setup
|
||||
Here's a very basic example of setting up:
|
||||
```python
|
||||
import trustcafeapiwrapper
|
||||
from trustcafeapiwrapper.wrappers.post.create_post import create_post
|
||||
|
||||
# Setup the API Client
|
||||
API = trustcafeapiwrapper.APIClient(
|
||||
client_id="YOUR_CLIENT_ID"
|
||||
client_secret="YOUR_CLIENT_SECRET"
|
||||
client_id="YOUR_CLIENT_ID",
|
||||
client_secret="YOUR_CLIENT_SECRET",
|
||||
env="alpha" # alpha | production.
|
||||
debug=False
|
||||
)
|
||||
```
|
||||
or with .env
|
||||
```python
|
||||
import trustcafeapiwrapper, os
|
||||
from dotenv import load_dotenv
|
||||
load_dotenv()
|
||||
API = APIClient(
|
||||
client_id=os.getenv("client_id"),
|
||||
client_secret=os.getenv("client_secret"),
|
||||
env="alpha", # alpha | production.
|
||||
debug=False,
|
||||
)
|
||||
```
|
||||
|
||||
## Use a wrapper
|
||||
Wrappers make it as simple as possible to perform an action.
|
||||
|
||||
A wrapper will come with a job and a payload ready to be passed to the `wrapped` function.
|
||||
|
||||
eg. Use the `create_post` wrapper
|
||||
|
||||
```python
|
||||
from trustcafeapiwrapper.wrappers.post.create_post import create_post
|
||||
|
||||
# Use the create_post wrapper
|
||||
API.wrapped(create_post(
|
||||
"This is a test post created via the create_post wrapper function.",
|
||||
))
|
||||
```
|
||||
```
|
||||
|
||||
|
||||
**_NOTE:_** There are not as many wrappers as jobs because fetching is simpler. More will be added though to make it consistent.
|
||||
|
||||
|
||||
## Make a request via `job`
|
||||
Jobs make the requests to the API. They have the endpoint and path setup already, we just pass the payload. If you want to avoid making your own payload, use a `wrapper`.
|
||||
|
||||
Use the job for getting public posts.
|
||||
|
||||
Now you don't need to worry about which API
|
||||
```python
|
||||
postlist = API.run_job('posts.getpublic')
|
||||
print(postlist)
|
||||
'''
|
||||
{
|
||||
"Items": [
|
||||
{
|
||||
...
|
||||
'''
|
||||
```
|
||||
**_NOTE:_** There are aren't the complete set yet created here.
|
||||
|
||||
## Custom Request
|
||||
There should hopefully be a `job` or a `wrapper` that exists for what you need but if not, then this is how you make a request using the core mechanism.
|
||||
|
||||
Use the `make_request` function to make a request from the content API to get posts in the music branch.
|
||||
```python
|
||||
postlist = API.make_request("GET", "content", f"post/ref-subwiki/music")
|
||||
print(postlist)
|
||||
'''
|
||||
{
|
||||
"Items": [
|
||||
{
|
||||
...
|
||||
'''
|
||||
```
|
||||
|
||||
# Jobs
|
||||
## block
|
||||
## branch
|
||||
### `get`
|
||||
### `listbyname`
|
||||
## change
|
||||
## comment
|
||||
### `create`
|
||||
### `listbypostid`
|
||||
## feed
|
||||
## mute
|
||||
## notifcation
|
||||
### `listnotifications`
|
||||
## post
|
||||
## reaction
|
||||
## trust
|
||||
## userprofile
|
||||
## vote
|
||||
|
||||
|
||||
# Wrappers
|
||||
## post
|
||||
### `create_post`
|
||||
### `update_post`
|
||||
## comment
|
||||
### `create_comment`
|
||||
|
||||
# Utils
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
[project]
|
||||
name = "trustcafeapiwrapper"
|
||||
version = "0.1.0.6"
|
||||
version = "0.1.0.7"
|
||||
description = "Wraps the Trust Cafe API"
|
||||
readme = "README.md"
|
||||
requires-python = ">=3.13"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,17 @@ endpoints = {
|
|||
"auth": "https://oo0wks9pbi.execute-api.us-east-1.amazonaws.com/alpha/",
|
||||
"content": "https://w1yygdhayc.execute-api.us-east-1.amazonaws.com/alpha/",
|
||||
"megaphone": "https://opdhjaktnl.execute-api.us-east-1.amazonaws.com/alpha/",
|
||||
}
|
||||
"moderation": "https://sbb1xrqsf1.execute-api.us-east-1.amazonaws.com/alpha/",
|
||||
"spider": "https://xuvz1oj1sk.execute-api.us-east-1.amazonaws.com/dev/"
|
||||
},
|
||||
"production": {
|
||||
"audrey": "https://iiouau5d2k.execute-api.us-east-1.amazonaws.com/production/",
|
||||
"auth": "https://e2we3nktl4.execute-api.us-east-1.amazonaws.com/production/",
|
||||
"content": "https://32hho6rvg1.execute-api.us-east-1.amazonaws.com/production/",
|
||||
"megaphone": "https://yfnamdpnc8.execute-api.us-east-1.amazonaws.com/production/",
|
||||
"moderation": "https://egfpoo3mw3.execute-api.us-east-1.amazonaws.com/production/",
|
||||
"spider": "https://coi5kvgypc.execute-api.us-east-1.amazonaws.com/production/"
|
||||
},
|
||||
}
|
||||
class APIClient(BaseModel):
|
||||
# Internal State (Not passed in __init__)
|
||||
|
|
@ -21,12 +31,18 @@ class APIClient(BaseModel):
|
|||
|
||||
client_id: SecretStr
|
||||
client_secret: SecretStr
|
||||
|
||||
|
||||
_access_token: str = PrivateAttr(default="")
|
||||
_access_token_timeout: int = PrivateAttr(default=0)
|
||||
|
||||
|
||||
def __init__(self, **data):
|
||||
super().__init__(**data)
|
||||
self.validate_environment()
|
||||
|
||||
def validate_environment(self):
|
||||
if self.environment not in endpoints:
|
||||
raise ValueError(f"Environment '{self.environment}' is not valid. Must be one of: {list(endpoints.keys())}")
|
||||
|
||||
def make_request(self, method: str, endpoint: str, path: str, data: dict = None, authenticate: bool = True, query_params: dict = None) -> dict:
|
||||
|
||||
# Make sure the endpoint is defined in the endpoints dictionary
|
||||
|
|
@ -45,13 +61,16 @@ class APIClient(BaseModel):
|
|||
url += f"?{urlencode(query_params)}"
|
||||
|
||||
# Set up headers for the request
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
# Add the Authorization header with the access token if authentication is required
|
||||
if authenticate:
|
||||
token = self._access_token
|
||||
headers["Authorization"] = f"Bearer {token}"
|
||||
else:
|
||||
token = 'guest'
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/json",
|
||||
"Authorization": f"Bearer {token}"
|
||||
}
|
||||
|
||||
|
||||
# Debugging output to show the request details before making the API call
|
||||
if self.debug:
|
||||
|
|
@ -112,6 +131,17 @@ class APIClient(BaseModel):
|
|||
self._access_token = token_data.get("access_token", "")
|
||||
self._access_token_timeout = token_data.get("access_token_timeout", 0)
|
||||
|
||||
def set_environment(self, environment: str) -> None:
|
||||
"""
|
||||
Set the environment for the API client.
|
||||
|
||||
Args:
|
||||
environment (str): The environment to set (e.g., "alpha", "production").
|
||||
"""
|
||||
|
||||
self.validate_environment()
|
||||
self.environment = environment
|
||||
|
||||
def is_token_valid(self) -> bool:
|
||||
"""
|
||||
Checks if the current access token is still valid based on the current time and the token's expiration time.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
from .update import update
|
||||
from .create import create
|
||||
from .get import get
|
||||
from .listbybranch import listbybranch
|
||||
from .listbyuserprofile import listbyuserprofile
|
||||
from .create import create
|
||||
from .listall import listall
|
||||
from .listpublic import listpublic
|
||||
from .listremoved import listremoved
|
||||
|
|
@ -7,5 +7,5 @@ def listpublic(API, lastEvaluatedKey=None) -> dict:
|
|||
Returns:
|
||||
dict: The list of public posts.
|
||||
"""
|
||||
post_list = API.make_request("GET", "content", f"post/public", authenticate=True, query_params=lastEvaluatedKey)
|
||||
post_list = API.make_request("GET", "content", f"public/posts", authenticate=False, query_params=lastEvaluatedKey)
|
||||
return post_list
|
||||
12
src/trustcafeapiwrapper/jobs/post/update.py
Normal file
12
src/trustcafeapiwrapper/jobs/post/update.py
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
def update(API, payload: dict) -> dict:
|
||||
"""
|
||||
Updates an existing post in the API.
|
||||
|
||||
Args:
|
||||
payload (dict): The data for the post update.
|
||||
|
||||
Returns:
|
||||
dict: The post data.
|
||||
"""
|
||||
post_data = API.make_request("PUT", "content", "post/update", data=payload, authenticate=True)
|
||||
return post_data
|
||||
|
|
@ -1 +1,2 @@
|
|||
from .create_post import create_post
|
||||
from .create_post import create_post
|
||||
from .update_post import update_post
|
||||
37
src/trustcafeapiwrapper/wrappers/post/update_post.py
Normal file
37
src/trustcafeapiwrapper/wrappers/post/update_post.py
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
from trustcafeapiwrapper.utils import get_post_pksk, get_parent_pksk_from_path
|
||||
|
||||
def update_post(parent_path, post_path, post_text, blur_label=None, card_url=None, collaborative=False):
|
||||
"""
|
||||
Updates an existing post.
|
||||
|
||||
Args:
|
||||
post_slug (str): The slug of the post to update.
|
||||
post_text (str): The new text for the post.
|
||||
parent_path (str, optional): The parent path for the post. Defaults to '/'.
|
||||
blur_label (str, optional): The blur label for the post. Defaults to None.
|
||||
card_url (str, optional): The card URL for the post. Defaults to None.
|
||||
collaborative (bool, optional): Whether the post is collaborative. Defaults to False.
|
||||
|
||||
Returns:
|
||||
dict: The updated post data.
|
||||
"""
|
||||
parent_pksk = get_parent_pksk_from_path(parent_path)
|
||||
post_pksk = get_post_pksk(parent_pksk, post_path)
|
||||
|
||||
payload = {
|
||||
"key": {
|
||||
"pk": post_pksk.get('pk', None),
|
||||
"sk": post_pksk.get('sk', None)
|
||||
},
|
||||
"postSlug": post_path.strip('/post/'),
|
||||
"blurLabel": blur_label,
|
||||
"cardUrl": card_url,
|
||||
"postText": post_text,
|
||||
"collaborative": collaborative,
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
"job_function": "post.update",
|
||||
"payload": payload
|
||||
}
|
||||
35
testing.py
35
testing.py
|
|
@ -61,9 +61,9 @@ def save_response(response):
|
|||
# print("-----------------------------")
|
||||
# feed = API.run_job('post.listbybranch', "music")
|
||||
# print(feed)
|
||||
print("-----------------------------")
|
||||
feed = save_response(API.run_job('post.listremoved'))
|
||||
print(feed)
|
||||
# print("-----------------------------")
|
||||
# feed = save_response(API.run_job('post.listremoved'))
|
||||
# print(feed)
|
||||
# print("----------------------------z
|
||||
# branchlist = API.run_job('branch.listbyname')
|
||||
# print(branchlist)
|
||||
|
|
@ -80,6 +80,16 @@ print(feed)
|
|||
# print(feed)
|
||||
# print("-----------------------------")
|
||||
|
||||
# save_response(API.run_job('post.create', {
|
||||
# "blurLabel": None,
|
||||
# "cardUrl": None,
|
||||
# "postText": "This is a test post created via the API wrapper.",
|
||||
# "collaborative": False,
|
||||
# "parent": {
|
||||
# "pk": "maintrunk#maintrunk",
|
||||
# "sk": "maintrunk#maintrunk"
|
||||
# }
|
||||
# }))
|
||||
# save_response(API.run_job('post.create', {
|
||||
# "blurLabel": None,
|
||||
# "cardUrl": None,
|
||||
|
|
@ -139,7 +149,7 @@ print(feed)
|
|||
# }))
|
||||
|
||||
# save_response(API.run_job('post.listall'))
|
||||
# save_response(API.run_job('post.listpublic'))
|
||||
save_response(API.run_job('post.listpublic'))
|
||||
|
||||
# from trustcafeapiwrapper.wrappers.post.create_post import create_post
|
||||
|
||||
|
|
@ -183,8 +193,15 @@ print(feed)
|
|||
# users = API.run_job('trust.listbyuserhas', "simon-little ")
|
||||
# print(users)
|
||||
|
||||
from trustcafeapiwrapper.wrappers.trust import trust
|
||||
save_response(API.wrapped(trust(
|
||||
100,
|
||||
"/user/bossman"
|
||||
)))
|
||||
# from trustcafeapiwrapper.wrappers.trust import trust
|
||||
# save_response(API.wrapped(trust(
|
||||
# 100,
|
||||
# "/user/bossman"
|
||||
# )))
|
||||
|
||||
# from trustcafeapiwrapper.wrappers.post.update_post import update_post
|
||||
# save_response(API.wrapped(update_post(
|
||||
# post_text="This is an updated version of the test post created via the create_post wrapper function.",
|
||||
# post_path="/post/1775143460-ef45186a",
|
||||
# parent_path="/",
|
||||
# )))
|
||||
|
|
|
|||
|
|
@ -46,11 +46,43 @@ class TestAPIClient(unittest.TestCase):
|
|||
|
||||
result = self.api_client.run_job(mock_job, "value1")
|
||||
self.assertEqual(result, "Job executed with value1")
|
||||
|
||||
|
||||
|
||||
def test_set_production_env(self):
|
||||
Prod_API = APIClient(
|
||||
client_id="prod_client_id",
|
||||
client_secret="prod_client_secret",
|
||||
debug=False,
|
||||
environment="production"
|
||||
)
|
||||
self.assertEqual(Prod_API.environment, "production")
|
||||
|
||||
def test_set_bad_env(self):
|
||||
with self.assertRaises(ValueError):
|
||||
APIClient(
|
||||
client_id="test_client_id",
|
||||
client_secret="test_client_secret",
|
||||
debug=False,
|
||||
environment="invalid_env"
|
||||
)
|
||||
|
||||
def test_set_environment_method(self):
|
||||
self.api_client.set_environment("production")
|
||||
self.assertEqual(self.api_client.environment, "production")
|
||||
|
||||
def test_make_non_supported_request_type(self):
|
||||
with self.assertRaises(ValueError):
|
||||
self.api_client.make_request("PATCH", "content", "test")
|
||||
|
||||
def test_make_request_with_bad_endpoint(self):
|
||||
with self.assertRaises(ValueError):
|
||||
self.api_client.make_request("GET", "invalid_endpoint", "test")
|
||||
|
||||
|
||||
@patch('trustcafeapiwrapper.apiclient.requests.request')
|
||||
def test_make_request(self, mock_request):
|
||||
# This is a placeholder test. In a real test, you'd mock the HTTP request and response.
|
||||
|
||||
response = self.api_client.make_request("GET", "content", "https://api.example.com/test")
|
||||
# This test should be expanded
|
||||
# Or the the functions should be broken up more to be more easily testable
|
||||
response = self.api_client.make_request("GET", "content", "test")
|
||||
#
|
||||
mock_request.assert_called_once()
|
||||
|
|
@ -13,10 +13,10 @@ class TestGetChildSpkskFromPaths(unittest.TestCase):
|
|||
self.assertEqual(get_child_spksk_from_paths(parent_path, item_path), expected_output)
|
||||
|
||||
def test_comment_reaction(self):
|
||||
parent_path = '/post/12345'
|
||||
parent_path = '/post/12345-abcv'
|
||||
item_path = '/comment/67890'
|
||||
expected_output = {
|
||||
"pk": "post#12345",
|
||||
"pk": "post#12345-abcv",
|
||||
"sk": "comment#67890",
|
||||
"entity": "comment",
|
||||
"slug": "67890"
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ class TestReact(unittest.TestCase):
|
|||
def test_react_to_post(self):
|
||||
reaction_type = 'like'
|
||||
parent_path = '/'
|
||||
item_path = '/post/12345'
|
||||
item_path = '/post/12345-abcv'
|
||||
result = react(reaction_type, parent_path, item_path)
|
||||
|
||||
self.assertIsInstance(result, dict)
|
||||
|
|
@ -14,13 +14,13 @@ class TestReact(unittest.TestCase):
|
|||
self.assertEqual(result["job_function"], "reaction.reacttosomething")
|
||||
self.assertEqual(result["payload"]["reaction"], reaction_type)
|
||||
self.assertEqual(result["payload"]["parent"]["pk"], "maintrunk#maintrunk")
|
||||
self.assertEqual(result["payload"]["parent"]["sk"], "post#12345")
|
||||
self.assertEqual(result["payload"]["parent"]["sk"], "post#12345-abcv")
|
||||
self.assertEqual(result["payload"]["parent"]["entity"], "post")
|
||||
self.assertEqual(result["payload"]["parent"]["slug"], "12345")
|
||||
self.assertEqual(result["payload"]["parent"]["slug"], "12345-abcv")
|
||||
|
||||
def test_react_to_comment(self):
|
||||
reaction_type = 'like'
|
||||
parent_path = '/post/12345'
|
||||
parent_path = '/post/12345-abcv'
|
||||
item_path = '/comment/67890'
|
||||
result = react(reaction_type, parent_path, item_path)
|
||||
|
||||
|
|
@ -29,7 +29,7 @@ class TestReact(unittest.TestCase):
|
|||
self.assertIn("payload", result)
|
||||
self.assertEqual(result["job_function"], "reaction.reacttosomething")
|
||||
self.assertEqual(result["payload"]["reaction"], reaction_type)
|
||||
self.assertEqual(result["payload"]["parent"]["pk"], "post#12345")
|
||||
self.assertEqual(result["payload"]["parent"]["pk"], "post#12345-abcv")
|
||||
self.assertEqual(result["payload"]["parent"]["sk"], "comment#67890")
|
||||
self.assertEqual(result["payload"]["parent"]["entity"], "comment")
|
||||
self.assertEqual(result["payload"]["parent"]["slug"], "67890")
|
||||
30
tests/wrappers/update_post.py
Normal file
30
tests/wrappers/update_post.py
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import unittest
|
||||
from trustcafeapiwrapper.wrappers.post.update_post import update_post
|
||||
class TestUpdatePost(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.post_text = "This is an updated test post created via the update_post wrapper function."
|
||||
self.blur_label = None
|
||||
self.card_url = None
|
||||
self.collaborative = False
|
||||
|
||||
def test_update_post(self):
|
||||
result = update_post(
|
||||
parent_path='/',
|
||||
post_path='/post/1235-abcv',
|
||||
post_text=self.post_text,
|
||||
blur_label=self.blur_label,
|
||||
card_url=self.card_url,
|
||||
collaborative=self.collaborative
|
||||
)
|
||||
self.assertIsInstance(result, dict)
|
||||
self.assertIn("job_function", result)
|
||||
self.assertIn("payload", result)
|
||||
self.assertEqual(result["job_function"], "post.update")
|
||||
self.assertEqual(result["payload"]["postText"], self.post_text)
|
||||
self.assertEqual(result["payload"]["blurLabel"], self.blur_label)
|
||||
self.assertEqual(result["payload"]["cardUrl"], self.card_url)
|
||||
self.assertEqual(result["payload"]["collaborative"], self.collaborative)
|
||||
self.assertEqual(result["payload"]["postSlug"], "1235-abcv")
|
||||
self.assertEqual(result["payload"]["key"]["pk"], "maintrunk#maintrunk")
|
||||
self.assertEqual(result["payload"]["key"]["sk"], "post#1235-abcv")
|
||||
self.assertNotIn("slug", result["payload"]["key"])
|
||||
|
|
@ -11,6 +11,7 @@ from tests.utils.get_user_slug_from_path import TestGetUserSlugFromPath
|
|||
from tests.utils.get_userprofile_pksk_from_slug import TestGetUserprofilePkskFromSlug
|
||||
|
||||
from tests.wrappers.create_post import TestCreatePost
|
||||
from tests.wrappers.update_post import TestUpdatePost
|
||||
from tests.wrappers.create_comment import TestCreateComment
|
||||
from tests.wrappers.react import TestReact
|
||||
from tests.wrappers.vote import TestVoteCast
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue