Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Denote every field which is omitted when None. #106

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
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
50 changes: 50 additions & 0 deletions examples/search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/usr/bin/env python3
import argparse
import os

from jmapc import Client, EmailQueryFilterCondition, MailboxQueryFilterCondition, Ref
from jmapc.methods import EmailQuery, MailboxGet, MailboxGetResponse, MailboxQuery

def main():
parser = argparse.ArgumentParser()
parser.add_argument("--host", "-H", default="email.example.com")
parser.add_argument("--username", "-u", default="[email protected]")
parser.add_argument("--password", "-p", default="keep-it-secret")
parser.add_argument("search", help="The terms to search for.")
args = parser.parse_args()


# Create and configure client
#client = Client.create_with_api_token(
#host=os.environ["JMAP_HOST"], api_token=os.environ["JMAP_API_TOKEN"]
#)
client = Client.create_with_password(
host=args.host,
user=args.username,
password=args.password,
)

methods = [
EmailQuery(filter=EmailQueryFilterCondition(text=args.search)),
]

# Call JMAP API with the prepared request
results = client.request(methods)

if not results:
print("No results")
return

print(f"Found {len(results)} results")
# Retrieve the InvocationResponse for the second method. The InvocationResponse
# contains the client-provided method ID, and the result data model.
method_1_result = results[0]

# Retrieve the result data model from the InvocationResponse instance
method_1_result_data = method_1_result.response

# Retrieve the Email data from the result data model
print(method_1_result_data)

if __name__ == "__main__":
main()
58 changes: 29 additions & 29 deletions jmapc/methods/base.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from __future__ import annotations

import contextlib
from dataclasses import dataclass, field
from dataclasses import dataclass
from typing import Any, Dict, List, Optional
from typing import Set as SetType
from typing import Type, Union, cast

from ..errors import Error
from ..models import AddedItem, Comparator, ListOrRef, SetError, StrOrRef
from ..serializer import Model
from ..serializer import Model, null_omitted_field


class MethodBase(Model):
Expand All @@ -35,7 +35,7 @@ class Method(MethodBase):

@dataclass
class MethodWithAccount(Method):
account_id: Optional[str] = field(init=False, default=None)
account_id: Optional[str] = null_omitted_field(init=False)


class ResponseCollector(MethodBase):
Expand Down Expand Up @@ -87,7 +87,7 @@ class ChangesMethod:
@dataclass
class Changes(MethodWithAccount, ChangesMethod):
since_state: str
max_changes: Optional[int] = None
max_changes: Optional[int] = null_omitted_field()


@dataclass
Expand All @@ -107,10 +107,10 @@ class CopyMethod:
@dataclass
class Copy(MethodWithAccount, CopyMethod):
from_account_id: str
if_from_in_state: Optional[str] = None
if_in_state: Optional[str] = None
if_from_in_state: Optional[str] = null_omitted_field()
if_in_state: Optional[str] = null_omitted_field()
on_success_destroy_original: bool = False
destroy_from_if_in_state: Optional[str] = None
destroy_from_if_in_state: Optional[str] = null_omitted_field()


@dataclass
Expand All @@ -127,8 +127,8 @@ class GetMethod:

@dataclass
class Get(MethodWithAccount, GetMethod):
ids: Optional[ListOrRef[str]]
properties: Optional[List[str]] = None
ids: Optional[ListOrRef[str]] = null_omitted_field()
properties: Optional[List[str]] = null_omitted_field()


@dataclass
Expand All @@ -147,10 +147,10 @@ class SetMethod:

@dataclass
class Set(MethodWithAccount, SetMethod):
if_in_state: Optional[StrOrRef] = None
create: Optional[Dict[str, Any]] = None
update: Optional[Dict[str, Dict[str, Any]]] = None
destroy: Optional[ListOrRef[str]] = None
if_in_state: Optional[StrOrRef] = null_omitted_field()
create: Optional[Dict[str, Any]] = null_omitted_field()
update: Optional[Dict[str, Dict[str, Any]]] = null_omitted_field()
destroy: Optional[ListOrRef[str]] = null_omitted_field()


@dataclass
Expand All @@ -160,9 +160,9 @@ class SetResponse(ResponseWithAccount, SetMethod):
created: Optional[Dict[str, Any]]
updated: Optional[Dict[str, Any]]
destroyed: Optional[List[str]]
not_created: Optional[Dict[str, SetError]] = None
not_updated: Optional[Dict[str, SetError]] = None
not_destroyed: Optional[Dict[str, SetError]] = None
not_created: Optional[Dict[str, SetError]] = null_omitted_field()
not_updated: Optional[Dict[str, SetError]] = null_omitted_field()
not_destroyed: Optional[Dict[str, SetError]] = null_omitted_field()


