Create api.py
for any application which needs an api.
Create yourt methods:
import api
from api.models import Role
@api.method:
def describe_site(rl:Role)->str:
return 'My wonderful site'
These types are allowed:
None
str
bytes
bool
int
float
datetime
sent as a float number of seconds since 1 Jan 1971Enum
smodels.Model
prepared for use in your API - see here for details. A models.Model
configured for API are also known as a resource.@dataclass
classesList[]
sDict[]
sapi.Json
for treating dict
s & list
s as json.api_api.FileUpload
api.py
s in your site's applications@api.method
rl:Role
as their first parameter@api.method
name
name
is not given, the python function's name is used.require_host
(None
)This allows a check on request.META['HTTP_HOST']
. This can be:
str
@api.method(require_host='dev.mysite.com')
def describe_local_IP()->str
...
tuple
, list
or set
of str
@api.method(require_host=['dev.mysite.com'])
def describe_local_IP()->str
...
require_host(host:str)->bool
True
if the host is allowed, False
otherwise@api.method(require_host=[lambda host:host == 'dev.mysite.com'])
def describe_local_IP()->str
...
require_auth
(default True
)False
to allow this method for all callers regardless of what authorisation,
if any, has been provided. When this is False
, user_check
and read_only_resources
are ignored. Use require_auth=False
with caution!user_check
(default None
).user_check(user:UserModel)->bool
, which should return True
if the user is allowed to call this method. For example, some methods might only be allowed for calls between instances serving your website: user_check = lambda user: return user == home.site_user
read_only_resources
(None
, all resources are changed)describes which resources in the parameters are read only, ie not changed. This is used for checking a caller's permission. It can be:
str
of '0'
s and '1'
s'1'
means the resource is left unchanged by this method.@api.method(read_only_resources='01')
def modify_user_favourite(user:UserModel, repo:Repo, favourite:bool)->None
...
list
, tuple
or set
of classes@api.method(read_only_resources=[Repo])
def modify_user_favourite(user:UserModel, repo:Repo, favourite:bool)->None
...
read_only_resources(resources:List[resource])->str
Return a str
of '0'
s and '1'
s. Each '0'
and '1'
corresponds to a passed-in resource, where '1'
means the resource is left unchanged by this method.
@api.method(read_only_resources=lambda resources:'01') def modify_user_favourite(user:UserModel, repo:Repo, favourite:bool)->None ...
resource_access
(None
, all resources are fully accessed)describe the access to the passed-in resources. Access is classified into public, read
or full. Public is more permissive than read access, for example describe_user
might
require read access, as it gives information, such as the user's email address, which
the user might not want public, whereas describe_user_brief
might only give the
username, which is generally known.
It can be:
str
of 'p'
s, 'r'
s or 'f'
s{str: access}
owner
is a modified resource, the 'owner': 'f',
;
suppose argument fred
is a list of resources which are only read, then 'fred[]': 'r',
;
suppose betty
is a dataclass with resource field owner
used publicly, then 'betty.owner': 'p',
;
suppose complex
is a dictionary with resource keys publically used and resource values modified, then 'complex[key]': 'p', 'complex[]
: 'f','.{type: access}
prefer_query
(()
)external
(True
)ignore_resource_policies
(False
)Policy
's should be ignored for this method. The expected use case are the methods which adjust
a resource's Policy
list. If a resource has a Policy
added which is irrelevant to its Policy
-adjusting method
then, if the Policy
was followed, the method would become blocked, and so the resource's Policy
list would become
stuck. To prevent this, mark these methods ignore_resource_policies=True
.Your API can receive file uploads by using the type api_api.FileUpload
. Your methods will receive a FileUpload
. A FileUpload
has these properties:
name
file
content_type
To send files, pass FileUpload
s to the api_api API for your site
At time of writing requests
brings the file into memory before sending it, so avoid sending huge files this way.
At time of writing multi-select <input type="files" ..
are not supported through api_api - this is a limitation of requests
.
[note to author from author:
To fix requests
's sending of files to avoid bringing the whole file into memory first:
change urllib3.filepost.encode_multipart_formdata
to prepare the body to be a file-like object which returns a stream of bytes
. This stream should be the append of a series of bytes
and file-like objects - reading from the file-like objects only when the stream needs it.
change requests.models.RequestEncodingMixin._encode_files
to set fdata = fp
not fdata = fp.read()
]
A decorated method has these properties:
check_permission(role, ...)