class QueryMethod:
Expand All @@ -171,12 +171,12 @@ class QueryMethod:

@dataclass
class Query(MethodWithAccount, QueryMethod):
sort: Optional[List[Comparator]] = None
position: Optional[int] = None
anchor: Optional[str] = None
anchor_offset: Optional[int] = None
limit: Optional[int] = None
calculate_total: Optional[bool] = None
sort: Optional[List[Comparator]] = null_omitted_field()
position: Optional[int] = null_omitted_field()
anchor: Optional[str] = null_omitted_field()
anchor_offset: Optional[int] = null_omitted_field()
limit: Optional[int] = null_omitted_field()
calculate_total: Optional[bool] = null_omitted_field()


@dataclass
Expand All @@ -185,8 +185,8 @@ class QueryResponse(ResponseWithAccount, QueryMethod):
can_calculate_changes: bool
position: int
ids: ListOrRef[str]
total: Optional[int] = None
limit: Optional[int] = None
total: Optional[int] = null_omitted_field()
limit: Optional[int] = null_omitted_field()


class QueryChangesMethod:
Expand All @@ -195,10 +195,10 @@ class QueryChangesMethod:

@dataclass
class QueryChanges(MethodWithAccount, QueryChangesMethod):
sort: Optional[List[Comparator]] = None
since_query_state: Optional[str] = None
max_changes: Optional[int] = None
up_to_id: Optional[str] = None
sort: Optional[List[Comparator]] = null_omitted_field()
since_query_state: Optional[str] = null_omitted_field()
max_changes: Optional[int] = null_omitted_field()
up_to_id: Optional[str] = null_omitted_field()
calculate_total: bool = False


Expand All @@ -208,7 +208,7 @@ class QueryChangesResponse(ResponseWithAccount, QueryChangesMethod):
new_query_state: str
removed: List[str]
added: List[AddedItem]
total: Optional[int] = None
total: Optional[int] = null_omitted_field()


ResponseOrError = Union[Error, Response]
Expand Down
5 changes: 3 additions & 2 deletions jmapc/methods/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

from .. import constants
from .base import Method, Response
from ..serializer import null_omitted_field


class CoreBase:
Expand All @@ -21,12 +22,12 @@ class CoreEcho(CoreBase, EchoMethod, Method):
def to_dict(self, *args: Any, **kwargs: Any) -> Dict[str, Any]:
return self.data or dict()

data: Optional[Dict[str, Any]] = None
data: Optional[Dict[str, Any]] = null_omitted_field()


@dataclass
class CoreEchoResponse(CoreBase, EchoMethod, Response):
data: Optional[Dict[str, Any]] = None
data: Optional[Dict[str, Any]] = null_omitted_field()

@classmethod
def from_dict(
Expand Down
3 changes: 2 additions & 1 deletion jmapc/methods/mailbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from .. import constants
from ..models import Mailbox, MailboxQueryFilter
from ..serializer import null_omitted_field
from .base import (
Changes,
ChangesResponse,
Expand Down Expand Up @@ -48,7 +49,7 @@ class MailboxGetResponse(MailboxBase, GetResponse):

@dataclass
class MailboxQuery(MailboxBase, Query):
filter: Optional[MailboxQueryFilter] = None
filter: Optional[MailboxQueryFilter] = null_omitted_field()
sort_as_tree: bool = False
filter_as_tree: bool = False

Expand Down
62 changes: 34 additions & 28 deletions jmapc/models/email.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,50 @@

from dataclasses_json import config

from ..serializer import Model, datetime_decode, datetime_encode
from ..serializer import (
Model,
datetime_decode,
datetime_encode,
null_omitted_field,
)
from .models import EmailAddress, ListOrRef, Operator, StrOrRef


@dataclass
class Email(Model):
id: Optional[str] = field(metadata=config(field_name="id"), default=None)
blob_id: Optional[str] = None
thread_id: Optional[str] = None
mailbox_ids: Optional[Dict[str, bool]] = None
keywords: Optional[Dict[str, bool]] = None
size: Optional[int] = None
received_at: Optional[datetime] = field(
blob_id: Optional[str] = null_omitted_field()
thread_id: Optional[str] = null_omitted_field()
mailbox_ids: Optional[Dict[str, bool]] = null_omitted_field()
keywords: Optional[Dict[str, bool]] = null_omitted_field()
size: Optional[int] = null_omitted_field()
received_at: Optional[datetime] = null_omitted_field(
default=None,
metadata=config(encoder=datetime_encode, decoder=datetime_decode),
)
message_id: Optional[List[str]] = None
in_reply_to: Optional[List[str]] = None
references: Optional[List[str]] = None
headers: Optional[List[EmailHeader]] = None
mail_from: Optional[List[EmailAddress]] = field(
metadata=config(field_name="from"), default=None
message_id: Optional[List[str]] = null_omitted_field()
in_reply_to: Optional[List[str]] = null_omitted_field()
references: Optional[List[str]] = null_omitted_field()
headers: Optional[List[EmailHeader]] = null_omitted_field()
mail_from: Optional[List[EmailAddress]] = null_omitted_field(
default=None, metadata=config(field_name="from")
)
to: Optional[List[EmailAddress]] = None
cc: Optional[List[EmailAddress]] = None
bcc: Optional[List[EmailAddress]] = None
reply_to: Optional[List[EmailAddress]] = None
subject: Optional[str] = None
sent_at: Optional[datetime] = field(
to: Optional[List[EmailAddress]] = null_omitted_field()
cc: Optional[List[EmailAddress]] = null_omitted_field()
bcc: Optional[List[EmailAddress]] = null_omitted_field()
reply_to: Optional[List[EmailAddress]] = null_omitted_field()
subject: Optional[str] = null_omitted_field()
sent_at: Optional[datetime] = null_omitted_field(
default=None,
metadata=config(encoder=datetime_encode, decoder=datetime_decode),
)
body_structure: Optional[EmailBodyPart] = None
body_values: Optional[Dict[str, EmailBodyValue]] = None
text_body: Optional[List[EmailBodyPart]] = None
html_body: Optional[List[EmailBodyPart]] = None
attachments: Optional[List[EmailBodyPart]] = None
has_attachment: Optional[bool] = None
preview: Optional[str] = None
body_structure: Optional[EmailBodyPart] = null_omitted_field()
body_values: Optional[Dict[str, EmailBodyValue]] = null_omitted_field()
text_body: Optional[List[EmailBodyPart]] = null_omitted_field()
html_body: Optional[List[EmailBodyPart]] = null_omitted_field()
attachments: Optional[List[EmailBodyPart]] = null_omitted_field()
has_attachment: Optional[bool] = null_omitted_field()
preview: Optional[str] = null_omitted_field()


@dataclass
Expand Down Expand Up @@ -97,8 +102,9 @@ class EmailQueryFilterCondition(Model):
not_keyword: Optional[StrOrRef] = None
has_attachment: Optional[bool] = None
text: Optional[StrOrRef] = None
mail_from: Optional[str] = field(
metadata=config(field_name="from"), default=None
mail_from: Optional[str] = null_omitted_field(
default=None,
metadata=config(field_name="from"),
)
to: Optional[StrOrRef] = None
cc: Optional[StrOrRef] = None
Expand Down
30 changes: 15 additions & 15 deletions jmapc/models/mailbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,33 @@

from dataclasses_json import config

from ..serializer import Model
from ..serializer import Model, null_omitted_field
from .models import Operator, StrOrRef


@dataclass
class Mailbox(Model):
id: Optional[str] = field(metadata=config(field_name="id"), default=None)
name: Optional[str] = None
id: Optional[str] = null_omitted_field(metadata=config(field_name="id"))
name: Optional[str] = null_omitted_field()
sort_order: Optional[int] = 0
total_emails: Optional[int] = None
unread_emails: Optional[int] = None
total_threads: Optional[int] = None
unread_threads: Optional[int] = None
total_emails: Optional[int] = null_omitted_field()
unread_emails: Optional[int] = null_omitted_field()
total_threads: Optional[int] = null_omitted_field()
unread_threads: Optional[int] = null_omitted_field()
is_subscribed: Optional[bool] = False
role: Optional[str] = None
parent_id: Optional[str] = field(
metadata=config(field_name="parentId"), default=None
role: Optional[str] = null_omitted_field()
parent_id: Optional[str] = null_omitted_field(
metadata=config(field_name="parentId"),
)


@dataclass
class MailboxQueryFilterCondition(Model):
name: Optional[StrOrRef] = None
role: Optional[StrOrRef] = None
parent_id: Optional[StrOrRef] = None
has_any_role: Optional[bool] = None
is_subscribed: Optional[bool] = None
name: Optional[StrOrRef] = null_omitted_field()
role: Optional[StrOrRef] = null_omitted_field()
parent_id: Optional[StrOrRef] = null_omitted_field()
has_any_role: Optional[bool] = null_omitted_field()
is_subscribed: Optional[bool] = null_omitted_field()


@dataclass
Expand Down
Loading