diff --git a/.github/workflows/python-testing.yml b/.github/workflows/python-testing.yml index 30463e3..c0e1223 100644 --- a/.github/workflows/python-testing.yml +++ b/.github/workflows/python-testing.yml @@ -15,8 +15,8 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8] - os: [windows-2022, windows-2019, ubuntu-18.04, ubuntu-20.04, macos-10.15, macos-11] + python-version: [3.7, 3.9] + os: [windows-2022, ubuntu-22.04, macos-11] steps: - name: Checkout code uses: actions/checkout@v2 diff --git a/README.md b/README.md index 05d9daa..1bfb126 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Official documentation for the SDK is hosted at [here](https://github.com/mainfl ```sh pip install lazydocs requests python3 setup.py install -lazydocs --src-base-url="https://github.com/mainflux/sdk-py/blob/main/" --overview-file="README.md" lib +lazydocs --src-base-url="https://github.com/mainflux/sdk-py/blob/main/" --overview-file="README.md" mainflux ``` Please note that lazydocs requires Python version 3.5 or higher. diff --git a/docs/README.md b/docs/README.md index 00ece48..99543d5 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,7 +9,6 @@ - [`channels`](./channels.md#module-channels) - [`errors`](./errors.md#module-errors) - [`groups`](./groups.md#module-groups) -- [`keys`](./keys.md#module-keys) - [`messages`](./messages.md#module-messages) - [`response`](./response.md#module-response) - [`sdk`](./sdk.md#module-sdk) @@ -19,17 +18,16 @@ ## Classes -- [`boostrap.Bootstrap`](./boostrap.md#class-bootstrap) -- [`certs.Certs`](./certs.md#class-certs) -- [`channels.Channels`](./channels.md#class-channels) -- [`groups.Groups`](./groups.md#class-groups) -- [`keys.Keys`](./keys.md#class-keys) -- [`messages.Messages`](./messages.md#class-messages) +- [`boostrap.Bootstrap`](./boostrap.md#class-bootstrap): Bootstrap service API client. +- [`certs.Certs`](./certs.md#class-certs): Mainflux Certificates API +- [`channels.Channels`](./channels.md#class-channels): Channels class provides the abstraction of the Mainflux Channels API. +- [`groups.Groups`](./groups.md#class-groups): Groups class provides the abstraction of the Mainflux groups service API. +- [`messages.Messages`](./messages.md#class-messages): Messages API client - [`response.Error`](./response.md#class-error) - [`response.Response`](./response.md#class-response) - [`sdk.SDK`](./sdk.md#class-sdk) -- [`things.Things`](./things.md#class-things) -- [`users.Users`](./users.md#class-users) +- [`things.Things`](./things.md#class-things): Things API client. +- [`users.Users`](./users.md#class-users): Users API client. ## Functions diff --git a/docs/boostrap.md b/docs/boostrap.md index fa3f4a0..fc2fdcf 100644 --- a/docs/boostrap.md +++ b/docs/boostrap.md @@ -14,11 +14,23 @@ ## class `Bootstrap` +Bootstrap service API client. +Bootstrap service is used to manage configurations for Mainflux Things. It provides services such as updating, viewing, removing and adding new configurations. - +**Attributes:** + + - `url` (str): Mainflux Bootstrap API URL. + - `CONFIGS_ENDPOINT` (str): Configurations API endpoint. + - `BOOTSTRAP_ENDPOINT` (str): Bootstrap API endpoint. + - `WHITELIST_ENDPOINT` (str): Whitelist API endpoint. + - `BOOTSTRAP_CERTS_ENDPOINT` (str): Bootstrap certificates API endpoint. + + + + ### method `__init__` @@ -26,16 +38,27 @@ __init__(url: str) ``` +Initializes Bootstrap with the provided URL. + +params: url (str): Mainflux Bootstrap API URL. + +**returns:** + + - `Bootstrap`: Bootstrap object. +**raises:** + None + + --- - + ### method `add` @@ -45,9 +68,38 @@ add(config: dict, token: str) Adds new config to the list of config owned by user identified using the provided access token. +Some of the key data needed include the external_key and external_id which must be specific to the thing provided with the thing_id. Mind that every configuration must have a specific thing_id. + +params: config (dict): Configuration data for example: { "external_id": "123", "external_key": "456", "thing_id": "fdb1057c-2905-4f71-9a80-e0ce9191e667", "name": "thing_name" } token (str): Authorization token. + + + +**returns:** + + - `mf_response `: response.Response. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(bootstrap_url="http`: //localhost:9013") + >>> config = { + + - ` ... "external_id"`: "123", + + - ` ... "external_key"`: "456", + + - ` ... "thing_id"`: "fdb1057c-2905-4f71-9a80-e0ce9191e667", + + - ` ... "name"`: "thing_name" + ... } + >>> mf_resp = mfsdk.bootstrap.add(config, token) + >>> mf_resp + + --- - + ### method `bootstrap` @@ -57,9 +109,28 @@ bootstrap(external_id: str, external_key: str) Retrieves a configuration with given external ID and external key. +params: external_id (str): External ID. external_key (str): External key. + + + +**returns:** + + - `mf_resp `: response.Response - response object. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(bootstrap_url="http`: //localhost:9013") + >>> external_id = "external_id" + >>> external_key = "external_key" + >>> mf_resp = mfsdk.bootstrap.bootstrap(external_id, external_key) + >>> mf_resp + + --- - + ### method `remove` @@ -69,9 +140,27 @@ remove(config_id: str, token: str) Removes a Config. In case of successful removal the service will ensure that the removed config is disconnected from all the Mainflux channels. +params: config_id (str): Configuration ID. token (str): Authorization token. + + + +**returns:** + + - `mf_response `: response.Response. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(bootstrap_url="http`: //localhost:9013") + >>> config_id = "config_id" + >>> mf_resp = mfsdk.bootstrap.remove(config_id, token) + >>> mf_resp + + --- - + ### method `update` @@ -81,9 +170,38 @@ update(config: dict, token: str) Update is performed by replacing the current resource data with values provided in a request payload. Note that the owner, ID, external ID, external key, Mainflux Thing ID and key cannot be changed. +params: config (dict): Configuration data for example: { "external_id": "123", "external_key": "456", "thing_id": "fdb1057c-2905-4f71-9a80-e0ce9191e667", "name": "thing_name" } token (str): Authorization token. + + + +**returns:** + + + + - `mf_response `: response.Response. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(bootstrap_url="http`: //localhost:9013") + >>> config = { + + - ` ... "external_id"`: "123", + + - ` ... "external_key"`: "456", + + - ` ... "thing_id"`: "fdb1057c-2905-4f71-9a80-e0ce9191e667", + + - ` ... "name"`: "thing_name" + ... } + >>> mf_resp = mfsdk.bootstrap.update(config, token) + >>> mf_resp + + --- - + ### method `update_certs` @@ -99,21 +217,62 @@ update_certs( Update is performed by replacing the current certificate data with values provided in a request payload. +params: config_id (str): Configuration ID. client_cert (str): Client certificate. client_key (str): Client key. ca (str): CA certificate. token (str): Authorization token. + + + +**returns:** + + - `mf_response `: response.Response. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(bootstrap_url="http`: //localhost:9013") + >>> config_id = "config_id" + >>> client_cert = "client_cert" + >>> client_key = "client_key" + >>> ca = "ca" + >>> mf_resp = mfsdk.bootstrap.update_certs(config_id, client_cert, client_key, ca, token) + >>> mf_resp + + --- - + ### method `view` ```python -view(config_id: str, token: str) +view(thing_id: str, token: str) ``` Retrieves a configuration with given config id +Provides a configuration with given config id. + +params: thing_id (str): Thing ID. token (str): Authorization token. + + + +**returns:** + + - `mf_resp `: response.Response - response object. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(bootstrap_url="http`: //localhost:9013") + >>> thing_id = "thing_id" + >>> mf_resp = mfsdk.bootstrap.view(thing_id, token) + >>> mf_resp + + --- - + ### method `whitelist` @@ -123,6 +282,33 @@ whitelist(config: dict, token: str) Updating state represents enabling/disabling Config, i.e.connecting and disconnecting corresponding Mainflux Thing to the list of Channels. +params: config (dict): Configuration data for example: { "external_id": "123", "external_key": "456", "thing_id": "fdb1057c-2905-4f71-9a80-e0ce9191e667", "name": "thing_name" } token (str): Authorization token. + + + +**returns:** + + - `mf_response `: response.Response. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(bootstrap_url="http`: //localhost:9013") + >>> config = { + + - ` ... "external_id"`: "123", + + - ` ... "external_key"`: "456", + + - ` ... "thing_id"`: "fdb1057c-2905-4f71-9a80-e0ce9191e667", + + - ` ... "name"`: "thing_name" + ... } + >>> mf_resp = mfsdk.bootstrap.whitelist(config, token) + >>> mf_resp + + diff --git a/docs/certs.md b/docs/certs.md index 73a21e1..c096cd4 100644 --- a/docs/certs.md +++ b/docs/certs.md @@ -14,11 +14,18 @@ ## class `Certs` +Mainflux Certificates API +Certs is used to issue, view, and revoke certificates. It is used to issue certificates for things. - +**Args:** + + - `url` (str): Mainflux Certificates API URL. + - `CERTS_ENDPOINT` (str): Certificates API endpoint. + + ### method `__init__` @@ -26,30 +33,61 @@ __init__(url: str) ``` +Initializes Certs with the provided URL. + + +**Args:** + + - `url` (str): Mainflux Certificates API URL. +**Returns:** + + - `Certs`: Certs object. + --- - + ### method `issue` ```python -issue(thing_id: str, key_bits: int, key_type: str, valid: str, token: str) +issue(thing_id: str, valid: str, token: str) ``` +Issues a certificate for a given thing ID. + + +**Args:** + + - `thing_id` (str): Thing ID. + - `valid` (str): Certificate validity period. + - `token` (str): Authorization token. +**Returns:** + + - `Response`: Mainflux response. + +Usage: ``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(certs_url="http`: //localhost:9019") + >>> thing_id = "thing_id" + >>> valid = "1h" + >>> mf_resp = mfsdk.certs.issue(thing_id, valid) + >>> mf_resp + + --- - + ### method `revoke` @@ -57,21 +95,93 @@ issue(thing_id: str, key_bits: int, key_type: str, valid: str, token: str) revoke(thing_id: str, token: str) ``` +Revokes a certificate for a given thing ID. + +Deletes a certificate for a given thing ID and valid token. +params: thing_id (str): thing id token (str): valid authorization token used to delete the certificate +**Returns:** + + - `mf_resp `: response.Response - response object. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(certs_url="http`: //localhost:9019") + >>> thing_id = "thing_id" + >>> mf_resp = mfsdk.certs.revoke(thing_id) + >>> mf_resp + + --- - + -### method `view` +### method `view_by_serial` ```python -view(thing_id: str, token: str) +view_by_serial(cert_id: str, token: str) ``` -Generates an access token when provided with proper credentials. +Retrieves a certificate for a given cert ID. + +Provides a certificate for a given cert ID. + +Params: + + cert_id (str): Certificate ID. token (str): Authorization token. + + + +**Returns:** + + - `mf_resp `: response.Response - response object. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(certs_url="http`: //localhost:9019") + >>> cert_id = "cert_id" + >>> mf_resp = mfsdk.certs.view_by_serial(cert_id) + >>> mf_resp + + +--- + + + +### method `view_by_thing` + +```python +view_by_thing(thing_id: str, token: str) +``` + +Retrieves a list of certificates' serial IDs for a given thing ID. + +Provides a list of certificates' serial IDs for a given thing ID. + +Params: thing_id (str): Thing ID. token (str): Authorization token. + + + +**Returns:** + + - `mf_resp `: response.Response - response object. + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(certs_url="http`: //localhost:9019") + >>> thing_id = "thing_id" + >>> mf_resp = mfsdk.certs.view_by_thing(thing_id) + >>> mf_resp + diff --git a/docs/channels.md b/docs/channels.md index 0f30e84..85e66cf 100644 --- a/docs/channels.md +++ b/docs/channels.md @@ -14,11 +14,29 @@ ## class `Channels` +Channels class provides the abstraction of the Mainflux Channels API. +Channels are used to connect things and users. They are used to send messages to things and receive messages from things. Channels API provides the following functionalities: + - create channel + - create multiple channels in a bulk + - get channel + - get all channels + - get all channels to which a specific thing is connected to + - update channel + - delete channel + - identify thing - +**Attributes:** + + - `CHANNELS_ENDPOINT` (str): Channels API endpoint + - `THINGS_ENDPOINT` (str): Things API endpoint + - `IDENTIFY_ENDPOINT` (str): Identify API endpoint + + + + ### method `__init__` @@ -26,16 +44,31 @@ __init__(url: str) ``` +Initializes Channels class with the provided url + + + +**Args:** + + - `url` (str): Mainflux Channels API URL +**returns:** + + - `Channels`: Channels object initialized with the provided url. + + + +**raises:** + None --- - + ### method `create` @@ -45,9 +78,32 @@ create(channel: dict, token: str) Creates channel entity in the database +Creates a new channel in the database when provided with a valid token. + +params: channel (dict): Channel entity to be created for example: { "name": "channel_name", "metadata": { "description": "channel_description" } } token (str): User's token + + + +**returns:** + + - `Response`: Response object containing the response from the server + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(channels_url="http`: //localhost:9000") + >>> channel = { + + - ` ... "name"`: "channel_name" + ... } + >>> mf_resp = mfsdk.channels.create(channel, token) + >>> mf_resp + + --- - + ### method `create_bulk` @@ -55,23 +111,78 @@ Creates channel entity in the database create_bulk(channels: list, token: str) ``` -Creates multiple channels in a bulk +Creates multiple channels in bulk + +Creates multiple new channels when provided with channels information and a valid token. + +params: channels: list- Channel entities to be created for example: [ { "name": "channel_name", "metadata": { "description": "channel_description" } }, { "name": "channel_name", "metadata": { "description": "channel_description" } } ] + + token (str): User's token + + + +**returns:** + + - `Response`: Response object containing the response from the server + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(channels_url="http`: //localhost:9000") + >>> channels = [ + ... { + + - ` ... "name"`: "channel_name" + ... }, + ... { + + - ` ... "name"`: "channel_name" + ... } + ... ] + >>> mf_resp = mfsdk.channels.create_bulk(channels, token) + >>> mf_resp + --- - + -### method `delete` +### method `disable` ```python -delete(channel_id: str, token: str) +disable(channel_id: str, token: str) ``` -Deletes a channel entity from database +Deletes a channel entity from database. + +Deletes a channel entity from database when provided with a valid channel ID and token. The channel is not deleted from the database but is marked as disabled. + +params: + + channel_id (str): Channel ID token (str): User's token + + + +**returns:** + + + + - `mf_resp`: response.Response -response object + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(channels_url="http`: //localhost:9000") + >>> channel_id = "channel_id" + >>> mf_resp = mfsdk.channels.disable(channel_id, token) + >>> mf_resp + --- - + ### method `get` @@ -81,9 +192,29 @@ get(channel_id: str, token: str) Gets a channel entity for a logged-in user +Provides a channel entity when provided with a valid channel ID and token. + +params: channel_id (str): Channel ID token (str): User's token + + + +**returns:** + + - `Response`: Response object + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(channels_url="http`: //localhost:9000") + >>> channel_id = "channel_id" + >>> mf_resp = mfsdk.channels.get(channel_id, token) + >>> mf_resp + + --- - + ### method `get_all` @@ -93,9 +224,34 @@ get_all(query_params: dict, token: str) Gets all channels from database +Gets all channels from database when provided with a valid token.. + +params: query_params (dict): Query parameters for example: { "offset": 0, "limit": 10 } token (str): User's token + + + +**returns:** + + - `mf_resp`: response.Response -response object + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(channels_url="http`: //localhost:9000") + >>> query_params = { + + - ` ... "offset"`: 0, + + - ` ... "limit"`: 10 + ... } + >>> mf_resp = mfsdk.channels.get_all(query_params, token) + >>> mf_resp + + --- - + ### method `get_by_thing` @@ -103,11 +259,37 @@ Gets all channels from database get_by_thing(thing_id: str, query_params: dict, token: str) ``` -Gets all channels to which a specific thing is connected to +Gets all channels to which a specific thing is connected to. + +Provides a list of all the channels a thing is connected to when provided with a valid token and thing ID. + +params: thing_id (str): Thing ID query_params (dict): Query parameters for example: { "offset": 0, "limit": 10 } token (str): User's token + + + +**returns:** + + - `mf_resp`: response.Response -response object + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(channels_url="http`: //localhost:9000") + >>> thing_id = "thing_id" + >>> query_params = { + + - ` ... "offset"`: 0, + + - ` ... "limit"`: 10 + ... } + >>> mf_resp = mfsdk.channels.get_by_thing(thing_id, query_params, token) + >>> mf_resp + --- - + ### method `identify_thing` @@ -117,9 +299,29 @@ identify_thing(thing_key: str) Validates thing's key and returns it's ID if key is valid +Uses a thing_key or secret to validate a thing and provide its information. + +params: thing_key (str): Thing's key + + + +**returns:** + + - `mf_resp`: response.Response -response object + +Usage: + +``` from mainflux import sdk ``` + + - ` >>> mfsdk = sdk.SDK(channels_url="http`: //localhost:9000") + >>> thing_key = "thing_key" + >>> mf_resp = mfsdk.channels.identify_thing(thing_key) + >>> mf_resp + + --- - + ### method `update` @@ -129,6 +331,30 @@ update(channel_id: str, channel: dict, token: str) Updates channel entity +Updates a channel entity when provided with a valid channel ID, channel entity and token. The information that can be updated are channel's name and metadata. + +params: channel_id (str): Channel ID channel (dict): Channel entity to be updated for example: { "name": "channel_name", "metadata": { "description": "channel_description" } } token (str): User's token + + + +**returns:** + + - `mf_resp`: response.Response -response object + +Usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.SDK(channels_url="http`: //localhost:9000") + >>> channel_id = "channel_id" + >>> channel = { + + - ` ... "name"`: "channel_name" + ... } + >>> mf_resp = mfsdk.channels.update(channel_id, channel, token) + >>> mf_resp + + diff --git a/docs/errors.md b/docs/errors.md index 6a1bf99..b981d57 100644 --- a/docs/errors.md +++ b/docs/errors.md @@ -15,7 +15,7 @@ - **channels** - **messages** - **groups** -- **boostrap** +- **bootstrap** - **certs** --- diff --git a/docs/groups.md b/docs/groups.md index 7474a36..f80db33 100644 --- a/docs/groups.md +++ b/docs/groups.md @@ -11,14 +11,21 @@ --- - + ## class `Groups` +Groups class provides the abstraction of the Mainflux groups service API. +Groups class provides the following functionality: create, get, get_all, parents, children, update, members, memberships, assign, unassign, disable. - +**Attributes:** + + - `URL`: Mainflux groups service URL. + - `GROUPS_ENDPOINT`: Mainflux groups service API endpoint. + + ### method `__init__` @@ -35,19 +42,34 @@ __init__(url: str) --- - + ### method `assign` ```python -assign(group_id: str, members_ids, member_type: str, token: str) +assign(group_id: str, member_id: str, member_type: list, token: str) ``` Assign +Assigns a member to a group when provided with group_id, member_id, member_type which is their action and a token. + +params: group_id: str - group id member_id: str - member id member_type: list - actions the member can perform for example: [ "m_read", "m_write", "m_admin" ] token: str - token used to assign a member to a group. + + + +**returns:** + + - `mf_resp`: "Policy created" + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> group_id = "group_id" >>> member_id = "member_id" >>> member_type = [ ... "m_read", "m_write", "m_admin" ... ] >>> mf_resp = mfsdk.groups.assign(group_id, member_id, member_type) >>> mf_resp + --- - + ### method `children` @@ -57,9 +79,24 @@ children(group_id: str, query_params: dict, token: str) Gets children for a specific group from database +Provides information about children for a specific group from database when provided with group_id, query_params and token. + +params: group_id: str - group id of the parent group in question. query_params: dict - query parameters for example: { "offset": 0, "limit": 10, } where offset is the number of items to skip before starting to collect the result set and limit is the numbers of items to return. token: str - token used to get children for a specific group. + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> group_id = "group_id" >>> query_params = { ... "offset": 0, ... "limit": 10, ... } >>> mf_resp = mfsdk.groups.children(group_id, query_params) >>> mf_resp + --- - + ### method `create` @@ -67,23 +104,51 @@ Gets children for a specific group from database create(group: dict, token: str) ``` -Creates group entity in the database +Creates a group entity in the database. + +Creates a group entity in the database using the provided group object and token. + +params: group: dict - group information for example: { "name": "groupName", } token: str - token used to create a new group. + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> group = { ... "name": "groupName", ... } >>> mf_resp = mfsdk.groups.create(group) >>> mf_resp --- - + -### method `delete` +### method `disable` ```python -delete(group_id: str, token: str) +disable(group_id: str, user_token: str) ``` -Deletes a group entity from database +Disables a group entity from database + +Deletes a group from the database when provided with group_id and user_token. + +params: group_id: str - group id user_token: str - token used to delete a group. + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> group_id = "group_id" >>> mf_resp = mfsdk.groups.disable(group_id) >>> mf_resp --- - + ### method `get` @@ -93,9 +158,24 @@ get(group_id: str, token: str) Gets a group entity +Provides information about a group entity using the provided group_id and a token. + +params: group_id: str - group id token: str - token used to get a group. + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> group_id = "group_id" >>> mf_resp = mfsdk.groups.get(group_id) >>> mf_resp + --- - + ### method `get_all` @@ -105,9 +185,24 @@ get_all(query_params: dict, token: str) Gets all groups from database +Gets all groups from database using the provided query_params and token. + +params: query_params: dict - query parameters for example: { "offset": 0, "limit": 10, } where offset is the number of items to skip before starting to collect the result set and limit is the numbers of items to return. token: str - token used to get all groups. + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> query_params = { ... "offset": 0, ... "limit": 10, ... } >>> mf_resp = mfsdk.groups.get_all(query_params) >>> mf_resp + --- - + ### method `members` @@ -115,11 +210,26 @@ Gets all groups from database members(group_id: str, query_params: dict, token: str) ``` -Get list of members ID's from group +Gets members associated with the group specified by id + +Provides information about members associated with the group specified by id when provided with group_id, query_params and token. + +params: group_id: str - group id query_params: dict - query parameters for example: { "offset": 0, "limit": 10, } where offset is the number of items to skip before starting to collect the result set and limit is the numbers of items to return. token: str - token used to get members associated with the group specified by id. + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> group_id = "group_id" >>> query_params = { ... "offset": 0, ... "limit": 10, ... } >>> mf_resp = mfsdk.groups.members(group_id, query_params) >>> mf_resp --- - + ### method `memberships` @@ -127,11 +237,24 @@ Get list of members ID's from group memberships(member_id: str, query_params: dict, token: str) ``` -Get list of members ID's from group +Retrieves a list of groups the user is connected to + +Retrieves a list of groups the user is connected to when provided with the users id, query_params and token. + +params: member_id: str - user id query_params: dict - query parameters for example: { "offset": 0, "limit": 10, } where offset is the number of items to skip before starting to collect the result set and limit is the numbers of items to return. token: str - token used to retrieve a list of groups the user is connected to. + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> member_id = "member_id" >>> query_params = { ... "offset": 0, ... "limit": 10, ... } >>> mf_resp = mfsdk.groups.memberships(member_id, query_params) >>> mf_resp --- - + ### method `parents` @@ -141,21 +264,24 @@ parents(group_id: str, query_params: dict, token: str) Gets parents for a specific group from database ---- +Provides information about parents for a specific group from database when provided with group_id, query_params and token. - +params: group_id: str - group id of the child group in question. query_params: dict - query parameters for example: { "offset": 0, "limit": 10, } where offset is the number of items to skip before starting to collect the result set and limit is the numbers of items to return. token: str - token used to get parents for a specific group. -### method `share_groups` -```python -share_groups(token: str, user_group_id: str, thing_group_id: str) -``` -Adds access rights on thing groups to the user group +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> group_id = "group_id" >>> query_params = { ... "offset": 0, ... "limit": 10, ... } >>> mf_resp = mfsdk.groups.parents(group_id, query_params) >>> mf_resp --- - + ### method `unassign` @@ -163,11 +289,24 @@ Adds access rights on thing groups to the user group unassign(group_id: str, token: str, members_ids) ``` -Assign +Unassign + +Deletes a user's policy from over a group when provided with group_id, token and members_ids. + +params: group_id: str - group id token: str - token used to delete a user's policy from over a group. members_ids: str - member id + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> group_id = "group_id" >>> members_ids = "members_ids" >>> mf_resp = mfsdk.groups.unassign(group_id, members_ids) >>> mf_resp --- - + ### method `update` @@ -177,6 +316,21 @@ update(group_id: str, group: dict, token: str) Updates group entity +Updates a group entity in the database using the provided group_id, group object and token. It updates the group name and metadata. + +params: group_id: str - group id group: dict - group information for example: { "name": "groupName", } token: str - token used to update a group. + + + +**returns:** + + + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> group_id = "group_id" >>> group = { ... "name": "groupName", ... } >>> mf_resp = mfsdk.groups.update(group_id, group) >>> mf_resp + diff --git a/docs/keys.md b/docs/keys.md deleted file mode 100644 index 666e350..0000000 --- a/docs/keys.md +++ /dev/null @@ -1,77 +0,0 @@ - - - - -# module `keys` - - - - - - ---- - - - -## class `Keys` - - - - - - -### method `__init__` - -```python -__init__(url: str) -``` - - - - - - - - ---- - - - -### method `get_key_details` - -```python -get_key_details(key_id: str, token: str) -``` - -Gets API key details for the given key - ---- - - - -### method `issue` - -```python -issue(duration: str, token: str) -``` - -Generates a new API key - ---- - - - -### method `revoke` - -```python -revoke(key_id: str, token: str) -``` - -Revoke API key identified by the given ID. - - - - ---- - -_This file was automatically generated via [lazydocs](https://github.com/ml-tooling/lazydocs)._ diff --git a/docs/messages.md b/docs/messages.md index 607309f..b6403e8 100644 --- a/docs/messages.md +++ b/docs/messages.md @@ -14,11 +14,18 @@ ## class `Messages` +Messages API client +Messages API client enables interaction with Mainflux Messages API. It provides methods for sending and reading messages. - +**Attributes:** + + - `adapter_url`: URL of the Mainflux Messages adapter + - `reader_url`: URL of the Mainflux Messages reader + + ### method `__init__` @@ -35,7 +42,7 @@ __init__(adapter_url: str, reader_url: str) --- - + ### method `read` @@ -45,18 +52,61 @@ read(channel_id: str, token: str) Reads messages from database for a given channel +Reads message from a given channel via HTTP protocol. Message is read through a reader add-on such as timescale. + +params: channel_id: ID of the channel to read message from token: token of the user reading the message + + + +**returns:** + + - `mf_resp`: response object + +usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.Sdk("http`: //localhost:9011") + >>> channel_id = "2b86beba-83dd-4b39-8165-4dda4e6eb4ad" + >>> mf_resp = mfsdk.messages.read(channel_id, token) + >>> mf_resp + + --- - + ### method `send` ```python -send(channel_id: str, msg: dict, thing_key: str) +send(channel_id: str, msg: str, thing_key: str) ``` Sends message via HTTP protocol +Sends message to a given channel via HTTP protocol. Message is sent through a writer add-on such as timescale. Message is sent to a http port specific to the writer add-on. The thing and channel must be created before sending the message and connected. + +params: channel_id: ID of the channel to send message to msg: message to send to the channel that should be in encoded into bytes format for example: [{"bn":"demo", "bu":"V", "n":"voltage", "u":"V", "v":5}] thing_key: secret of the thing sending the message + + + +**returns:** + + - `mf_resp`: response object + +usage: + +``` from mainflux import sdk``` + + - ` >>> mfsdk = sdk.Sdk("http`: //localhost:9011") + >>> channel_id = "2b86beba-83dd-4b39-8165-4dda4e6eb4ad" + + - ` >>> msg = '[{"bn"`: "demo", "bu":"V", "n":"voltage", "u":"V", "v":5}]' + >>> thing_key = "fc68b31b-d7fd-4879-b3a7-0baf4580c5b1" + >>> mf_resp = mfsdk.messages.send(channel_id, msg, thing_key) + >>> mf_resp + + diff --git a/docs/sdk.md b/docs/sdk.md index 04e26c7..6f3182f 100644 --- a/docs/sdk.md +++ b/docs/sdk.md @@ -14,14 +14,14 @@ --- - + ## class `SDK` - + ### method `__init__` @@ -33,7 +33,7 @@ __init__( http_adapter_url='http://localhost', certs_url='http://localhost', bootstrap_url='http://localhost', - auth_url='http://localhost' + groups_url='http://localhost' ) ``` @@ -46,7 +46,7 @@ __init__( --- - + ### method `version` diff --git a/docs/things.md b/docs/things.md index 2c2e712..0af7178 100644 --- a/docs/things.md +++ b/docs/things.md @@ -11,14 +11,21 @@ --- - + ## class `Things` +Things API client. +Things API is used for creating and managing things. It is used for creating new things, creating multiple things getting thing information, updating thing information, disabling and enabling things ,and connecting and disconnecting things. - +**Attributes:** + + - `URL`: str - URL of the Things API + - `THINGS_ENDPOINT`: str - Things API endpoint + + ### method `__init__` @@ -35,31 +42,90 @@ __init__(url: str) --- - + + +### method `authorise_thing` + +```python +authorise_thing(access_request: dict, token: str) +``` + +Authorises thing. + +Creates policies for a thing as a subject over a channel which is the object. It authorizes the thing to perform some actions over the channel. + +params: + + access_request: dict - access request information for example: { "subject": "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9", "object": "567f7da5-b7bf-49b7-bf2f-99995e78afd9", "actions": "m_write" "entity_type": "group" } token: str - token used for authorising thing + + + +**returns:** + + - `mf_resp`: "True" + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> access_request = { ... "subject": "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9", ... "object": "567f7da5-b7bf-49b7-bf2f-99995e78afd9", ... "actions": "m_write" ... "entity_type": "group" ... } >>> mf_resp = mfsdk.things.authorise_thing(access_request) >>> mf_resp + +--- + + ### method `connect` ```python -connect(thing_id: str, channel_id: str, token: str) +connect(thing_id: str, channel_id: str, action: str, token: str) ``` -Connects thing and channel +Connects thing and channel. + +Connects a thing and channel with provided thing ID as the subject, channel ID as the object, action that the thing can partake in and a valid token. + +params: thing_id: str - ID of the thing channel_id: str - ID of the channel action: str - action for example: "m_write" token: str - token used for connecting thing and channel + + + +**returns:** + + - `mf_resp`: "connected" + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> channel_id = "567f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> action = "m_write" >>> mf_resp = mfsdk.things.connect(thing_id, channel_id, action) >>> mf_resp --- - + ### method `connects` ```python -connects(thing_ids: list, channel_ids: list, token: str) +connects(thing_ids: list, channel_ids: list, actions: list, token: str) ``` -Connects thing and channel +Connects things and channels. + +Connects multiple things and channels with provided thing IDs as the subjects, channel IDs as the objects, actions that the thing can partake in and a valid token. + +params: thing_ids: list - list of thing IDs channel_ids: list - list of channel IDs actions: list - list of actions for example: ["m_write", "m_read"] token: str - token used for connecting things and channels + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_ids = ["fd4f7da5-b7bf-49b7-bf2f-99995e78afd9"] >>> channel_ids = ["567f7da5-b7bf-49b7-bf2f-99995e78afd9"] >>> actions = ["m_write", "m_read"] >>> mf_resp = mfsdk.things.connects(thing_ids, channel_ids, actions) >>> mf_resp --- - + ### method `create` @@ -67,11 +133,24 @@ Connects thing and channel create(thing: dict, token: str) ``` -Creates thing entity in the database +Creates thing entity in the database. + +Creates a new thing with provided thing information. If token is provided, it will be used to create a new thing + +params: thing: dict - thing information for example: { "name": "thing1" } token: str - token used for creating a new thing + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing = { ... "name": "thing1", ... } >>> mf_resp = mfsdk.things.create(thing) >>> mf_resp --- - + ### method `create_bulk` @@ -79,23 +158,49 @@ Creates thing entity in the database create_bulk(things: list, token: str) ``` -Creates multiple things in a bulk +Creates multiple things in bulk. + +Creates multiple new things with provided things information. If a token is provided, it will be used to create the new things. + +params: things: list - a list of things with theri information for example: [ {"name": "thing2"}, {"name": "thing3"}, {"name": "thing4"} ] token: str - token used for creating the new things. + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> things = [ ... {"name": "thing2"}, ... {"name": "thing3"}, ... {"name": "thing4"} ... ] >>> mf_resp = mfsdk.things.create_bulk(things) >>> mf_resp --- - + -### method `delete` +### method `disable` ```python -delete(thing_id: str, token: str) +disable(thing_id: str, token: str) ``` -Deletes a thing entity from database +Deletes a thing entity from the database. + +Deletes a thing with provided thing ID and valid token. + +params: thing_id: str - ID of the thing token: str - token used for deleting thing + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> mf_resp = mfsdk.things.disable(thing_id) >>> mf_resp --- - + ### method `disconnect` @@ -103,11 +208,26 @@ Deletes a thing entity from database disconnect(thing_id: str, channel_id: str, token: str) ``` -Disconnect thing and channel +Disconnects thing and channel. + +Disconnects a thing and channel with provided thing ID as the subject, channel ID as the object and a valid token. + +params: thing_id: str - ID of the thing channel_id: str - ID of the channel token: str - token used for disconnecting thing and channel + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> channel_id = "567f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> mf_resp = mfsdk.things.disconnect(thing_id, channel_id) >>> mf_resp --- - + ### method `disconnects` @@ -115,11 +235,26 @@ Disconnect thing and channel disconnects(thing_ids: list, channel_ids: list, token: str) ``` -Disconnect thing and channel +Disconnect things and channels. + +Disconnects multiple things and channels with provided thing IDs as the subjects, channel IDs as the objects and a valid token. + +params: thing_ids: list - list of thing IDs channel_ids: list - list of channel IDs token: str - token used for disconnecting things and channels + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_ids = ["fd4f7da5-b7bf-49b7-bf2f-99995e78afd9"] >>> channel_ids = ["567f7da5-b7bf-49b7-bf2f-99995e78afd9"] >>> mf_resp = mfsdk.things.disconnects(thing_ids, channel_ids) >>> mf_resp --- - + ### method `get` @@ -127,11 +262,24 @@ Disconnect thing and channel get(thing_id: str, token: str) ``` -Gets a thing entity for a logged-in user +Gets a thing entity. + +Provides information about a thing with provided thing ID and token. Information about a thing is provided in a JSON format and includes the name its owner, secret,tags and status. + +params: thing_id: str - ID of the thing token: str - token used for getting thing information + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> mf_resp = mfsdk.things.get(thing_id) >>> mf_resp --- - + ### method `get_all` @@ -139,11 +287,24 @@ Gets a thing entity for a logged-in user get_all(query_params: dict, token: str) ``` -Gets all things from database +Gets all things from database. + +Provides information about all things in a JSON format. It is controlled by a set of query parameters and a valid token. + +params: query_params: dict - query parameters for example: { "offset": 0, "limit": 10 } where offset is the number of things to skip and limit is the maximum token: str - token used for getting all things information + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> query_params = { ... "offset": 0, ... "limit": 10 ... } >>> mf_resp = mfsdk.things.get_all(query_params) >>> mf_resp --- - + ### method `get_by_channel` @@ -151,11 +312,47 @@ Gets all things from database get_by_channel(channel_id: str, query_params: dict, token: str) ``` -Gets all things to which a specific thing is connected to +Gets all things to which a specific thing is connected to. + +Provides a list of all things that are connected to a specific channel when given a channel ID and valid token. + +params: channel_id: str - ID of the channel query_params: dict - query parameters for example: { "offset": 0, "limit": 10 } where offset is the number of things to skip and limit is the maximum token: str - token used for getting all things information + +returns: mf_resp: response.Response - response object. + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> channel_id = "567f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> query_params = { ... "offset": 0, ... "limit": 10 ... } >>> mf_resp = mfsdk.things.get_by_channel(channel_id, query_params) >>> mf_resp + +--- + + + +### method `share_thing` + +```python +share_thing(user_id: str, channel_id: str, actions: list, token: str) +``` + +Shares thing. + +Allows a logged in user to create new policies for a thing over a channel provided with a user ID, channel ID, actions that the thing can partake in and a valid token. + +params: user_id: str - ID of the user channel_id: str - ID of the channel actions: list - list of actions for example: ["m_write", "m_read"] token: str - token used for sharing thing + + + +**returns:** + + - `mf_resp`: "OK" + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> user_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> channel_id = "567f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> actions = ["m_write", "m_read"] >>> mf_resp = mfsdk.things.share_thing(user_id, channel_id, actions) >>> mf_resp --- - + ### method `update` @@ -163,7 +360,101 @@ Gets all things to which a specific thing is connected to update(thing_id: str, thing: dict, token: str) ``` -Updates thing entity +Updates thing entity. + +Allows a logged in user to make changes and update a thing's information with provided thing ID and valid token. Information such as the metadata and name can be updated. + +params: thing_id: str - ID of the thing thing: dict - thing information for example: { "name": "thing1" } token: str - token used for updating thing information + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> thing = { ... "name": "thing2", ... } >>> mf_resp = mfsdk.things.update(thing_id, thing) >>> mf_resp + +--- + + + +### method `update_thing_owner` + +```python +update_thing_owner(thing_id: str, thing: dict, token: str) +``` + +Updates thing owner. + +Allows a logged in user to make changes and update a thing's information with provided thing ID and valid token. The thing owner can be updated. + +params: thing_id: str - ID of the thing thing: dict - thing information for example: { "owner": "user1" } token: str - token used for updating thing information + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> thing = { ... "owner": "user1" ... } >>> mf_resp = mfsdk.things.update_thing_owner(thing_id, thing) >>> mf_resp + +--- + + + +### method `update_thing_secret` + +```python +update_thing_secret(thing_id: str, thing: dict, token: str) +``` + +Updates thing secret. + +Allows a logged in user to make changes and update a thing's information with provided thing ID and valid token. The thing's secret can be updated. + +params: thing_id: str - ID of the thing thing: dict - thing information for example: { "key": "thing1" } token: str - token used for updating thing information + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> thing = { ... "key": "thing2", ... } >>> mf_resp = mfsdk.things.update_thing_secret(thing_id, thing) >>> mf_resp + +--- + + + +### method `update_thing_tags` + +```python +update_thing_tags(thing_id: str, thing: dict, token: str) +``` + +Updates thing tags. + +Allows a logged in user to make changes and update a thing's information with provided thing ID and valid token. The thing's tags can be updated. + +params: thing_id: str - ID of the thing thing: dict - thing information for example: { "tags": ["tag1", "tag2"] } token: str - token used for updating thing information + + + +**returns:** + + - `mf_resp`: response.Response - response object. + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" >>> thing = { ... "tags": ["tag1", "tag2"] ... } >>> mf_resp = mfsdk.things.update_thing_tags(thing_id, thing) >>> mf_resp diff --git a/docs/users.md b/docs/users.md index 622c303..837aacd 100644 --- a/docs/users.md +++ b/docs/users.md @@ -14,11 +14,18 @@ ## class `Users` +Users API client. +Users API is used for creating and managing users. It is used for creating new users, logging in, refreshing tokens, getting user information, updating user information, disabling and enabling users. - +**Attributes:** + + - `URL`: str - URL of the Users API + - `USERS_ENDPOINT`: str - Users API endpoint + + ### method `__init__` @@ -35,43 +42,105 @@ __init__(url: str) --- - + + +### method `authorise_user` + +```python +authorise_user(access_request: dict, token: str) +``` + +Authorises user + +Creates policies for a user as a subject over a group which is the object. It authorizes the User to perform some actions over the group. + +params: access_request = { "subject": "", "object": "", "action": "", "entity_type": "" } token: strOnly admin can use this endpoint. Therefore, you need an authentication token for the admin. Also, only policies defined on the system are allowed to add. + + + +**returns:** + + - `mf_resp`: "True" + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> access_request = { ... "subject": "", ... "object": "", ... "action": "", ... "entity_type": "" ... } >>> mf_resp = mfsdk.users.authorise_user(access_request, token) >>> mf_resp + +--- + + ### method `create` ```python -create(user: dict) +create(user: dict, token: str = '') ``` -Registers new user account given email and password. New account will be uniquely identified by its email address. +Creates a new user. + +Creates a new user with provided user information. If token is provided, it will be used to create a new user. + +params: user: dict - user information for example: { "name": "example", "credentials": { "identity": "example@main.com", "secret": "12345678" } } token: str - token used for creating a new user + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> user = { ... "name": "example", ... "credentials": { ... "identity": "example@mail.com", ... "secret": "12345678" ... } ... } >>> mf_resp = mfsdk.users.create(user) >>> mf_resp --- - + ### method `disable` ```python -disable(user_id: str, admin_token: str) +disable(user_id: str, user_token: str) ``` -Disables an enabled user account for a given user ID. +Disables an enabled user account for a given user ID. + +params: user_id: str - the user's given ID. token: str - token used for enabling a user + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> user_id= "886b4266-77d1-4258-abae-2931fb4f16de" >>> mf_resp = mfsdk.users.disable(user_id, user_token) >>> mf_resp --- - + ### method `enable` ```python -enable(user_id: str, admin_token: str) +enable(user_id: str, user_token: str) ``` -Enables a disabled user account for a given user ID. +Enables a disabled user account for a given user ID. + +Takes in the disabled User's ID and a valid token and enables the user. + +params: user_id: str - the user's given ID. token: str - token used for enabling a user + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> user_id= "886b4266-77d1-4258-abae-2931fb4f16de" >>> mf_resp = mfsdk.users.enable(user_id, user_token) >>> mf_resp --- - + ### method `get` @@ -79,23 +148,51 @@ Enables a disabled user account for a given user ID. get(user_id: str, token: str) ``` -Gets a user information +Gets a user information. + +Gets info on currently logged in user. Info is obtained using authorization token and the user id. + +params: user_id: str - user information eg "886b4266-77d1-4258-abae-2931fb4f16de", token: str - token used for creating a new user + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> user_id = "886b4266-77d1-4258-abae-2931fb4f16de" >>> token = "" >>> mf_resp = mfsdk.users.get(user_id, token) >>> mf_resp --- - + ### method `get_all` ```python -get_all(query_params: dict, admin_token: str) +get_all(query_params: dict, user_token: str) ``` -Retrieves a list of users +Retrieves a list of users. + +Gets a list of users from the database when provided with a user token and some parameters. + +params: user_token: str - token used for creating a new user query_params: dict - has a limit(int) which is the size of the subset to be expected and offset which is the number of items to skip during retrieval. For example: { "offset" : 0, "limit" : 10 } + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> query_params = { ... "offset" : 0, "limit" : 10 ... } >>> mf_resp = mfsdk.users.get(query_params, user_token) >>> mf_resp --- - + ### method `login` @@ -105,9 +202,99 @@ login(user: dict) Generates an access token when provided with proper credentials. +Issues a new access and refresh token for a user for authenticating into the system. + +params: user: a dict with the user information and password for example: {"credentials":{ "identity": "user@mainflux.com", "secret": "12345678" } } + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> credentials= { ... "identity": "user@mainflux.com", ... "secret": "12345678" ... } >>> mf_resp = mfsdk.users.login(credentials) >>> mf_resp + +--- + + + +### method `refresh_token` + +```python +refresh_token(refresh_token: str) +``` + +Refreshes Access and Refresh Token used for authenticating into the system. + +Creates a new access token and refresh token for a user when provided with a valid refresh token. + +params: refresh_token: str - token used to refresh access. + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` + + >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> mf_resp = mfsdk.users.refresh_token(refresh_token) >>> mf_resp + +--- + + + +### method `reset_password` + +```python +reset_password(password: str, confirm_password: str, token: str) +``` + +Changes user password with the reset_request token + +When user gets reset token, after he submitted email to /password/reset-request, posting a new password along to this endpoint will change password. + +params: passwor: str - the user's new password. confirm_password: str - a recurrence of the password to ensure it is the same. token: str - the reset token recieved from the reset_request email. + + + +**returns:** + + - `mf_resp`: "OK" + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") ... password = 234567 ... confirm_password = 234567 >>> mf_resp = mfsdk.users.reset_password(password, confirm_password, token) >>> mf_resp + --- - + + +### method `reset_password_request` + +```python +reset_password_request(email: str, url: str) +``` + +User Password reset request. + +Generates a reset token and sends an email to the user with link for resetting password. + +params: referrer email: str - this is the host being sent by the browser. The email must be valid preferably gmail and ensure that the email is already linked to a user as their identity in the database. The email is part of the header. url: str - http://localhost/reset-request + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") ... email = admin@example.com ... url = stp@gmail.com >>> mf_resp = mfsdk.users.reset_password_request(email, url) >>> mf_resp + +--- + + ### method `update` @@ -115,19 +302,126 @@ Generates an access token when provided with proper credentials. update(user: dict, user_token: str) ``` -Updates info on currently logged in user. Info is updated using authorization user_token +Updates information on currently logged in user. + +Information such as name and metadata is updated using authorization user_token + +params: user: dict - user information for example: { "name": "example", "id": "886b4266-77d1-4258-abae-2931fb4f16de" "credentials": { "identity": "example@main.com", "secret": "12345678" }, "metadata": { "foo": "bar" } } token: str - token used for creating a new user + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> user = { ... "name": "example", ... "id": "886b4266-77d1-4258-abae-2931fb4f16de" ... "metadata": { ... "foo": "bar" ... } ... } >>> mf_resp = mfsdk.users.update(user, token) >>> mf_resp --- - + ### method `update_password` ```python -update_password(old_password: str, password: str, user_token: str) +update_password(old_secret: str, new_secret: str, user_token: str) +``` + +Changes user password. + +Updates secret of currently logged in user. Secret is updated using authorization token and the new received info. + +params: old_secret: str - the logged in user's current secret. new_secret: str - the user's new secret. token: str - token used for creating a new user + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") ... old_secret = 12345678 ... new_secret = 87654321 >>> mf_resp = mfsdk.users.update(old_secret, new_secret, user_token) >>> mf_resp + +--- + + + +### method `update_user_identity` + +```python +update_user_identity(user: dict, user_token: str) ``` -Changes user password +Updates identity information on currently logged in user. + +The user Identity is updated using authorization user_token + +params: user: dict - user information for example: + + { "name": "example", "id": "886b4266-77d1-4258-abae-2931fb4f16de" "credentials": { "identity": "example@main.com", "secret": "12345678" }, "metadata": { "foo": "bar" } } token: str - token used for creating a new user + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> user = { ... "name": "example", ... "id": "886b4266-77d1-4258-abae-2931fb4f16de" ... "credentials": { ... "identity": "example@main.com", ... "secret": "12345678" ... }, ... "metadata": { ... "foo": "bar" ... } ... } >>> mf_resp = mfsdk.users.update_user_identity(user, user_token) >>> mf_resp + +--- + + + +### method `update_user_owner` + +```python +update_user_owner(user: dict, user_token: str) +``` + +Updates owner on currently logged in user. + +Updates owner for the user with provided ID. Owner is updated using authorization token and a new owner identifier received in request. + +params: user: dict - user information for example: + + { "name": "example", "id": "886b4266-77d1-4258-abae-2931fb4f16de" "owner": "c52d-3b0d-43b9-8c3e-275c087d875af" } token: str - token used for creating a new user + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> user = { ... "name": "example", ... "id": "886b4266-77d1-4258-abae-2931fb4f16de", ... "owner": "c52d-3b0d-43b9-8c3e-275c087d875af" ... } >>> mf_resp = mfsdk.users.update_user_owner(user, user_token) >>> mf_resp + +--- + + + +### method `update_user_tags` + +```python +update_user_tags(user: dict, user_token: str) +``` + +Updates tags on currently logged in user. + +Updates tags of the user with provided ID. Tags is updated using authorization token and the new tags received in request. + +params: user: dict - user information for example: + + { "name": "example", "id": "886b4266-77d1-4258-abae-2931fb4f16de" "tags": [ "back", "end" ] "metadata": { "foo": "bar" } } token: str - token used for creating a new user + + + +**returns:** + + - `mf_resp`: response.Response - response object + +Usage: +``` >>> from mainflux import sdk >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") >>> user = { ... "name": "example", ... "id": "886b4266-77d1-4258-abae-2931fb4f16de" ... "tags": [ ... "back", ... "end" ... ] ... } >>> mf_resp = mfsdk.users.update_user_tags(user, user_token) >>> mf_resp diff --git a/examples/examples.py b/examples/examples.py index 19f2f24..f01e0d0 100644 --- a/examples/examples.py +++ b/examples/examples.py @@ -1,21 +1,35 @@ from mainflux import sdk +import json default_url = "http://localhost" mfsdk = sdk.SDK( users_url=default_url, - things_url=default_url, - reader_url=default_url + ":9204", + things_url=default_url + ":9000", + reader_url=default_url + ":9011", http_adapter_url=default_url, - certs_url=default_url + ":8204", - bootstrap_url=default_url + ":8202", - auth_url=default_url, + certs_url=default_url + ":9019", + bootstrap_url=default_url + ":9013" ) +"""Repetitive values that can be easily fed into the example code""" + +email = "", +password = "", +user_id = "", +token = "", +refresh_token = "", +thing_id = "", +thing_id2 = "", +channel_id = "", +channel_id2 = "", +group_id = "", + """To start working with the Mainflux system, you need to create a user account""" mf_resp = mfsdk.users.create( - user={"email": "", "password": ""} + user={"credentials": {"identity": "", "secret": password}}, + token= token, ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -24,7 +38,17 @@ """To log in to the Mainflux system, you need to create a user token""" mf_resp = mfsdk.users.login( - user={"email": "", "password": ""} + user={ "identity" : "", "secret": password} +) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Refreshes Access and Refresh Token used for authenticating into the system.""" + +mf_resp = mfsdk.users.refresh_token( + refresh_token= refresh_token ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -33,57 +57,146 @@ """You can always check the user entity that is logged in by entering the user ID and token""" -mf_resp = mfsdk.users.get(id="", token="") +mf_resp = mfsdk.users.get(user_id= user_id, token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) -"""Updating user entities in the database""" -mf_resp = mfsdk.users.update( - user_token="", user={"metadata": {"foo": "bar"}} -) +"""Updates user entities in the database""" +user = { + "id": user_id, + "name": "", + "metadata": { + "foo": "bar" + } +} +mf_resp = mfsdk.users.update(user_token= token, user=user) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) -"""You can get all users in the database by calling the get_all () function""" -mf_resp = mfsdk.users.get_all( - query_params={"offset": 1, "limit": 5}, admin_token="" -) +"""Updates user identity in the database""" +user = { + "credentials": { + "identity": "", + }, + "id": user_id +} +mf_resp = mfsdk.users.update_user_identity(user_token= token, user=user) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) -mf_resp = mfsdk.users.disable(user_id="", admin_token="") +"""Updates user tags in the database""" +user = { + "id": user_id, + "name": "", + "tags": [ + "yellow", + "orange" + ] +} +mf_resp = mfsdk.users.update_user_tags(user_token= token, user=user) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) +"""Updates user owner in the database""" +user = { + "credentials": { + "identity": "", + "secret": password, + }, + "id": user_id, + "owner": "" +} +mf_resp = mfsdk.users.update_user_owner(user_token= token, user=user) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""User Password reset request""" +mf_resp = mfsdk.users.reset_password_request(email= email, url= "http://localhost/reset-request") +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) -mf_resp = mfsdk.users.enable(user_id="", admin_token="") +"""User Password reset with the reset_request token""" +mf_resp = mfsdk.users.reset_password(password="", confirm_password="", token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""You can get all users in the database by calling the get_all () function""" +mf_resp = mfsdk.users.get_all( + query_params={"offset": 0, "limit": 5}, + user_token= token +) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Disables user""" +mf_resp = mfsdk.users.disable(user_id= user_id, user_token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Enables user""" +mf_resp = mfsdk.users.enable(user_id= user_id, user_token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) -"""Changing the user password can be done by calling -the update password function""" +"""Changing the user password can be done by calling the update password function""" mf_resp = mfsdk.users.update_password( - old_password="", password="", - user_token="" + old_secret="", new_secret="", + user_token= token ) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) +"""Authorising a User""" +access_request = { + "subject": user_id, + "object": group_id, + "action": "", + "entity_type": "" +} +mf_resp = mfsdk.users.authorise_user(access_request=access_request, token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Authorising a Thing""" +access_request = { + "subject": thing_id, + "object": channel_id, + "action": "", + "entity_type": "" +} +mf_resp = mfsdk.things.authorise_thing(access_request=access_request, token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + """To create a thing, you need the thing name and a user token""" mf_resp = mfsdk.things.create( - thing={"name": ""}, token="") + thing={"name": ""}, token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: @@ -92,8 +205,8 @@ """You can create multiple things at once by entering a series of things structures and a user token""" mf_resp = mfsdk.things.create_bulk( - things=[{"name": ""}, {"name": ""}], - token="", + things=[{"name": ""}, {"name": ""}, {"name": ""}], + token= token, ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -101,7 +214,7 @@ print(mf_resp.error.message) """You can get thing information by entering the thing ID and user token""" -mf_resp = mfsdk.things.get(token="", thing_id="") +mf_resp = mfsdk.things.get(thing_id= thing_id, token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: @@ -109,36 +222,74 @@ """You can get all things in the database by calling the get_all () function""" mf_resp = mfsdk.things.get_all( - query_params={"offset": 1, "limit": 5}, token="" + query_params={"offset": 0, "limit": 5}, token= token ) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) -"""Updating a thing entity in a database""" +"""Updates a thing entity in a database""" mf_resp = mfsdk.things.update( - thing_id="", token="", thing={"name": ""} + thing_id=thing_id, token= token, thing={"name": ""} ) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) +"""Updates a thing secret in a database""" +mf_resp = mfsdk.things.update_thing_secret( + thing_id=thing_id, token= token, thing={"secret": password} +) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) -"You can get all thing connected to channel" +"""Updates a thing's tags in a database""" +thing= { + "id": thing_id, + "name": "", + "tags": [ + "dev","back" + ] + } +mf_resp = mfsdk.things.update_thing_tags( + thing_id=thing_id, token= token, thing=thing +) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Updates a thing's owner""" +thing= { + "id": thing_id, + "name": "", + "owner": "", +} +mf_resp = mfsdk.things.update_thing_owner( + thing_id=thing_id, token= token, thing=thing +) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""You can get all thing connected to channel""" mf_resp = mfsdk.things.get_by_channel( - channel_id="", + channel_id=channel_id, query_params={"offset": 1, "limit": 5}, - token="", + token=token, ) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) -"""To delete a thing you need a thing ID and a user token""" -mf_resp = mfsdk.things.delete(thing_id="", token="") +"""To disable a thing you need a thing ID and a user token""" +mf_resp = mfsdk.things.disable(thing_id=thing_id, token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: @@ -146,7 +297,7 @@ """Connect thing to channel""" mf_resp = mfsdk.things.connect( - channel_id="", thing_id="", token="" + channel_id=channel_id, thing_id=thing_id, action="", token= token ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -155,7 +306,7 @@ """Disconnect thing from channel""" mf_resp = mfsdk.things.disconnect( - channel_id="", thing_id="", token="" + channel_id=channel_id, thing_id=thing_id, token= token ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -164,10 +315,12 @@ """Connect things to channels""" mf_resp = mfsdk.things.connects( - channel_ids=["", ""], - thing_ids=["", ""], - token="", + thing_ids=[thing_id, thing_id2], + channel_ids=[channel_id, channel_id2], + actions="", + token= token, ) + if mf_resp.error.status == 0: print(mf_resp.value) else: @@ -175,18 +328,30 @@ """Disconnect things from channels""" mf_resp = mfsdk.things.disconnects( - channel_ids=["", ""], - thing_ids=["", ""], - token="", + thing_ids=[thing_id, thing_id2], + channel_ids=[channel_id, channel_id2], + token=token, ) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) +"""Share thing""" +mf_resp = mfsdk.things.share_thing( + channel_id= channel_id, + user_id= user_id, + actions= [""], + token= token, +) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + """To create a channel, you need a channel and a token""" mf_resp = mfsdk.channels.create( - channel={"name": "channel_name"}, token="") + channel={"name": ""}, token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: @@ -194,19 +359,19 @@ """As with things, you can create multiple channels at once""" mf_resp = mfsdk.channels.create_bulk( - channels=[{"name": ""}, {"name": ""}], - token="", + channels=[{"name": ""}, {"name": ""}], + token= token, ) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) - + """Update channel entities in the database""" mf_resp = mfsdk.channels.update( - channel_id="", - token="", - channel={"name": ""}, + channel_id=channel_id, + token= token, + channel={"name": ""}, ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -214,7 +379,7 @@ print(mf_resp.error.message) """You can get channel information by entering the channel ID and user token""" -mf_resp = mfsdk.channels.get(token="", channel_id="") +mf_resp = mfsdk.channels.get(token= token, channel_id=channel_id) if mf_resp.error.status == 0: print(mf_resp.value) else: @@ -223,7 +388,7 @@ """You can get all channels in the database by calling the get_all () function""" mf_resp = mfsdk.channels.get_all( - query_params={"offset": 1, "limit": 5}, token="" + query_params={"offset": 0, "limit": 5}, token= token ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -232,8 +397,8 @@ """A list of all the channels to which a given thing is connected""" mf_resp = mfsdk.channels.get_by_thing( - thing_id="", query_params={"offset": 1, "limit": 5}, - token="" + thing_id=thing_id, query_params={"offset": 0, "limit": 5}, + token= token ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -241,15 +406,15 @@ print(mf_resp.error.message) """Identifies thing when given thing key""" -mf_resp = mfsdk.channels.identify_thing(thing_key="") +mf_resp = mfsdk.channels.identify_thing(thing_key="") if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) """Delete channels from the database""" -mf_resp = mfsdk.channels.delete( - channel_id="", token="") +mf_resp = mfsdk.channels.disable( + channel_id=channel_id, token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: @@ -257,22 +422,29 @@ """To create a group, you need the group name and a user token""" mf_resp = mfsdk.groups.create( - group={"name": "group_name"}, token="") + group={"name": "group_name"}, token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) """You can get group information by entering the group ID and token""" -mf_resp = mfsdk.groups.get(group_id="", token="") +mf_resp = mfsdk.groups.get(group_id= group_id, token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) """Group update""" +group={ + "id": group_id, + "name": "", + "metdata": { + "foo": "bar" + } + } mf_resp = mfsdk.groups.update( - group_id="", token="", group={""} + token= token, group= group, group_id="group_id" ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -281,19 +453,19 @@ """You can get groups in the database by calling the get_all () function""" mf_resp = mfsdk.groups.get_all( - token="", query_params={"offset": 1, "limit": 5} + token= token, query_params={"offset": 0, "limit": 5} ) if mf_resp.error.status == 0: print(mf_resp.value) else: print(mf_resp.error.message) -"""Assign user, thing or channel to a group""" +"""Assign user to a group""" mf_resp = mfsdk.groups.assign( - group_id="", - token="", - members_ids=["" | "" | ""], - member_type='<"users" | "things" | "channels">', + group_id=group_id, + token= token, + member_id="", + member_type=[""], ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -302,9 +474,9 @@ """Unassign""" mf_resp = mfsdk.groups.unassign( - group_id="", - token="", - members_ids=["" | "" | ""], + group_id="", + token= token, + members_ids="", ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -313,7 +485,7 @@ """Get list of children from group""" mf_resp = mfsdk.groups.children( - group_id="", token="", + group_id=group_id, token= token, query_params={"offset": 0, "limit": 5} ) if mf_resp.error.status == 0: @@ -323,7 +495,7 @@ """Get list of parents from group""" mf_resp = mfsdk.groups.parents( - group_id="", token="", + group_id=group_id, token= token, query_params={"offset": 0, "limit": 5} ) if mf_resp.error.status == 0: @@ -333,7 +505,7 @@ """Get list of members from group""" mf_resp = mfsdk.groups.members( - member_id="", token="", + group_id=group_id, token= token, query_params={"offset": 0, "limit": 5} ) if mf_resp.error.status == 0: @@ -343,8 +515,8 @@ """Get list of memberships from member""" mf_resp = mfsdk.groups.memberships( - group_id="", - token="", + member_id="", + token= token, query_params={"offset": 0, "limit": 5}, ) if mf_resp.error.status == 0: @@ -353,7 +525,7 @@ print(mf_resp.error.message) """Delete group from the database""" -mf_resp = mfsdk.groups.delete(group_id="", token="") +mf_resp = mfsdk.groups.disable(group_id=group_id, user_token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: @@ -361,7 +533,7 @@ """Sends message via HTTP protocol""" mf_resp = mfsdk.messages.send( - channel_id="", msg="", thing_key="" + channel_id=channel_id, msg='[]', thing_key="" ) if mf_resp.error.status == 0: print(mf_resp.value) @@ -369,7 +541,95 @@ print(mf_resp.error.message) """Reads messages from database for a given channel""" -mf_resp = mfsdk.messages.read(channel_id="", token="") +mf_resp = mfsdk.messages.read(channel_id=channel_id, token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Issue certs""" +mf_resp = mfsdk.certs.issue(thing_id=thing_id,valid="", token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""View Certs""" +mf_resp = mfsdk.certs.view_by_thing(thing_id=thing_id, token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""View Certs""" +mf_resp = mfsdk.certs.view_by_serial(cert_id="", token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Revoke Certs""" +mf_resp = mfsdk.certs.revoke(thing_id=thing_id, token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Adds new config to the list of config owned by user identified using the provided access token.""" +config = { + "external_id": "", + "external_key": "", + "thing_id": thing_id, + "name": "" +} +mf_resp = mfsdk.bootstrap.add(config=config, token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Updating state represents enabling/disabling Config, i.e.connecting and disconnecting corresponding Mainflux Thing to the list of Channels.""" +config = { + "external_id": "", + "external_key": "", + "thing_id": thing_id, + "name": "" +} +mf_resp = mfsdk.bootstrap.whitelist(config=config, token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Retrieves a configuration with given config id""" +mf_resp = mfsdk.bootstrap.view(thing_id= thing_id, token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Update is performed by replacing the current resource data with values provided in a request payload. Note that the owner, ID, external ID, external key, Mainflux Thing ID and key cannot be changed.""" +config = { + "external_id": "", + "external_key": "", + "thing_id": thing_id, + "name": "" +} +mf_resp = mfsdk.bootstrap.update(config=config, token= token) +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Retrieves a configuration with given external ID and external key.""" +mf_resp = mfsdk.bootstrap.bootstrap(external_id="", external_key= "") +if mf_resp.error.status == 0: + print(mf_resp.value) +else: + print(mf_resp.error.message) + +"""Removes a Config. In case of successful removal the service will ensure that the removed config is disconnected from all the Mainflux channels.""" +mf_resp = mfsdk.bootstrap.remove(config_id= "", token= token) if mf_resp.error.status == 0: print(mf_resp.value) else: diff --git a/mainflux/boostrap.py b/mainflux/boostrap.py index bd74011..9f23047 100644 --- a/mainflux/boostrap.py +++ b/mainflux/boostrap.py @@ -6,64 +6,165 @@ class Bootstrap: - configs_endpoint = "configs" - bootstrap_endpoint = "bootstrap" - whitelist_endpoint = "state" - bootstrap_certs_endpoint = "configs/certs" + """Bootstrap service API client. + + Bootstrap service is used to manage configurations for Mainflux Things. It provides + services such as updating, viewing, removing and adding new configurations. + + Attributes: + url (str): Mainflux Bootstrap API URL. + CONFIGS_ENDPOINT (str): Configurations API endpoint. + BOOTSTRAP_ENDPOINT (str): Bootstrap API endpoint. + WHITELIST_ENDPOINT (str): Whitelist API endpoint. + BOOTSTRAP_CERTS_ENDPOINT (str): Bootstrap certificates API endpoint. + + """ + CONFIGS_ENDPOINT = "configs" + BOOTSTRAP_ENDPOINT = "bootstrap" + WHITELIST_ENDPOINT = "things/state" + BOOTSTRAP_CERTS_ENDPOINT = "configs/certs" def __init__(self, url: str): + """Initializes Bootstrap with the provided URL. + + params: + url (str): Mainflux Bootstrap API URL. + + returns: + Bootstrap: Bootstrap object. + + raises: + None + """ self.url = url def add(self, config: dict, token: str): """Adds new config to the list of config owned by user identified - using the provided access token.""" + using the provided access token. + + Some of the key data needed include the external_key and external_id which must be + specific to the thing provided with the thing_id. Mind that every configuration + must have a specific thing_id. + + params: + config (dict): Configuration data for example: + { + "external_id": "123", + "external_key": "456", + "thing_id": "fdb1057c-2905-4f71-9a80-e0ce9191e667", + "name": "thing_name" + } + token (str): Authorization token. + + returns: + mf_response : response.Response. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(bootstrap_url="http://localhost:9013") + >>> config = { + ... "external_id": "123", + ... "external_key": "456", + ... "thing_id": "fdb1057c-2905-4f71-9a80-e0ce9191e667", + ... "name": "thing_name" + ... } + >>> mf_resp = mfsdk.bootstrap.add(config, token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.configs_endpoint, + self.url + "/things" + "/" + self.CONFIGS_ENDPOINT, json=config, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 201: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.boostrap["add"], http_resp.status_code + errors.bootstrap["add"], http_resp.status_code ) else: - location = http_resp.headers.get("location") - mf_resp.value = location.split("/")[2] + mf_resp.value = "Configuration added" return mf_resp def whitelist(self, config: dict, token: str): """Updating state represents enabling/disabling Config, i.e.connecting and disconnecting corresponding Mainflux Thing to the - list of Channels.""" + list of Channels. + + params: + config (dict): Configuration data for example: + { + "external_id": "123", + "external_key": "456", + "thing_id": "fdb1057c-2905-4f71-9a80-e0ce9191e667", + "name": "thing_name" + } + token (str): Authorization token. + + returns: + mf_response : response.Response. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(bootstrap_url="http://localhost:9013") + >>> config = { + ... "external_id": "123", + ... "external_key": "456", + ... "thing_id": "fdb1057c-2905-4f71-9a80-e0ce9191e667", + ... "name": "thing_name" + ... } + >>> mf_resp = mfsdk.bootstrap.whitelist(config, token) + >>> mf_resp + """ mf_resp = response.Response() - if config["MFThing"] == "": + if config["thing_id"] == "": mf_resp.error.status = 1 mf_resp.error.message = "parameter not found in the query" http_resp = requests.put( - self.url + "/" + self.whitelist_endpoint + "/" + config["MFThing"], + self.url + "/" + self.WHITELIST_ENDPOINT + "/" + config["thing_id"], json=config, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 201: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.boostrap["whitelist"], http_resp.status_code + errors.bootstrap["whitelist"], http_resp.status_code ) + else: + mf_resp.value = "Configuration Updated" return mf_resp - def view(self, config_id: str, token: str): - """Retrieves a configuration with given config id""" + def view(self, thing_id: str, token: str): + """Retrieves a configuration with given config id + + Provides a configuration with given config id. + + params: + thing_id (str): Thing ID. + token (str): Authorization token. + + returns: + mf_resp : response.Response - response object. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(bootstrap_url="http://localhost:9013") + >>> thing_id = "thing_id" + >>> mf_resp = mfsdk.bootstrap.view(thing_id, token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.configs_endpoint + "/" + config_id, + self.url + "/things" + "/" + self.CONFIGS_ENDPOINT + "/" + thing_id, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.boostrap["view"], http_resp.status_code + errors.bootstrap["view"], http_resp.status_code ) else: mf_resp.value = http_resp.json() @@ -73,33 +174,84 @@ def update(self, config: dict, token: str): """Update is performed by replacing the current resource data with values provided in a request payload. Note that the owner, ID, external ID, external key, Mainflux Thing ID and key cannot be - changed.""" + changed. + + params: + config (dict): Configuration data for example: + { + "external_id": "123", + "external_key": "456", + "thing_id": "fdb1057c-2905-4f71-9a80-e0ce9191e667", + "name": "thing_name" + } + token (str): Authorization token. + + returns: + + mf_response : response.Response. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(bootstrap_url="http://localhost:9013") + >>> config = { + ... "external_id": "123", + ... "external_key": "456", + ... "thing_id": "fdb1057c-2905-4f71-9a80-e0ce9191e667", + ... "name": "thing_name" + ... } + >>> mf_resp = mfsdk.bootstrap.update(config, token) + >>> mf_resp + """ mf_resp = response.Response() - if config["MFThing"] == "": + if config["thing_id"] == "": mf_resp.error.status = 1 mf_resp.error.message = "parameter not found in the query" http_resp = requests.put( - self.url + "/" + self.configs_endpoint + "/" + config["MFThing"], + self.url + "/things/" + self.CONFIGS_ENDPOINT + "/" + config["thing_id"], headers=utils.construct_header(token, utils.CTJSON), - data=config, + json=config, ) if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.boostrap["update"], http_resp.status_code + errors.bootstrap["update"], http_resp.status_code ) + else: + mf_resp.value = "Configuration updated." return mf_resp def update_certs( self, config_id: str, client_cert: str, client_key: str, ca: str, - token: str - ): + token: str): """Update is performed by replacing the current certificate data - with values provided in a request payload.""" + with values provided in a request payload. + + params: + config_id (str): Configuration ID. + client_cert (str): Client certificate. + client_key (str): Client key. + ca (str): CA certificate. + token (str): Authorization token. + + returns: + mf_response : response.Response. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(bootstrap_url="http://localhost:9013") + >>> config_id = "config_id" + >>> client_cert = "client_cert" + >>> client_key = "client_key" + >>> ca = "ca" + >>> mf_resp = mfsdk.bootstrap.update_certs(config_id, client_cert, client_key, ca, token) + >>> mf_resp + """ payload = {"client_cert": client_cert, "client_key": client_key, "ca_cert": ca} http_resp = requests.patch( - self.url + "/" + self.bootstrap_certs_endpoint + "/" + config_id, + self.url + "/" + self.BOOTSTRAP_CERTS_ENDPOINT + "/" + config_id, headers=utils.construct_header(token, utils.CTJSON), json=payload, ) @@ -107,37 +259,74 @@ def update_certs( if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.boostrap["update"], http_resp.status_code + errors.bootstrap["update"], http_resp.status_code ) return mf_resp def remove(self, config_id: str, token: str): """Removes a Config. In case of successful removal the service will ensure that the removed config is disconnected from all the - Mainflux channels.""" + Mainflux channels. + + params: + config_id (str): Configuration ID. + token (str): Authorization token. + + returns: + mf_response : response.Response. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(bootstrap_url="http://localhost:9013") + >>> config_id = "config_id" + >>> mf_resp = mfsdk.bootstrap.remove(config_id, token) + >>> mf_resp + """ mf_resp = response.Response() - http_resp = requests.post( - self.url + "/" + self.configs_endpoint + "/" + config_id, + http_resp = requests.delete( + self.url + "/things/" + self.CONFIGS_ENDPOINT + "/" + config_id, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 204: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.boostrap["remove"], http_resp.status_code + errors.bootstrap["remove"], http_resp.status_code ) + else: + mf_resp.value = "Configuration removed." return mf_resp def bootstrap(self, external_id: str, external_key: str): """Retrieves a configuration with given external ID and external - key.""" + key. + + params: + external_id (str): External ID. + external_key (str): External key. + + returns: + mf_resp : response.Response - response object. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(bootstrap_url="http://localhost:9013") + >>> external_id = "external_id" + >>> external_key = "external_key" + >>> mf_resp = mfsdk.bootstrap.bootstrap(external_id, external_key) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.bootstrap_endpoint + "/" + external_id, - headers=utils.construct_header(external_key, utils.CTJSON), + self.url + "/things/bootstrap" + "/" + external_id, + headers=utils.construct_header(utils.ThingPrefix+external_key, utils.CTJSON), ) if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.boostrap["bootstrap"], http_resp.status_code + errors.bootstrap["bootstrap"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() return mf_resp diff --git a/mainflux/certs.py b/mainflux/certs.py index 23fee28..2837ba8 100644 --- a/mainflux/certs.py +++ b/mainflux/certs.py @@ -6,25 +6,55 @@ class Certs: - certs_endpoint = "configs" + """Mainflux Certificates API + + Certs is used to issue, view, and revoke certificates. + It is used to issue certificates for things. + + Args: + url (str): Mainflux Certificates API URL. + CERTS_ENDPOINT (str): Certificates API endpoint. + """ + CERTS_ENDPOINT = "certs" def __init__(self, url: str): + """Initializes Certs with the provided URL. + + Args: + url (str): Mainflux Certificates API URL. + + Returns: + Certs: Certs object. + """ self.url = url - def issue( - self, thing_id: str, key_bits: int, key_type: str, valid: str, - token: str - ): - + def issue(self, thing_id: str, valid: str, token: str): + """ + Issues a certificate for a given thing ID. + + Args: + thing_id (str): Thing ID. + valid (str): Certificate validity period. + token (str): Authorization token. + + Returns: + Response: Mainflux response. + + Usage: + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(certs_url="http://localhost:9019") + >>> thing_id = "thing_id" + >>> valid = "1h" + >>> mf_resp = mfsdk.certs.issue(thing_id, valid) + >>> mf_resp + """ payload = { "thing_id": thing_id, - "key_bits": key_bits, - "key_type": key_type, "ttl": valid, } mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.certs_endpoint, + self.url + "/" + self.CERTS_ENDPOINT, json=payload, headers=utils.construct_header(token, utils.CTJSON), ) @@ -34,29 +64,101 @@ def issue( errors.certs["issue"], http_resp.status_code ) else: - location = http_resp.headers.get("location") - mf_resp.value = location.split("/")[2] + mf_resp.value = http_resp.json() + return mf_resp + + def view_by_thing(self, thing_id: str, token: str): + """Retrieves a list of certificates' serial IDs for a given thing ID. + + Provides a list of certificates' serial IDs for a given thing ID. + + Params: + thing_id (str): Thing ID. + token (str): Authorization token. + + Returns: + mf_resp : response.Response - response object. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(certs_url="http://localhost:9019") + >>> thing_id = "thing_id" + >>> mf_resp = mfsdk.certs.view_by_thing(thing_id) + >>> mf_resp + """ + mf_resp = response.Response() + http_resp = requests.get( + self.url + "/serials" + "/" + thing_id, + headers=utils.construct_header(token, utils.CTJSON), + ) + if http_resp.status_code != 200: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.certs["view_by_thing"], http_resp.status_code + ) + else: + mf_resp.value = http_resp.json() return mf_resp + + def view_by_serial(self, cert_id: str, token: str): + """Retrieves a certificate for a given cert ID. + + Provides a certificate for a given cert ID. + + Params: - def view(self, thing_id: str, token: str): - """Generates an access token when provided with proper credentials.""" + cert_id (str): Certificate ID. + token (str): Authorization token. + + Returns: + mf_resp : response.Response - response object. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(certs_url="http://localhost:9019") + >>> cert_id = "cert_id" + >>> mf_resp = mfsdk.certs.view_by_serial(cert_id) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.certs_endpoint + "/" + thing_id, + self.url + "/" + self.CERTS_ENDPOINT + "/" + cert_id, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.certs["view"], http_resp.status_code + errors.certs["view_by_serial"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() return mf_resp def revoke(self, thing_id: str, token: str): + """Revokes a certificate for a given thing ID. + + Deletes a certificate for a given thing ID and valid token. + params: + thing_id (str): thing id + token (str): valid authorization token used to delete the certificate + + Returns: + mf_resp : response.Response - response object. + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(certs_url="http://localhost:9019") + >>> thing_id = "thing_id" + >>> mf_resp = mfsdk.certs.revoke(thing_id) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.delete( - self.url + "/" + self.certs_endpoint + "/" + thing_id, + self.url + "/" + self.CERTS_ENDPOINT + "/" + thing_id, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 200: @@ -64,4 +166,6 @@ def revoke(self, thing_id: str, token: str): mf_resp.error.message = errors.handle_error( errors.certs["revoke"], http_resp.status_code ) + else: + mf_resp.value = "DELETED" return mf_resp diff --git a/mainflux/channels.py b/mainflux/channels.py index d979584..041ca2e 100644 --- a/mainflux/channels.py +++ b/mainflux/channels.py @@ -6,18 +6,74 @@ class Channels: - channels_endpoint = "channels" - things_endpoint = "things" - identify_endpoint = "identify" + """Channels class provides the abstraction of the Mainflux Channels API. + + Channels are used to connect things and users. They are used to send messages to things and + receive messages from things. Channels API provides the following functionalities: + - create channel + - create multiple channels in a bulk + - get channel + - get all channels + - get all channels to which a specific thing is connected to + - update channel + - delete channel + - identify thing + + Attributes: + CHANNELS_ENDPOINT (str): Channels API endpoint + THINGS_ENDPOINT (str): Things API endpoint + IDENTIFY_ENDPOINT (str): Identify API endpoint + + """ + CHANNELS_ENDPOINT = "channels" + THINGS_ENDPOINT = "things" + IDENTIFY_ENDPOINT = "identify" def __init__(self, url: str): + """Initializes Channels class with the provided url + + Args: + url (str): Mainflux Channels API URL + + returns: + Channels: Channels object initialized with the provided url. + + raises: + None + """ self.url = url def create(self, channel: dict, token: str): - """Creates channel entity in the database""" + """Creates channel entity in the database + + Creates a new channel in the database when provided with a valid token. + + params: + channel (dict): Channel entity to be created for example: + { + "name": "channel_name", + "metadata": { + "description": "channel_description" + } + } + token (str): User's token + + returns: + Response: Response object containing the response from the server + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(channels_url="http://localhost:9000") + >>> channel = { + ... "name": "channel_name" + ... } + >>> mf_resp = mfsdk.channels.create(channel, token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.channels_endpoint, + self.url + "/" + self.CHANNELS_ENDPOINT, json=channel, headers=utils.construct_header(token, utils.CTJSON), ) @@ -27,19 +83,59 @@ def create(self, channel: dict, token: str): errors.channels["create"], http_resp.status_code ) else: - location = http_resp.headers.get("location") - mf_resp.value = location.split("/")[2] + mf_resp.value = http_resp.json() return mf_resp def create_bulk(self, channels: list, token: str): - """Creates multiple channels in a bulk""" + """Creates multiple channels in bulk + + Creates multiple new channels when provided with channels information + and a valid token. + + params: + channels: list- Channel entities to be created for example: + [ + { + "name": "channel_name", + "metadata": { + "description": "channel_description" + } + }, + { + "name": "channel_name", + "metadata": { + "description": "channel_description" + } + } + ] + + token (str): User's token + + returns: + Response: Response object containing the response from the server + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(channels_url="http://localhost:9000") + >>> channels = [ + ... { + ... "name": "channel_name" + ... }, + ... { + ... "name": "channel_name" + ... } + ... ] + >>> mf_resp = mfsdk.channels.create_bulk(channels, token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.channels_endpoint + "/bulk", + self.url + "/" + self.CHANNELS_ENDPOINT + "/bulk", json=channels, headers=utils.construct_header(token, utils.CTJSON), ) - if http_resp.status_code != 201: + if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.channels["create_bulk"], http_resp.status_code @@ -49,10 +145,28 @@ def create_bulk(self, channels: list, token: str): return mf_resp def get(self, channel_id: str, token: str): - """Gets a channel entity for a logged-in user""" + """Gets a channel entity for a logged-in user + + Provides a channel entity when provided with a valid channel ID and token. + + params: + channel_id (str): Channel ID + token (str): User's token + + returns: + Response: Response object + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(channels_url="http://localhost:9000") + >>> channel_id = "channel_id" + >>> mf_resp = mfsdk.channels.get(channel_id, token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.channels_endpoint + "/" + channel_id, + self.url + "/" + self.CHANNELS_ENDPOINT + "/" + channel_id, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 200: @@ -65,13 +179,37 @@ def get(self, channel_id: str, token: str): return mf_resp def get_all(self, query_params: dict, token: str): - """Gets all channels from database""" - url = self.url + "/" + self.channels_endpoint + """Gets all channels from database + + Gets all channels from database when provided with a valid token.. + + params: + query_params (dict): Query parameters for example: + { + "offset": 0, + "limit": 10 + } + token (str): User's token + + returns: + mf_resp: response.Response -response object + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(channels_url="http://localhost:9000") + >>> query_params = { + ... "offset": 0, + ... "limit": 10 + ... } + >>> mf_resp = mfsdk.channels.get_all(query_params, token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - url, + self.url + "/" + self.CHANNELS_ENDPOINT, headers=utils.construct_header(token, utils.CTJSON), - params=query_params + params=query_params, ) if http_resp.status_code != 200: mf_resp.error.status = 1 @@ -83,16 +221,38 @@ def get_all(self, query_params: dict, token: str): return mf_resp def get_by_thing(self, thing_id: str, query_params: dict, token: str): - """Gets all channels to which a specific thing is connected to""" + """Gets all channels to which a specific thing is connected to. + + Provides a list of all the channels a thing is connected to when provided with a valid + token and thing ID. + + params: + thing_id (str): Thing ID + query_params (dict): Query parameters for example: + { + "offset": 0, + "limit": 10 + } + token (str): User's token + + returns: + mf_resp: response.Response -response object + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(channels_url="http://localhost:9000") + >>> thing_id = "thing_id" + >>> query_params = { + ... "offset": 0, + ... "limit": 10 + ... } + >>> mf_resp = mfsdk.channels.get_by_thing(thing_id, query_params, token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url - + "/" - + self.things_endpoint - + "/" - + thing_id - + "/" - + self.channels_endpoint, + self.url + "/" + self.THINGS_ENDPOINT + "/" + thing_id + "/" + self.CHANNELS_ENDPOINT, headers=utils.construct_header(token, utils.CTJSON), params=query_params, ) @@ -106,9 +266,38 @@ def get_by_thing(self, thing_id: str, query_params: dict, token: str): return mf_resp def update(self, channel_id: str, channel: dict, token: str): - """Updates channel entity""" + """Updates channel entity + + Updates a channel entity when provided with a valid channel ID, channel entity and token. + The information that can be updated are channel's name and metadata. + + params: + channel_id (str): Channel ID + channel (dict): Channel entity to be updated for example: + { + "name": "channel_name", + "metadata": { + "description": "channel_description" + } + } + token (str): User's token + + returns: + mf_resp: response.Response -response object + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(channels_url="http://localhost:9000") + >>> channel_id = "channel_id" + >>> channel = { + ... "name": "channel_name" + ... } + >>> mf_resp = mfsdk.channels.update(channel_id, channel, token) + >>> mf_resp + """ http_resp = requests.put( - self.url + "/" + self.channels_endpoint + "/" + channel_id, + self.url + "/" + self.CHANNELS_ENDPOINT + "/" + channel_id, json=channel, headers=utils.construct_header(token, utils.CTJSON), ) @@ -118,16 +307,39 @@ def update(self, channel_id: str, channel: dict, token: str): mf_resp.error.message = errors.handle_error( errors.channels["update"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() return mf_resp - def delete(self, channel_id: str, token: str): - """Deletes a channel entity from database""" - http_resp = requests.delete( - self.url + "/" + self.channels_endpoint + "/" + channel_id, + def disable(self, channel_id: str, token: str): + """Deletes a channel entity from database. + + Deletes a channel entity from database when provided with a valid channel ID and token. + The channel is not deleted from the database but is marked as disabled. + + params: + + channel_id (str): Channel ID + token (str): User's token + + returns: + + mf_resp: response.Response -response object + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(channels_url="http://localhost:9000") + >>> channel_id = "channel_id" + >>> mf_resp = mfsdk.channels.disable(channel_id, token) + >>> mf_resp + """ + http_resp = requests.post( + self.url + "/" + self.CHANNELS_ENDPOINT + "/" + channel_id + "/disable", headers=utils.construct_header(token, utils.CTJSON), ) mf_resp = response.Response() - if http_resp.status_code != 204: + if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.channels["delete"], http_resp.status_code @@ -135,15 +347,33 @@ def delete(self, channel_id: str, token: str): return mf_resp def identify_thing(self, thing_key: str): - """Validates thing's key and returns it's ID if key is valid""" + """Validates thing's key and returns it's ID if key is valid + + Uses a thing_key or secret to validate a thing and provide its information. + + params: + thing_key (str): Thing's key + + returns: + mf_resp: response.Response -response object + + Usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(channels_url="http://localhost:9000") + >>> thing_key = "thing_key" + >>> mf_resp = mfsdk.channels.identify_thing(thing_key) + >>> mf_resp + """ http_resp = requests.post( - self.url + "/" + self.identify_endpoint, json={"token": thing_key} + self.url + "/" + self.IDENTIFY_ENDPOINT, + headers=utils.construct_header(utils.ThingPrefix + thing_key, utils.CTJSON), ) mf_resp = response.Response() if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.channels["get_by_thing"], http_resp.status_code + errors.channels["identify_thing"], http_resp.status_code ) else: mf_resp.value = http_resp.json() diff --git a/mainflux/errors.py b/mainflux/errors.py index 959c6f7..09a435c 100644 --- a/mainflux/errors.py +++ b/mainflux/errors.py @@ -20,11 +20,14 @@ def handle_error(error_dict, status_code): users = { "create": { - 409: "Failed due to using an existing email address.", + 409: "Failed due to using an existing identity.", }, "login": { 409: "Failed due to using an existing email address.", }, + "refresh_token": { + 404: "A non-existent entity request.", + }, "get": { 400: "Failed due to malformed query parameters.", }, @@ -34,12 +37,30 @@ def handle_error(error_dict, status_code): "update": { 404: "Failed due to non existing user.", }, + "update_user_identity": { + 401: "Missing or invalid access token provided.", + }, + "update_user_tags": { + 401: "Missing or invalid access token provided.", + }, + "update_user_owner": { + 401: "Missing or invalid access token provided.", + }, "enable": { 404: "Failed due to non existing user." }, "disable": { 404: "Failed due to non existing user." }, + "reset_password_request": { + 400: "Failed due to malformed JSON." + }, + "reset_password": { + 400: "Failed due to malformed JSON." + }, + "authorise_user":{ + 400: "Failed due to malformed JSON." + } } things = { @@ -47,7 +68,6 @@ def handle_error(error_dict, status_code): 422: "Unprocessable Entity." }, "create_bulk": { - }, "get": { 400: "Failed due to malformed query parameters.", @@ -62,27 +82,42 @@ def handle_error(error_dict, status_code): "update": { 404: "Thing does not exist.", }, + "update_thing_secret": { + 401: "Missing or invalid access token provided.", + }, + "update_thing_tags": { + 401: "Missing or invalid access token provided.", + }, + "update_thing_owner": { + 401: "Missing or invalid access token provided.", + }, "delete": { 400: "Failed due to malformed thing's ID.", }, "connect": { - + 400: "A non-existent entity request." }, "disconnect": { 400: "Failed due to malformed query parameters.", 404: "Channel or thing does not exist.", }, + "share_thing": { + 400: "A non-existent entity request." + }, + "authorise_thing":{ + 403: "False", + }, } channels = { "create": { - + 409: "Failed due to using an existing identity." }, "create_bulk": { - + 401: "Missing or invalid access token provided." }, "get": { - + 401: "Missing or invalid access token provided." }, "get_all": { 400: "Failed due to malformed channel's ID.", @@ -98,6 +133,9 @@ def handle_error(error_dict, status_code): "delete": { 400: "Failed due to malformed channel's ID." }, + "identify_thing":{ + 401: "Thing and channel are not connected, or thing with specified key doesn't exist." + }, } messages = { @@ -131,20 +169,32 @@ def handle_error(error_dict, status_code): "members": { 409: "Failed due to using an existing email address.", }, - "assign": { - + "memberships":{ + 400: "Failed due to malformed query parameters." }, + "parents": { + 400: "Failed due to malformed query parameters." + }, + "children": { + 400: "Failed due to malformed query parameters." + }, + "assign": { + 400: "Failed due to malformed JSON." + }, "unassign": { 400: "Failed due to malformed query parameters.", 404: "Group does not exist.", }, - "delete": { + "disable": { 400: "Failed due to malformed query parameters.", 404: "Group does not exist.", }, } -boostrap = { +bootstrap = { + "add": { + 401: "Missing or invalid access token provided.", + }, "view": { 404: "Config does not exist.", }, @@ -155,12 +205,21 @@ def handle_error(error_dict, status_code): "update": { 404: "Config does not exist.", }, - "boostrap": { + "bootstrap": { 404: "Failed to retrieve corresponding config." - } + }, + "remove": { + 400: "Failed due to malformed config ID." + } } certs = { - "view": { + "issue": { + 401: "Missing or invalid access token provided.", + }, + "view_by_thing": { + 404: "Failed to retrieve corresponding certificate.", + }, + "view_by_serial": { 404: "Failed to retrieve corresponding certificate.", }, "revoke": { diff --git a/mainflux/groups.py b/mainflux/groups.py index 3e63042..13e59b2 100644 --- a/mainflux/groups.py +++ b/mainflux/groups.py @@ -1,4 +1,5 @@ import requests +import json from mainflux import response from mainflux import errors @@ -6,16 +7,60 @@ class Groups: - groups_endpoint = "groups" + """Groups class provides the abstraction of the Mainflux groups service API. + + Groups class provides the following functionality: create, get, get_all, parents, + children, update, members, memberships, assign, unassign, disable. + + Attributes: + URL: Mainflux groups service URL. + GROUPS_ENDPOINT: Mainflux groups service API endpoint. + + """ + GROUPS_ENDPOINT = "groups" def __init__(self, url: str): - self.url = url - + self.URL = url + """Initializes Groups API client with the provided URL. + + params: + url: Mainflux groups service URL. + + returns: + Groups object. + + raises: + None. + """ def create(self, group: dict, token: str): - """Creates group entity in the database""" + """Creates a group entity in the database. + + Creates a group entity in the database using the provided group + object and token. + + params: + group: dict - group information for example: + { + "name": "groupName", + } + token: str - token used to create a new group. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> group = { + ... "name": "groupName", + ... } + >>> mf_resp = mfsdk.groups.create(group) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.groups_endpoint, + self.URL + "/" + self.GROUPS_ENDPOINT, json=group, headers=utils.construct_header(token, utils.CTJSON), ) @@ -25,15 +70,33 @@ def create(self, group: dict, token: str): errors.groups["create"], http_resp.status_code ) else: - location = http_resp.headers.get("location") - mf_resp.value = location.split("/")[2] + mf_resp.value = http_resp.json() return mf_resp def get(self, group_id: str, token: str): - """Gets a group entity""" + """Gets a group entity + + Provides information about a group entity using the provided group_id + and a token. + + params: + group_id: str - group id + token: str - token used to get a group. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> group_id = "group_id" + >>> mf_resp = mfsdk.groups.get(group_id) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.groups_endpoint + "/" + group_id, + self.URL + "/" + self.GROUPS_ENDPOINT + "/" + group_id, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 200: @@ -46,10 +109,37 @@ def get(self, group_id: str, token: str): return mf_resp def get_all(self, query_params: dict, token: str): - """Gets all groups from database""" + """Gets all groups from database + + Gets all groups from database using the provided query_params and token. + + params: + query_params: dict - query parameters for example: + { + "offset": 0, + "limit": 10, + } + where offset is the number of items to skip before starting to collect + the result set and limit is the numbers of items to return. + token: str - token used to get all groups. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> query_params = { + ... "offset": 0, + ... "limit": 10, + ... } + >>> mf_resp = mfsdk.groups.get_all(query_params) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.groups_endpoint, + self.URL + "/" + self.GROUPS_ENDPOINT, headers=utils.construct_header(token, utils.CTJSON), params=query_params, ) @@ -63,30 +153,88 @@ def get_all(self, query_params: dict, token: str): return mf_resp def parents(self, group_id: str, query_params: dict, token: str): - """Gets parents for a specific group from database""" + """Gets parents for a specific group from database + + Provides information about parents for a specific group from database + when provided with group_id, query_params and token. + + params: + group_id: str - group id of the child group in question. + query_params: dict - query parameters for example: + { + "offset": 0, + "limit": 10, + } + where offset is the number of items to skip before starting to collect + the result set and limit is the numbers of items to return. + token: str - token used to get parents for a specific group. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> group_id = "group_id" + >>> query_params = { + ... "offset": 0, + ... "limit": 10, + ... } + >>> mf_resp = mfsdk.groups.parents(group_id, query_params) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.groups_endpoint + "/" + group_id + - "/parents", + self.URL + "/" + self.GROUPS_ENDPOINT + "/" + group_id + "/parents", headers=utils.construct_header(token, utils.CTJSON), params=query_params, ) if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.groups["get_all"], http_resp.status_code + errors.groups["parents"], http_resp.status_code ) else: mf_resp.value = http_resp.json() return mf_resp def children(self, group_id: str, query_params: dict, token: str): - """Gets children for a specific group from database""" + """Gets children for a specific group from database + + Provides information about children for a specific group from database + when provided with group_id, query_params and token. + + params: + group_id: str - group id of the parent group in question. + query_params: dict - query parameters for example: + { + "offset": 0, + "limit": 10, + } + where offset is the number of items to skip before starting to collect + the result set and limit is the numbers of items to return. + token: str - token used to get children for a specific group. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> group_id = "group_id" + >>> query_params = { + ... "offset": 0, + ... "limit": 10, + ... } + >>> mf_resp = mfsdk.groups.children(group_id, query_params) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.groups_endpoint + "/" + group_id + - "children", + self.URL + "/" + self.GROUPS_ENDPOINT + "/" + group_id + "/children", headers=utils.construct_header(token, utils.CTJSON), params=query_params, ) @@ -100,10 +248,37 @@ def children(self, group_id: str, query_params: dict, token: str): return mf_resp def update(self, group_id: str, group: dict, token: str): - """Updates group entity""" + """Updates group entity + + Updates a group entity in the database using the provided group_id, + group object and token. It updates the group name and metadata. + + params: + group_id: str - group id + group: dict - group information for example: + { + "name": "groupName", + } + token: str - token used to update a group. + + returns: + + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> group_id = "group_id" + >>> group = { + ... "name": "groupName", + ... } + >>> mf_resp = mfsdk.groups.update(group_id, group) + >>> mf_resp + """ http_resp = requests.put( - self.url + "/" + self.groups_endpoint + "/" + group_id, - json=group, + self.URL + "/" + self.GROUPS_ENDPOINT + "/" + group_id, + data=json.dumps(group), headers=utils.construct_header(token, utils.CTJSON), ) mf_resp = response.Response() @@ -112,13 +287,44 @@ def update(self, group_id: str, group: dict, token: str): mf_resp.error.message = errors.handle_error( errors.groups["update"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() return mf_resp def members(self, group_id: str, query_params: dict, token: str): - """Get list of members ID's from group""" + """Gets members associated with the group specified by id + + Provides information about members associated with the group specified by id + when provided with group_id, query_params and token. + + params: + group_id: str - group id + query_params: dict - query parameters for example: + { + "offset": 0, + "limit": 10, + } + where offset is the number of items to skip before starting to collect + the result set and limit is the numbers of items to return. + token: str - token used to get members associated with the group specified by id. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> group_id = "group_id" + >>> query_params = { + ... "offset": 0, + ... "limit": 10, + ... } + >>> mf_resp = mfsdk.groups.members(group_id, query_params) + >>> mf_resp + """ http_resp = requests.get( - self.url + "/" + self.groups_endpoint + "/" + group_id + - "/members", + self.URL + "/" + self.GROUPS_ENDPOINT + "/" + group_id + "/members", headers=utils.construct_header(token, utils.CTJSON), params=query_params, ) @@ -128,12 +334,44 @@ def members(self, group_id: str, query_params: dict, token: str): mf_resp.error.message = errors.handle_error( errors.groups["members"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() return mf_resp def memberships(self, member_id: str, query_params: dict, token: str): - """Get list of members ID's from group""" + """Retrieves a list of groups the user is connected to + + Retrieves a list of groups the user is connected to when provided with + the users id, query_params and token. + + params: + member_id: str - user id + query_params: dict - query parameters for example: + { + "offset": 0, + "limit": 10, + } + where offset is the number of items to skip before starting to collect + the result set and limit is the numbers of items to return. + token: str - token used to retrieve a list of groups the user is connected to. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> member_id = "member_id" + >>> query_params = { + ... "offset": 0, + ... "limit": 10, + ... } + >>> mf_resp = mfsdk.groups.memberships(member_id, query_params) + >>> mf_resp + """ http_resp = requests.get( - self.url + "/members/" + member_id + "/" + self.groups_endpoint, + self.URL + "/users" + "/" + member_id + "/memberships", headers=utils.construct_header(token, utils.CTJSON), params=query_params, ) @@ -141,34 +379,85 @@ def memberships(self, member_id: str, query_params: dict, token: str): if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.groups["members"], http_resp.status_code + errors.groups["memberships"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() return mf_resp - def assign(self, group_id: str, members_ids, member_type: str, token: str): - """Assign""" - payload = {"type": member_type, "members": members_ids} + def assign(self, group_id: str, member_id: str, member_type: list, token: str): + """Assign + + Assigns a member to a group when provided with group_id, member_id, + member_type which is their action and a token. + + params: + group_id: str - group id + member_id: str - member id + member_type: list - actions the member can perform for example: + [ + "m_read", "m_write", "m_admin" + ] + token: str - token used to assign a member to a group. + + returns: + mf_resp: "Policy created" + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> group_id = "group_id" + >>> member_id = "member_id" + >>> member_type = [ + ... "m_read", "m_write", "m_admin" + ... ] + >>> mf_resp = mfsdk.groups.assign(group_id, member_id, member_type) + >>> mf_resp + """ + payload = {"object": group_id, "subject": member_id, "actions": member_type} mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.groups_endpoint + "/" + group_id + - "/members", + self.URL + "/users/policies", headers=utils.construct_header(token, utils.CTJSON), json=payload, ) - if http_resp.status_code != 200: + if http_resp.status_code != 201: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.groups["assign"], http_resp.status_code ) + else: + mf_resp.value = "Policy created" return mf_resp def unassign(self, group_id: str, token: str, members_ids): - """Assign""" - payload = {"members": members_ids} + """Unassign + + Deletes a user's policy from over a group when provided with group_id, + token and members_ids. + + params: + group_id: str - group id + token: str - token used to delete a user's policy from over a group. + members_ids: str - member id + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> group_id = "group_id" + >>> members_ids = "members_ids" + >>> mf_resp = mfsdk.groups.unassign(group_id, members_ids) + >>> mf_resp + """ + payload = {"Object": group_id, "Subject": members_ids} mf_resp = response.Response() http_resp = requests.delete( - self.url + "/" + self.groups_endpoint + "/" + group_id + - "/members", + self.URL + "/users/policies" + "/" + members_ids + "/" + group_id, headers=utils.construct_header(token, utils.CTJSON), json=payload, ) @@ -179,34 +468,35 @@ def unassign(self, group_id: str, token: str, members_ids): ) return mf_resp - def delete(self, group_id: str, token: str): - """Deletes a group entity from database""" - http_resp = requests.delete( - self.url + "/" + self.groups_endpoint + "/" + group_id, - headers=utils.construct_header(token, utils.CTJSON), - ) - mf_resp = response.Response() - if http_resp.status_code != 204: - mf_resp.error.status = 1 - mf_resp.error.message = errors.handle_error( - errors.groups["delete"], http_resp.status_code - ) - return mf_resp - - def share_groups( - self, token: str, user_group_id: str, thing_group_id: str - ): - """Adds access rights on thing groups to the user group""" - mf_resp = response.Response() + def disable(self, group_id: str, user_token: str): + """Disables a group entity from database + + Deletes a group from the database when provided with group_id and + user_token. + + params: + group_id: str - group id + user_token: str - token used to delete a group. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> group_id = "group_id" + >>> mf_resp = mfsdk.groups.disable(group_id) + >>> mf_resp + """ http_resp = requests.post( - self.url + "/" + self.groups_endpoint + "/" + user_group_id + - "/share", - headers=utils.construct_header(token, utils.CTJSON), - json=thing_group_id, + self.URL + "/" + self.GROUPS_ENDPOINT + "/" + group_id + "/disable", + headers=utils.construct_header(user_token, utils.CTJSON), ) + mf_resp = response.Response() if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( - errors.groups["assign"], http_resp.status_code + errors.groups["disable"], http_resp.status_code ) return mf_resp diff --git a/mainflux/keys.py b/mainflux/keys.py deleted file mode 100644 index acc7e4c..0000000 --- a/mainflux/keys.py +++ /dev/null @@ -1,60 +0,0 @@ -import requests - -from mainflux import response -from mainflux import errors -from mainflux import utils - - -class Keys: - keys_endpoint = "keys" - - def __init__(self, url: str): - self.url = url - - def issue(self, duration: str, token: str): - """Generates a new API key""" - payload = {"type": 2, "duration": duration} - mf_resp = response.Response() - http_resp = requests.post( - self.url + "/" + self.keys_endpoint, - json=payload, - headers=utils.construct_header(token, utils.CTJSON), - ) - if http_resp.status_code != 201: - mf_resp.error.status = 1 - mf_resp.error.message = errors.handle_error( - errors.things["create"], http_resp.status_code - ) - else: - mf_resp.value = http_resp.json() - return mf_resp - - def get_key_details(self, key_id: str, token: str): - """Gets API key details for the given key""" - mf_resp = response.Response() - http_resp = requests.get( - self.url + "/" + self.keys_endpoint + "/" + key_id, - headers=utils.construct_header(token, utils.CTJSON), - ) - if http_resp.status_code != 200: - mf_resp.error.status = 1 - mf_resp.error.message = errors.handle_error( - errors.things["create"], http_resp.status_code - ) - else: - mf_resp.value = http_resp.json() - return mf_resp - - def revoke(self, key_id: str, token: str): - """Revoke API key identified by the given ID.""" - mf_resp = response.Response() - http_resp = requests.delete( - self.url + "/" + self.keys_endpoint + "/" + key_id, - headers=utils.construct_header(token, utils.CTJSON), - ) - if http_resp.status_code != 204: - mf_resp.error.status = 1 - mf_resp.error.message = errors.handle_error( - errors.things["delete"], http_resp.status_code - ) - return mf_resp diff --git a/mainflux/messages.py b/mainflux/messages.py index 0791e60..2e8e815 100644 --- a/mainflux/messages.py +++ b/mainflux/messages.py @@ -6,25 +6,72 @@ class Messages: + """Messages API client + + Messages API client enables interaction with Mainflux Messages API. + It provides methods for sending and reading messages. + + Attributes: + adapter_url: URL of the Mainflux Messages adapter + reader_url: URL of the Mainflux Messages reader + """ def __init__(self, adapter_url: str, reader_url: str): self.adapter_url = adapter_url self.reader_url = reader_url + """Initializes Messages API client with adapter and reader URLs + + params: + adapter_url: URL of the Mainflux Messages adapter + reader_url: URL of the Mainflux Messages reader + + returns: + Messages API client object + + raises: + None + """ + def send(self, channel_id: str, msg: str, thing_key: str): + """Sends message via HTTP protocol + + Sends message to a given channel via HTTP protocol. Message is sent + through a writer add-on such as timescale. Message is sent to a + http port specific to the writer add-on. The thing and channel must be + created before sending the message and connected. + + params: + channel_id: ID of the channel to send message to + msg: message to send to the channel that should be in encoded into + bytes format for example: + [{"bn":"demo", "bu":"V", "n":"voltage", "u":"V", "v":5}] + thing_key: secret of the thing sending the message + + returns: + mf_resp: response object + + usage: - def send(self, channel_id: str, msg: dict, thing_key: str): - """Sends message via HTTP protocol""" + >>> from mainflux import sdk + >>> mfsdk = sdk.Sdk("http://localhost:9011") + >>> channel_id = "2b86beba-83dd-4b39-8165-4dda4e6eb4ad" + >>> msg = '[{"bn":"demo", "bu":"V", "n":"voltage", "u":"V", "v":5}]' + >>> thing_key = "fc68b31b-d7fd-4879-b3a7-0baf4580c5b1" + >>> mf_resp = mfsdk.messages.send(channel_id, msg, thing_key) + >>> mf_resp + """ chan_name_parts = channel_id.split(".", 2) chan_id = chan_name_parts[0] subtopic = "" if len(chan_name_parts) == 2: - subtopic = chan_name_parts[0].replace(".", "/", -1) + subtopic = chan_name_parts[1].replace(".", "/", -1) mf_resp = response.Response() http_resp = requests.post( self.adapter_url + "/http/channels/" + chan_id + "/messages/" + subtopic, - json=msg, + data=bytes(msg, 'utf-8'), headers=utils.construct_header( utils.ThingPrefix + thing_key, utils.CTJSON), ) + print(http_resp) if http_resp.status_code != 202: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( @@ -33,12 +80,31 @@ def send(self, channel_id: str, msg: dict, thing_key: str): return mf_resp def read(self, channel_id: str, token: str): - """Reads messages from database for a given channel""" + """Reads messages from database for a given channel + + Reads message from a given channel via HTTP protocol. Message is read + through a reader add-on such as timescale. + + params: + channel_id: ID of the channel to read message from + token: token of the user reading the message + + returns: + mf_resp: response object + + usage: + + >>> from mainflux import sdk + >>> mfsdk = sdk.Sdk("http://localhost:9011") + >>> channel_id = "2b86beba-83dd-4b39-8165-4dda4e6eb4ad" + >>> mf_resp = mfsdk.messages.read(channel_id, token) + >>> mf_resp + """ chan_name_parts = channel_id.split(".", 2) chan_id = chan_name_parts[0] subtopic = "" if len(chan_name_parts) == 2: - subtopic = chan_name_parts[0].replace(".", "/", -1) + subtopic = chan_name_parts[1].replace(".", "/", -1) mf_resp = response.Response() http_resp = requests.get( self.reader_url + "/channels/" + chan_id + "/messages", diff --git a/mainflux/sdk.py b/mainflux/sdk.py index e3a5b6e..464927d 100644 --- a/mainflux/sdk.py +++ b/mainflux/sdk.py @@ -3,7 +3,6 @@ from mainflux import messages from mainflux import channels from mainflux import groups -from mainflux import keys from mainflux import boostrap from mainflux import certs @@ -21,7 +20,7 @@ def __init__( http_adapter_url=default_url, certs_url=default_url, bootstrap_url=default_url, - auth_url=default_url, + groups_url=default_url, ): self.users = users.Users(users_url) self.things = things.Things(things_url) @@ -29,8 +28,7 @@ def __init__( adapter_url=http_adapter_url, reader_url=reader_url ) self.channels = channels.Channels(things_url) - self.groups = groups.Groups(auth_url) - self.keys = keys.Keys(auth_url) + self.groups = groups.Groups(groups_url) self.bootstrap = boostrap.Bootstrap(bootstrap_url) self.certs = certs.Certs(certs_url) self.version_url = things_url diff --git a/mainflux/things.py b/mainflux/things.py index 0884da0..b222e75 100644 --- a/mainflux/things.py +++ b/mainflux/things.py @@ -1,24 +1,65 @@ import requests - from mainflux import response from mainflux import errors from mainflux import utils class Things: - things_endpoint = "things" - connect_endpoint = "connect" - disconnect_endpoint = "disconnect" - identify_endpoint = "identify" + """Things API client. + + Things API is used for creating and managing things. + It is used for creating new things, creating multiple things + getting thing information, updating thing information, disabling + and enabling things ,and connecting and disconnecting things. + + Attributes: + URL: str - URL of the Things API + THINGS_ENDPOINT: str - Things API endpoint + """ + THINGS_ENDPOINT = "things" def __init__(self, url: str): - self.url = url - + self.URL = url + """Initializes Things API client. + + params: + url: str - URL of the Things API + + returns: + Thigs: Things - Things API client + + raises: + None + """ def create(self, thing: dict, token: str): - """Creates thing entity in the database""" + """Creates thing entity in the database. + + Creates a new thing with provided thing information. + If token is provided, it will be used to create a new thing + + params: + thing: dict - thing information for example: + { + "name": "thing1" + } + token: str - token used for creating a new thing + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing = { + ... "name": "thing1", + ... } + >>> mf_resp = mfsdk.things.create(thing) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.things_endpoint, + self.URL + "/" + self.THINGS_ENDPOINT, json=thing, headers=utils.construct_header(token, utils.CTJSON), ) @@ -28,19 +69,46 @@ def create(self, thing: dict, token: str): errors.things["create"], http_resp.status_code ) else: - location = http_resp.headers.get("location") - mf_resp.value = location.split("/")[2] + mf_resp.value = http_resp.json() return mf_resp def create_bulk(self, things: list, token: str): - """Creates multiple things in a bulk""" + """Creates multiple things in bulk. + + Creates multiple new things with provided things information. + If a token is provided, it will be used to create the new things. + + params: + things: list - a list of things with theri information for example: + [ + {"name": "thing2"}, + {"name": "thing3"}, + {"name": "thing4"} + ] + token: str - token used for creating the new things. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> things = [ + ... {"name": "thing2"}, + ... {"name": "thing3"}, + ... {"name": "thing4"} + ... ] + >>> mf_resp = mfsdk.things.create_bulk(things) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.things_endpoint + "/bulk", + self.URL + "/" + self.THINGS_ENDPOINT + "/bulk", json=things, headers=utils.construct_header(token, utils.CTJSON), ) - if http_resp.status_code != 201: + if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.things["create_bulk"], http_resp.status_code @@ -50,10 +118,30 @@ def create_bulk(self, things: list, token: str): return mf_resp def get(self, thing_id: str, token: str): - """Gets a thing entity for a logged-in user""" + """Gets a thing entity. + + Provides information about a thing with provided thing ID and token. + Information about a thing is provided in a JSON format and includes the name + its owner, secret,tags and status. + + params: + thing_id: str - ID of the thing + token: str - token used for getting thing information + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> mf_resp = mfsdk.things.get(thing_id) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.things_endpoint + "/" + thing_id, + self.URL + "/" + self.THINGS_ENDPOINT + "/" + thing_id, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 200: @@ -66,10 +154,37 @@ def get(self, thing_id: str, token: str): return mf_resp def get_all(self, query_params: dict, token: str): - """Gets all things from database""" + """Gets all things from database. + + Provides information about all things in a JSON format. It is controlled + by a set of query parameters and a valid token. + + params: + query_params: dict - query parameters for example: + { + "offset": 0, + "limit": 10 + } + where offset is the number of things to skip and limit is the maximum + token: str - token used for getting all things information + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> query_params = { + ... "offset": 0, + ... "limit": 10 + ... } + >>> mf_resp = mfsdk.things.get_all(query_params) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.things_endpoint, + self.URL + "/" + self.THINGS_ENDPOINT, headers=utils.construct_header(token, utils.CTJSON), params=query_params, ) @@ -83,10 +198,39 @@ def get_all(self, query_params: dict, token: str): return mf_resp def get_by_channel(self, channel_id: str, query_params: dict, token: str): - """Gets all things to which a specific thing is connected to""" + """Gets all things to which a specific thing is connected to. + + Provides a list of all things that are connected to a specific channel when + given a channel ID and valid token. + + params: + channel_id: str - ID of the channel + query_params: dict - query parameters for example: + { + "offset": 0, + "limit": 10 + } + where offset is the number of things to skip and limit is the maximum + token: str - token used for getting all things information + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> channel_id = "567f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> query_params = { + ... "offset": 0, + ... "limit": 10 + ... } + >>> mf_resp = mfsdk.things.get_by_channel(channel_id, query_params) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/channels/" + channel_id + "/" + self.things_endpoint, + self.URL + "/channels/" + channel_id + "/" + self.THINGS_ENDPOINT, headers=utils.construct_header(token, utils.CTJSON), params=query_params, ) @@ -100,9 +244,36 @@ def get_by_channel(self, channel_id: str, query_params: dict, token: str): return mf_resp def update(self, thing_id: str, thing: dict, token: str): - """Updates thing entity""" - http_resp = requests.put( - self.url + "/" + self.things_endpoint + "/" + thing_id, + """Updates thing entity. + + Allows a logged in user to make changes and update a thing's + information with provided thing ID and valid token. Information + such as the metadata and name can be updated. + + params: + thing_id: str - ID of the thing + thing: dict - thing information for example: + { + "name": "thing1" + } + token: str - token used for updating thing information + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> thing = { + ... "name": "thing2", + ... } + >>> mf_resp = mfsdk.things.update(thing_id, thing) + >>> mf_resp + """ + http_resp = requests.patch( + self.URL + "/" + self.THINGS_ENDPOINT + "/" + thing_id, json=thing, headers=utils.construct_header(token, utils.CTJSON), ) @@ -112,27 +283,204 @@ def update(self, thing_id: str, thing: dict, token: str): mf_resp.error.message = errors.handle_error( errors.things["update"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() return mf_resp + + def update_thing_secret(self, thing_id: str, thing: dict, token: str): + """Updates thing secret. + + Allows a logged in user to make changes and update a thing's + information with provided thing ID and valid token. The thing's + secret can be updated. + + params: + thing_id: str - ID of the thing + thing: dict - thing information for example: + { + "key": "thing1" + } + token: str - token used for updating thing information + + returns: + mf_resp: response.Response - response object. + + Usage:: - def delete(self, thing_id: str, token: str): - """Deletes a thing entity from database""" - http_resp = requests.delete( - self.url + "/" + self.things_endpoint + "/" + thing_id, + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> thing = { + ... "key": "thing2", + ... } + >>> mf_resp = mfsdk.things.update_thing_secret(thing_id, thing) + >>> mf_resp + """ + http_resp = requests.patch( + self.URL + "/" + self.THINGS_ENDPOINT + "/" + thing_id + "/secret", + json=thing, headers=utils.construct_header(token, utils.CTJSON), ) mf_resp = response.Response() - if http_resp.status_code != 204: + if http_resp.status_code != 200: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.things["update_thing_secret"], http_resp.status_code + ) + else: + mf_resp.value = http_resp.json() + return mf_resp + + def update_thing_tags(self, thing_id: str, thing: dict, token: str): + """Updates thing tags. + + Allows a logged in user to make changes and update a thing's + information with provided thing ID and valid token. The thing's + tags can be updated. + + params: + thing_id: str - ID of the thing + thing: dict - thing information for example: + { + "tags": ["tag1", "tag2"] + } + token: str - token used for updating thing information + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> thing = { + ... "tags": ["tag1", "tag2"] + ... } + >>> mf_resp = mfsdk.things.update_thing_tags(thing_id, thing) + >>> mf_resp + """ + http_resp = requests.patch( + self.URL + "/" + self.THINGS_ENDPOINT + "/" + thing_id + "/tags", + json=thing, + headers=utils.construct_header(token, utils.CTJSON), + ) + mf_resp = response.Response() + if http_resp.status_code != 200: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.things["update_thing_tags"], http_resp.status_code + ) + else: + mf_resp.value = http_resp.json() + return mf_resp + + def update_thing_owner(self, thing_id: str, thing: dict, token: str): + """Updates thing owner. + + Allows a logged in user to make changes and update a thing's + information with provided thing ID and valid token. The thing + owner can be updated. + + params: + thing_id: str - ID of the thing + thing: dict - thing information for example: + { + "owner": "user1" + } + token: str - token used for updating thing information + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> thing = { + ... "owner": "user1" + ... } + >>> mf_resp = mfsdk.things.update_thing_owner(thing_id, thing) + >>> mf_resp + """ + http_resp = requests.patch( + self.URL + "/" + self.THINGS_ENDPOINT + "/" + thing_id + "/owner", + json=thing, + headers=utils.construct_header(token, utils.CTJSON), + ) + mf_resp = response.Response() + if http_resp.status_code != 200: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.things["update_thing_owner"], http_resp.status_code + ) + else: + mf_resp.value = http_resp.json() + return mf_resp + + def disable(self, thing_id: str, token: str): + """Deletes a thing entity from the database. + + Deletes a thing with provided thing ID and valid token. + + params: + thing_id: str - ID of the thing + token: str - token used for deleting thing + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> mf_resp = mfsdk.things.disable(thing_id) + >>> mf_resp + """ + http_resp = requests.post( + self.URL + "/" + self.THINGS_ENDPOINT + "/" + thing_id + "/disable", + headers=utils.construct_header(token, utils.CTJSON), + ) + mf_resp = response.Response() + if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.things["delete"], http_resp.status_code ) return mf_resp - def connects(self, thing_ids: list, channel_ids: list, token: str): - """Connects thing and channel""" - payload = {"channel_ids": channel_ids, "thing_ids": thing_ids} + def connects(self, thing_ids: list, channel_ids: list, actions: list, token: str): + """Connects things and channels. + + Connects multiple things and channels with provided thing IDs + as the subjects, channel IDs as the objects, actions that the + thing can partake in and a valid token. + + params: + thing_ids: list - list of thing IDs + channel_ids: list - list of channel IDs + actions: list - list of actions for example: + ["m_write", "m_read"] + token: str - token used for connecting things and channels + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_ids = ["fd4f7da5-b7bf-49b7-bf2f-99995e78afd9"] + >>> channel_ids = ["567f7da5-b7bf-49b7-bf2f-99995e78afd9"] + >>> actions = ["m_write", "m_read"] + >>> mf_resp = mfsdk.things.connects(thing_ids, channel_ids, actions) + >>> mf_resp + """ + payload = {"subjects": thing_ids, "objects": channel_ids, "actions": actions} http_resp = requests.post( - self.url + "/" + self.connect_endpoint, + self.URL + "/connect", headers=utils.construct_header(token, utils.CTJSON), json=payload, ) @@ -147,10 +495,31 @@ def connects(self, thing_ids: list, channel_ids: list, token: str): return mf_resp def disconnects(self, thing_ids: list, channel_ids: list, token: str): - """Disconnect thing and channel""" - payload = {"thing_ids": thing_ids, "channel_ids": channel_ids} - http_resp = requests.delete( - self.url + "/" + self.disconnect_endpoint, + """Disconnect things and channels. + + Disconnects multiple things and channels with provided thing IDs + as the subjects, channel IDs as the objects and a valid token. + + params: + thing_ids: list - list of thing IDs + channel_ids: list - list of channel IDs + token: str - token used for disconnecting things and channels + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_ids = ["fd4f7da5-b7bf-49b7-bf2f-99995e78afd9"] + >>> channel_ids = ["567f7da5-b7bf-49b7-bf2f-99995e78afd9"] + >>> mf_resp = mfsdk.things.disconnects(thing_ids, channel_ids) + >>> mf_resp + """ + payload = {"subjects": thing_ids, "objects": channel_ids} + http_resp = requests.post( + self.URL + "/disconnect", headers=utils.construct_header(token, utils.CTJSON), json=payload, ) @@ -162,14 +531,40 @@ def disconnects(self, thing_ids: list, channel_ids: list, token: str): ) return mf_resp - def connect(self, thing_id: str, channel_id: str, token: str): - """Connects thing and channel""" - http_resp = requests.put( - self.url + "/channels/" + channel_id + "/things/" + thing_id, + def connect(self, thing_id: str, channel_id: str, action: str, token: str): + """Connects thing and channel. + + Connects a thing and channel with provided thing ID as the subject, + channel ID as the object, action that the thing can partake in and a + valid token. + + params: + thing_id: str - ID of the thing + channel_id: str - ID of the channel + action: str - action for example: "m_write" + token: str - token used for connecting thing and channel + + returns: + mf_resp: "connected" + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> channel_id = "567f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> action = "m_write" + >>> mf_resp = mfsdk.things.connect(thing_id, channel_id, action) + >>> mf_resp + """ + payload= {"subject": thing_id, "object": channel_id, "action": action} + http_resp = requests.post( + self.URL + "/policies", headers=utils.construct_header(token, utils.CTJSON), + json= payload, ) mf_resp = response.Response() - if http_resp.status_code != 200: + if http_resp.status_code != 201: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.things["connect"], http_resp.status_code @@ -179,10 +574,33 @@ def connect(self, thing_id: str, channel_id: str, token: str): return mf_resp def disconnect(self, thing_id: str, channel_id: str, token: str): - """Disconnect thing and channel""" + """Disconnects thing and channel. + + Disconnects a thing and channel with provided thing ID as the subject, + channel ID as the object and a valid token. + + params: + thing_id: str - ID of the thing + channel_id: str - ID of the channel + token: str - token used for disconnecting thing and channel + + returns: + mf_resp: response.Response - response object. + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> thing_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> channel_id = "567f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> mf_resp = mfsdk.things.disconnect(thing_id, channel_id) + >>> mf_resp + """ + payload = {"subject": thing_id, "object": channel_id} http_resp = requests.delete( - self.url + "/channels/" + channel_id + "/things/" + thing_id, + self.URL + "/policies" + "/" + thing_id + "/" + channel_id, headers=utils.construct_header(token, utils.CTJSON), + json=payload, ) mf_resp = response.Response() if http_resp.status_code != 204: @@ -190,4 +608,97 @@ def disconnect(self, thing_id: str, channel_id: str, token: str): mf_resp.error.message = errors.handle_error( errors.things["disconnect"], http_resp.status_code ) + else: + mf_resp.value = "Disconnected" + return mf_resp + + def share_thing(self, user_id: str, channel_id: str, actions: list, token: str): + """Shares thing. + + Allows a logged in user to create new policies for a thing over a channel + provided with a user ID, channel ID, actions that the thing can partake in + and a valid token. + + params: + user_id: str - ID of the user + channel_id: str - ID of the channel + actions: list - list of actions for example: + ["m_write", "m_read"] + token: str - token used for sharing thing + + returns: + mf_resp: "OK" + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> user_id = "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> channel_id = "567f7da5-b7bf-49b7-bf2f-99995e78afd9" + >>> actions = ["m_write", "m_read"] + >>> mf_resp = mfsdk.things.share_thing(user_id, channel_id, actions) + >>> mf_resp + """ + payload = {"object": channel_id, "subject": user_id, "actions": actions, "external": True} + http_resp = requests.post( + self.URL + "/policies", + headers=utils.construct_header(token, utils.CTJSON), + data=payload + ) + mf_resp = response.Response() + if http_resp.status_code != 201: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.things["share_thing"], http_resp.status_code + ) + else: + mf_resp.value = "OK" + return mf_resp + + def authorise_thing(self,access_request: dict, token: str): + """Authorises thing. + + Creates policies for a thing as a subject over a channel which is the object. + It authorizes the thing to perform some actions over the channel. + + params: + + access_request: dict - access request information for example: + { + "subject": "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9", + "object": "567f7da5-b7bf-49b7-bf2f-99995e78afd9", + "actions": "m_write" + "entity_type": "group" + } + token: str - token used for authorising thing + + returns: + mf_resp: "True" + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(things_url="http://localhost:9000") + >>> access_request = { + ... "subject": "fd4f7da5-b7bf-49b7-bf2f-99995e78afd9", + ... "object": "567f7da5-b7bf-49b7-bf2f-99995e78afd9", + ... "actions": "m_write" + ... "entity_type": "group" + ... } + >>> mf_resp = mfsdk.things.authorise_thing(access_request) + >>> mf_resp + """ + mf_resp = response.Response() + http_resp= requests.post( + self.URL +"/channels/object/access", + headers=utils.construct_header(token, utils.CTJSON), + json= access_request + ) + if http_resp.status_code != 200: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.things["authorise_thing"], http_resp.status_code + ) + else: + mf_resp.value = "True" return mf_resp diff --git a/mainflux/users.py b/mainflux/users.py index 2761866..595b318 100644 --- a/mainflux/users.py +++ b/mainflux/users.py @@ -7,44 +7,178 @@ class Users: - users_endpoint = "users" + """Users API client. + + Users API is used for creating and managing users. + It is used for creating new users, logging in, refreshing tokens, + getting user information, updating user information, disabling + and enabling users. + + Attributes: + URL: str - URL of the Users API + USERS_ENDPOINT: str - Users API endpoint + """ + USERS_ENDPOINT = "users" def __init__(self, url: str): - self.url = url + self.URL = url + """Initializes Users API client. + + params: + url: str - URL of the Users API + + returns: + Users: Users - Users API client + + raises: + None + """ + def create(self, user: dict, token: str = ""): + """Creates a new user. + + Creates a new user with provided user information. + If token is provided, it will be used to create a new user. - def create(self, user: dict): - """Registers new user account given email and password. New account - will be uniquely identified by its email address.""" + params: + user: dict - user information for example: + { + "name": "example", + "credentials": { + "identity": "example@main.com", + "secret": "12345678" + } + } + token: str - token used for creating a new user + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> user = { + ... "name": "example", + ... "credentials": { + ... "identity": "example@mail.com", + ... "secret": "12345678" + ... } + ... } + >>> mf_resp = mfsdk.users.create(user) + >>> mf_resp + """ mf_resp = response.Response() - http_resp = requests.post(self.url + "/users", json=user) + http_resp = requests.post( + self.URL + "/users", + json=user, + headers=utils.construct_header(token, utils.CTJSON), + ) if http_resp.status_code != 201: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.users["create"], http_resp.status_code ) else: - location = http_resp.headers.get("location") - mf_resp.value = location.split("/")[2] + mf_resp.value = http_resp.json() return mf_resp def login(self, user: dict): - """Generates an access token when provided with proper credentials.""" + """Generates an access token when provided with proper credentials. + + Issues a new access and refresh token for a user for authenticating + into the system. + + params: + user: a dict with the user information and password for example: + {"credentials":{ + "identity": "user@mainflux.com", + "secret": "12345678" + } + } + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> credentials= { + ... "identity": "user@mainflux.com", + ... "secret": "12345678" + ... } + >>> mf_resp = mfsdk.users.login(credentials) + >>> mf_resp + """ mf_resp = response.Response() - http_resp = requests.post(self.url + "/tokens", json=user) + http_resp = requests.post(self.URL + "/users/tokens/issue", json=user) if http_resp.status_code != 201: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.users["login"], http_resp.status_code ) else: - mf_resp.value = http_resp.json()["token"] + mf_resp.value = http_resp.json() + return mf_resp + + def refresh_token(self, refresh_token: str): + """Refreshes Access and Refresh Token used for authenticating + into the system. + + Creates a new access token and refresh token for a user when + provided with a valid refresh token. + + params: + refresh_token: str - token used to refresh access. + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> mf_resp = mfsdk.users.refresh_token(refresh_token) + >>> mf_resp + """ + mf_resp = response.Response() + http_resp = requests.post( + self.URL + "/users/tokens/refresh", + headers=utils.construct_header(refresh_token, utils.CTJSON), + ) + if http_resp.status_code != 201: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.users["refresh_token"], http_resp.status_code + ) + else: + mf_resp.value = http_resp.json() return mf_resp def get(self, user_id: str, token: str): - """Gets a user information""" + """Gets a user information. + + Gets info on currently logged in user. Info is obtained + using authorization token and the user id. + + params: + user_id: str - user information eg "886b4266-77d1-4258-abae-2931fb4f16de", + token: str - token used for creating a new user + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> user_id = "886b4266-77d1-4258-abae-2931fb4f16de" + >>> token = "" + >>> mf_resp = mfsdk.users.get(user_id, token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.get( - self.url + "/" + self.users_endpoint + "/" + user_id, + self.URL + "/" + self.USERS_ENDPOINT + "/" + user_id, headers=utils.construct_header(token, utils.CTJSON), ) if http_resp.status_code != 200: @@ -56,11 +190,37 @@ def get(self, user_id: str, token: str): mf_resp.value = http_resp.json() return mf_resp - def get_all(self, query_params: dict, admin_token: str): - """Retrieves a list of users""" + def get_all(self, query_params: dict, user_token: str): + """Retrieves a list of users. + + Gets a list of users from the database when provided with a user token and + some parameters. + + params: + user_token: str - token used for creating a new user + query_params: dict - has a limit(int) which is the size of the subset + to be expected and offset which is the number of items to skip during retrieval. + For example: + { + "offset" : 0, "limit" : 10 + } + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> query_params = { + ... "offset" : 0, "limit" : 10 + ... } + >>> mf_resp = mfsdk.users.get(query_params, user_token) + >>> mf_resp + """ http_resp = requests.get( - self.url + "/" + self.users_endpoint, - headers=utils.construct_header(admin_token, utils.CTJSON), + self.URL + "/" + self.USERS_ENDPOINT, + headers=utils.construct_header(user_token, utils.CTJSON), params=query_params, ) mf_resp = response.Response() @@ -74,10 +234,44 @@ def get_all(self, query_params: dict, admin_token: str): return mf_resp def update(self, user: dict, user_token: str): - """Updates info on currently logged in user. Info is updated using - authorization user_token""" - http_resp = requests.put( - self.url + "/" + self.users_endpoint, + """Updates information on currently logged in user. + + Information such as name and metadata is updated using authorization user_token + + params: + user: dict - user information for example: + { + "name": "example", + "id": "886b4266-77d1-4258-abae-2931fb4f16de" + "credentials": { + "identity": "example@main.com", + "secret": "12345678" + }, + "metadata": { + "foo": "bar" + } + } + token: str - token used for creating a new user + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> user = { + ... "name": "example", + ... "id": "886b4266-77d1-4258-abae-2931fb4f16de" + ... "metadata": { + ... "foo": "bar" + ... } + ... } + >>> mf_resp = mfsdk.users.update(user, token) + >>> mf_resp + """ + http_resp = requests.patch( + self.URL + "/" + self.USERS_ENDPOINT + "/" + user["id"], headers=utils.construct_header(user_token, utils.CTJSON), data=json.dumps(user), ) @@ -87,50 +281,401 @@ def update(self, user: dict, user_token: str): mf_resp.error.message = errors.handle_error( errors.users["update"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() + return mf_resp + + def update_user_identity(self, user: dict, user_token: str): + """Updates identity information on currently logged in user. + + The user Identity is updated using authorization user_token + + params: + user: dict - user information for example: + + { + "name": "example", + "id": "886b4266-77d1-4258-abae-2931fb4f16de" + "credentials": { + "identity": "example@main.com", + "secret": "12345678" + }, + "metadata": { + "foo": "bar" + } + } + token: str - token used for creating a new user + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> user = { + ... "name": "example", + ... "id": "886b4266-77d1-4258-abae-2931fb4f16de" + ... "credentials": { + ... "identity": "example@main.com", + ... "secret": "12345678" + ... }, + ... "metadata": { + ... "foo": "bar" + ... } + ... } + >>> mf_resp = mfsdk.users.update_user_identity(user, user_token) + >>> mf_resp + """ + http_resp = requests.patch( + self.URL + "/" + self.USERS_ENDPOINT + "/" + user["id"] + "/identity", + headers=utils.construct_header(user_token, utils.CTJSON), + data= json.dumps(user), + ) + mf_resp = response.Response() + if http_resp.status_code != 200: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.users["update_user_identity"], http_resp.status_code + ) + else: + mf_resp.value = http_resp.json() + return mf_resp + + def update_user_tags(self, user: dict, user_token: str): + """Updates tags on currently logged in user. + + Updates tags of the user with provided ID. Tags is updated using + authorization token and the new tags received in request. + + params: + user: dict - user information for example: + + { + "name": "example", + "id": "886b4266-77d1-4258-abae-2931fb4f16de" + "tags": [ + "back", + "end" + ] + "metadata": { + "foo": "bar" + } + } + token: str - token used for creating a new user + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> user = { + ... "name": "example", + ... "id": "886b4266-77d1-4258-abae-2931fb4f16de" + ... "tags": [ + ... "back", + ... "end" + ... ] + ... } + >>> mf_resp = mfsdk.users.update_user_tags(user, user_token) + >>> mf_resp + """ + http_resp = requests.patch( + self.URL + "/" + self.USERS_ENDPOINT + "/" + user["id"] + "/tags", + headers=utils.construct_header(user_token, utils.CTJSON), + data=json.dumps(user), + ) + mf_resp = response.Response() + if http_resp.status_code != 200: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.users["update_user_tags"], http_resp.status_code + ) + else: + mf_resp.value = http_resp.json() + return mf_resp + + def update_user_owner(self, user: dict, user_token: str): + """Updates owner on currently logged in user. + + Updates owner for the user with provided ID. Owner is updated using + authorization token and a new owner identifier received in request. + + params: + user: dict - user information for example: + + { + "name": "example", + "id": "886b4266-77d1-4258-abae-2931fb4f16de" + "owner": "c52d-3b0d-43b9-8c3e-275c087d875af" + } + token: str - token used for creating a new user + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> user = { + ... "name": "example", + ... "id": "886b4266-77d1-4258-abae-2931fb4f16de", + ... "owner": "c52d-3b0d-43b9-8c3e-275c087d875af" + ... } + >>> mf_resp = mfsdk.users.update_user_owner(user, user_token) + >>> mf_resp + """ + http_resp = requests.patch( + self.URL + "/" + self.USERS_ENDPOINT + "/" + user["id"] + "/owner", + headers=utils.construct_header(user_token, utils.CTJSON), + data=json.dumps(user), + ) + mf_resp = response.Response() + if http_resp.status_code != 200: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.users["update_user_owner"], http_resp.status_code + ) + else: + mf_resp.value = http_resp.json() return mf_resp - def update_password( - self, old_password: str, password: str, user_token: str - ): - """Changes user password""" - payload = {"old_password": old_password, "password": password} + def update_password(self, old_secret: str, new_secret: str, user_token: str): + """Changes user password. + + Updates secret of currently logged in user. Secret is updated using + authorization token and the new received info. + + params: + old_secret: str - the logged in user's current secret. + new_secret: str - the user's new secret. + token: str - token used for creating a new user + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + ... old_secret = 12345678 + ... new_secret = 87654321 + >>> mf_resp = mfsdk.users.update(old_secret, new_secret, user_token) + >>> mf_resp + """ + payload = {"old_secret": old_secret, "new_secret": new_secret} http_resp = requests.patch( - self.url + "/password", + self.URL + "/" + self.USERS_ENDPOINT + "/secret", headers=utils.construct_header(user_token, utils.CTJSON), json=payload, ) mf_resp = response.Response() - if http_resp.status_code != 201: + if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.users["update"], http_resp.status_code ) + else: + mf_resp.value = "OK" + return mf_resp + + def reset_password_request(self, email: str, url: str): + """User Password reset request. + + Generates a reset token and sends an email to the user + with link for resetting password. + + params: + referrer email: str - this is the host being sent by the browser. + The email must be valid preferably gmail and ensure that the email is + already linked to a user as their identity in the database. + The email is part of the header. + url: str - http://localhost/reset-request + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + ... email = admin@example.com + ... url = stp@gmail.com + >>> mf_resp = mfsdk.users.reset_password_request(email, url) + >>> mf_resp + """ + http_resp = requests.post( + self.URL + "/password/reset-request", + headers={"Referer": url}, + json={"email": email}, + ) + mf_resp = response.Response() + if http_resp.status_code != 201: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.users["reset_password_request"], http_resp.status_code + ) + else: + mf_resp.value = http_resp.json() + return mf_resp + + def reset_password(self, password: str, confirm_password: str, token: str): + """Changes user password with the reset_request token + + When user gets reset token, after he submitted email to + /password/reset-request, posting a new password along to this + endpoint will change password. + + params: + passwor: str - the user's new password. + confirm_password: str - a recurrence of the password to ensure it + is the same. + token: str - the reset token recieved from the reset_request email. + + returns: + mf_resp: "OK" + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + ... password = 234567 + ... confirm_password = 234567 + >>> mf_resp = mfsdk.users.reset_password(password, confirm_password, token) + >>> mf_resp + """ + payload = { + "password": password, + "confirm_password": confirm_password, + "token": token, + } + http_resp = requests.put( + self.URL + "/password/reset", + json=payload, + ) + mf_resp = response.Response() + if http_resp.status_code != 201: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.users["reset_password"], http_resp.status_code + ) + else: + mf_resp.value = "OK" return mf_resp - def enable(self, user_id: str, admin_token: str): - """Enables a disabled user account for a given user ID.""" + def enable(self, user_id: str, user_token: str): + """Enables a disabled user account for a given user ID. + + Takes in the disabled User's ID and a valid token and enables the user. + + params: + user_id: str - the user's given ID. + token: str - token used for enabling a user + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> user_id= "886b4266-77d1-4258-abae-2931fb4f16de" + >>> mf_resp = mfsdk.users.enable(user_id, user_token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.users_endpoint + "/" + user_id + "/enable", - headers=utils.construct_header(admin_token, utils.CTJSON), + self.URL + "/" + self.USERS_ENDPOINT + "/" + user_id + "/enable", + headers=utils.construct_header(user_token, utils.CTJSON), ) - if http_resp.status_code != 204: + if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.users["enable"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() return mf_resp - def disable(self, user_id: str, admin_token: str): - """Disables an enabled user account for a given user ID.""" + def disable(self, user_id: str, user_token: str): + """Disables an enabled user account for a given user ID. + + params: + user_id: str - the user's given ID. + token: str - token used for enabling a user + + returns: + mf_resp: response.Response - response object + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> user_id= "886b4266-77d1-4258-abae-2931fb4f16de" + >>> mf_resp = mfsdk.users.disable(user_id, user_token) + >>> mf_resp + """ mf_resp = response.Response() http_resp = requests.post( - self.url + "/" + self.users_endpoint + "/" + user_id + "/disable", - headers=utils.construct_header(admin_token, utils.CTJSON), + self.URL + "/" + self.USERS_ENDPOINT + "/" + user_id + "/disable", + headers=utils.construct_header(user_token, utils.CTJSON), ) - if http_resp.status_code != 204: + if http_resp.status_code != 200: mf_resp.error.status = 1 mf_resp.error.message = errors.handle_error( errors.users["disable"], http_resp.status_code ) + else: + mf_resp.value = http_resp.json() + return mf_resp + + def authorise_user(self, access_request: dict, token: str): + """Authorises user + + Creates policies for a user as a subject over a group which is the object. + It authorizes the User to perform some actions over the group. + + params: + access_request = { + "subject": "", + "object": "", + "action": "", + "entity_type": "" + } + token: strOnly admin can use this endpoint. Therefore, you need + an authentication token for the admin. Also, only policies + defined on the system are allowed to add. + + returns: + mf_resp: "True" + + Usage:: + + >>> from mainflux import sdk + >>> mfsdk = sdk.SDK(users_url="http://localhost:9002") + >>> access_request = { + ... "subject": "", + ... "object": "", + ... "action": "", + ... "entity_type": "" + ... } + >>> mf_resp = mfsdk.users.authorise_user(access_request, token) + >>> mf_resp + """ + mf_resp = response.Response() + http_resp = requests.post( + self.URL + "/authorize", + headers=utils.construct_header(token, utils.CTJSON), + json=access_request, + ) + if http_resp.status_code != 200: + mf_resp.error.status = 1 + mf_resp.error.message = errors.handle_error( + errors.users["authorise_user"], http_resp.status_code + ) + else: + mf_resp.value = "True" return mf_resp diff --git a/tests/test_bootstrap.py b/tests/test_bootstrap.py new file mode 100644 index 0000000..39428e6 --- /dev/null +++ b/tests/test_bootstrap.py @@ -0,0 +1,99 @@ +from mainflux import sdk + +import json +import requests_mock + +s= sdk.SDK() + +config = { + "external_id": "234567", + "external_key": "234567", + "thing_id": "828b93e3-52b3-43a4-9ce2-ed8a47127ddd", + "name": "demo5" +} + +token= "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTI3NzI5NDgsImlhdCI6MTY5MjcxODk0OCwiaWRlbnRpdHkiOiJqb2xseV9uYXBpZXJAZW1haWwuY29tIiwiaXNzIjoiY2xpZW50cy5hdXRoIiwic3ViIjoiZjhiMDIwMmEtOGE4ZS00M2NjLWExZjctOWIzOWZkYmFlYjY3IiwidHlwZSI6ImFjY2VzcyJ9.-aEhFrpXxiCUW-zjCqQvUDi2UTtu5y0nkJgzDtUV-YiN1ZyMYUwwc9z8NvJPAfHCscagP4ifidWJ-ovkZULg4g" +thing_id= "828b93e3-52b3-43a4-9ce2-ed8a47127ddd" +external_id= "234567" +external_key= "234567" +config_id="828b93e3-52b3-43a4-9ce2-ed8a47127ddd" + +url = "http://localhost" + +def test_add(requests_mock): + requests_mock.register_uri( "POST", url+ "/things/configs", headers={"location": "/configs/" + thing_id}, json=config, status_code=201) + r = s.bootstrap.add(config=config, token=token) + assert r.error.status == 0 + assert r.value == "Configuration added" + +def test_add_bad_token(requests_mock): + requests_mock.register_uri( "POST", url+ "/things/configs", headers={"location": "/configs/" + thing_id}, json=config, status_code=401) + r = s.bootstrap.add(config=config, token=token) + assert r.error.status == 1 + assert r.error.message =="Missing or invalid access token provided." + +def test_whitelist(requests_mock): + requests_mock.register_uri( "PUT", url+ "/things/state/" + config["thing_id"], json=config, status_code=201) + r = s.bootstrap.whitelist(config=config, token=token) + assert r.error.status == 0 + assert r.value == "Configuration Updated" + +def test_whitelist_bad_config(requests_mock): + requests_mock.register_uri( "PUT", url+ "/things/state/" + config["thing_id"], json=config, status_code=400) + r = s.bootstrap.whitelist(config=config, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed due to malformed config's ID." + +def test_whitelist_config_removed(requests_mock): + requests_mock.register_uri( "PUT", url+ "/things/state/" + config["thing_id"], json=config, status_code=204) + r = s.bootstrap.whitelist(config=config, token=token) + assert r.error.status == 1 + assert r.error.message == "Config removed." + +def test_view(requests_mock): + requests_mock.register_uri( "GET", url+ "/things/configs/" + thing_id, json=config, status_code=200) + r = s.bootstrap.view(thing_id=thing_id, token=token) + assert r.error.status == 0 + assert config == r.value + +def test_view_bad_config(requests_mock): + requests_mock.register_uri( "GET", url+ "/things/configs/" + thing_id, json=config, status_code=404) + r = s.bootstrap.view(thing_id=thing_id, token=token) + assert r.error.status == 1 + assert r.error.message == "Config does not exist." + +def test_update(requests_mock): + requests_mock.register_uri( "PUT", url+ "/things/configs/" + config["thing_id"], json=config, status_code=200) + r = s.bootstrap.update(config=config, token=token) + assert r.error.status == 0 + assert r.value == "Configuration updated." + +def test_update_bad_config(requests_mock): + requests_mock.register_uri( "PUT", url+ "/things/configs/" + config["thing_id"], json=config, status_code=404) + r = s.bootstrap.update(config=config, token=token) + assert r.error.status == 1 + assert r.error.message == "Config does not exist." + +def test_bootstrap(requests_mock): + requests_mock.register_uri( "GET", url+ "/things/bootstrap/" + external_id, json=config, status_code=200) + r = s.bootstrap.bootstrap(external_id=external_id, external_key=external_key) + assert r.error.status == 0 + assert config == r.value + +def test_bootstrap_bad_config(requests_mock): + requests_mock.register_uri( "GET", url+ "/things/bootstrap/" + external_id, json=config, status_code=404) + r = s.bootstrap.bootstrap(external_id=external_id, external_key=external_key) + assert r.error.status == 1 + assert r.error.message == "Failed to retrieve corresponding config." + +def test_remove(requests_mock): + requests_mock.register_uri( "DELETE", url+ "/things/configs/" + config_id, status_code=204) + r = s.bootstrap.remove(config_id=config_id, token=token) + assert r.error.status == 0 + assert r.value== "Configuration removed." + +def test_remove_bad_config(requests_mock): + requests_mock.register_uri( "DELETE", url+ "/things/configs/" + config_id, status_code=400) + r = s.bootstrap.remove(config_id=config_id, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed due to malformed config ID." diff --git a/tests/test_certs.py b/tests/test_certs.py new file mode 100644 index 0000000..6690d67 --- /dev/null +++ b/tests/test_certs.py @@ -0,0 +1,66 @@ +from mainflux import sdk + +import json +import requests_mock + +s= sdk.SDK() + +certs= { + "cert_serial": "22:16:df:60:c2:99:bc:c4:9b:1d:fd:71:5e:e9:07:d9:1b:3c:85:1d", + "client_cert": "-----BEGIN CERTIFICATE-----\nMIIEATCCAumgAwIBAgIUIhbfYMKZvMSbHf1xXukH2Rs8hR0wDQYJKoZIhvcNAQEL\nBQAwLjEsMCoGA1UEAxMjbWFpbmZsdXguY29tIEludGVybWVkaWF0ZSBBdXRob3Jp\ndHkwHhcNMjMwODIxMTAwMjE5WhcNMjMwOTIwMTAwMjQ4WjAvMS0wKwYDVQQDEyRj\nODkyMzViNy0xZDU0LTQyY2YtODdlYi0yNDgzMWY3NDE0ZTEwggEiMA0GCSqGSIb3\nDQEBAQUAA4IBDwAwggEKAoIBAQDL2AXzhrmzqOVfqGQA9evsqM0n5BjL3F3FCo2q\n1vMVq8HrCmJ1qXk1h6coz5KILiloLET5PFALFqwHByBzNAg0IZUbTTTh8YuUtiq+\nbnRa2MOZZzQtXB1T6tPkAtcxdJ+H3j0eEIAbYh4DJiq8l1NmU0siCaFLHJ+pwJr5\ndGwGgWOW3sotrUvN9vVsXM41SgxJOeps765/lKQjSxTdhEERLsu6pQ+68K1WNYKg\npCO9kB2Dmu3jtoV34kMyhg1ptaegztEFrSgA8bSIsR4X19itIR5ku4jZb6pnmPcM\nBwHJlJXTl9zdzSC4MXOdb8aOXEROTsVuFGn5fnZ9OWDWXeEhAgMBAAGjggEUMIIB\nEDAOBgNVHQ8BAf8EBAMCA6gwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC\nMB0GA1UdDgQWBBSmppsLhLCf6N44sY6Np7/k+z//tzAfBgNVHSMEGDAWgBRXKFnr\nLL2mS5RUzzlVZHJup20WfTA7BggrBgEFBQcBAQQvMC0wKwYIKwYBBQUHMAKGH2h0\ndHA6Ly92YXVsdDo4MjAwL3YxL3BraV9pbnQvY2EwLwYDVR0RBCgwJoIkYzg5MjM1\nYjctMWQ1NC00MmNmLTg3ZWItMjQ4MzFmNzQxNGUxMDEGA1UdHwQqMCgwJqAkoCKG\nIGh0dHA6Ly92YXVsdDo4MjAwL3YxL3BraV9pbnQvY3JsMA0GCSqGSIb3DQEBCwUA\nA4IBAQCZla9Opcq6RQqnCoih9VCg3JbU0PmpfeM+2LtWziH51PyeIOxB5b244SPe\nUtLzSv4lAB6tsq4aYx67n7suiv1E50E9xOXtVq1s/yO/bhvR2NIQL3bFc61ZGW3C\nVetPdSfESwXqi5jdWcacs+F66ZFEza3fFYLgl7D8Bdd3i9hKlgK7pWDrhcDpgwMA\nabIRbQ4nuCyf8OVeklCUfbilRrvESrWIy9r0PjViZbJDFIFEs9uEvBUFcDokzJqY\nzVH1+pduYw7x5JzJy/cK8TGX3JdKK7Y058jnSrO3ZkW4uKQ3bCWD5kB0wNanrp+a\nfD1NTEQ0etWwdlylVx4JjtwA4T1k\n-----END CERTIFICATE-----", + "client_key": "-----BEGIN RSA PRIVATE KEY-----\nMIIEoQIBAAKCAQEAy9gF84a5s6jlX6hkAPXr7KjNJ+QYy9xdxQqNqtbzFavB6wpi\ndal5NYenKM+SiC4paCxE+TxQCxasBwcgczQINCGVG0004fGLlLYqvm50WtjDmWc0\nLVwdU+rT5ALXMXSfh949HhCAG2IeAyYqvJdTZlNLIgmhSxyfqcCa+XRsBoFjlt7K\nLa1Lzfb1bFzONUoMSTnqbO+uf5SkI0sU3YRBES7LuqUPuvCtVjWCoKQjvZAdg5rt\n47aFd+JDMoYNabWnoM7RBa0oAPG0iLEeF9fYrSEeZLuI2W+qZ5j3DAcByZSV05fc\n3c0guDFznW/GjlxETk7FbhRp+X52fTlg1l3hIQIDAQABAoIBACOiqz+sgNBgqWC0\nrm7gjxL7W4oqvQ7+gkING0EPfMWAFlGBqj7Jls/93AItb39xGnoEqzYrDg8yMna0\nDz80jG6YpFl2gNUzBeTEh+psotiy5lbuDNgVL2dZORu2R2p06eK1vleAKPUgjQCd\n7oCzr7fGve7AYjsgUOU7L5yGdtAYBLL4nu6XfMC9kWxHKwVMhWSRSZO4gr83Yhld\nsHrlLlSMYciuXWSZSMGvFTQc3jj8tyq9TqMTysqgbM++6E4KyzATwW2DsoiiMAPl\n/2wyG1GxsNHreeSxxAFQw4NhxY5ud34jYi6NNpR2u2kaa17eFD/YVyMTn+9gyugc\nuFt0tjECgYEA5CYve2BhowU6kSUx0055Kf1mD24MpGo29tKzR98m9Rc045iUMwdc\n51skrTjdVcskDYDVZ1cklO1dCerQBLJ3zc6iqyVdoABj0pMlfWvRyYj+8oRa1AgJ\n1K0FMkcT9UJ3jTxidoF3/8/sRZEvpuxIb6wSfNEoJVIxqFsz71khWrUCgYEA5LpH\nuKSHqzE2E2UHd9dXbTWf1ZxHbCMJrWMwMXDo1SenlR9E/W/WD7IXrGvdL6PWky9L\nHUxdbuOxMBIGEhRIFFgKBElZVuG5pBAt/1GPx1Skm0tg6V5DvndqQavfYyFVhZ3T\nR0I5tYFBaA7BwVsI1aQTryofZFZ8gRGyIgGUtD0CgYBufn/ohNlElreyrAzhhdPw\nniTbvDSrPDW6fHkPiefYM5EN2UuNGzfHZMDyk+O+NVAUqhywm+e/qOWyc+KjI7wa\nFMV7lfEuGII/7bvublWAAbVXxvomTm5UbidiHkJwOeyknmYhdrqjThPj7VjiwvSi\nAPhDMxj6WkBqhSE1/jjFMQJ/JmsjoOAB6b9aVeeiWX7SMIXRUw/s7zzzYyxF7AgL\nE8KVY3bdH7SpP/mqAEwd2uKqKA7JjyJEj1uvZ2OfoWnGsaQYCqBHYVCI3gXZtAj/\nHXwaKft/S7OJrXRhZKZ53yy6MLdRxaZaCyKq2c+gu9mOolPs+n8YxsHAJ+3Q/eVG\nFQKBgQChCjj0dZAMQd4q/UBfk8cMIZCbDnd1qm/9rwcW4hObvpB0WG8AZ7uE8W1K\nDS+SHu+4n3aA9gY4CSwOdLRz/mMng9cs3wjWiLUsHPAkneANYMNsuikJMM+IVdiv\nZVih7y8Q6BNZOQfpIqR8BNjft8MinJTN//ephL6REOaPzlHfhw==\n-----END RSA PRIVATE KEY-----", + "expiration": "2023-09-20T10:02:48Z", + "thing_id": "3d49a42f-63fd-491b-9784-adf4b64ef347" +} +token = "eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTI2NzM1NTksImlhdCI6MTY5MjYxOTU1OSwiaWRlbnRpdHkiOiJpbnNwaXJpbmdfZ29sZGJlcmdAZW1haWwuY29tIiwiaXNzIjoiY2xpZW50cy5hdXRoIiwic3ViIjoiYzk2ZWQwNmQtNTM5Zi00YjAxLTk2MWUtNTA0MjJmZjQ0OTBkIiwidHlwZSI6ImFjY2VzcyJ9.6J34t9Ts0VmjfsdxeUd66DQihFuB-mwlJ884KwoCjT1FmNr70yPjtIXpOvRjA58ggs-5kTh_ZZh25KtkTipWEg" +thing_id= "2544670a-a992-4865-8222-24e5deed7bae" +cert_id= "22:16:df:60:c2:99:bc:c4:9b:1d:fd:71:5e:e9:07:d9:1b:3c:85:1d" + +url= "http://localhost" + +def test_issue(requests_mock): + requests_mock.register_uri( "POST", url + "/certs", headers={"location": "/certs/" + thing_id}, json=certs, status_code=201) + r = s.certs.issue(thing_id=thing_id, valid= "10h", token=token) + assert r.error.status == 0 + assert certs == r.value + +def test_issue_bad_token(requests_mock): + requests_mock.register_uri( "POST", url + "/certs", headers={"location": "/certs/" + thing_id}, json=certs, status_code=401) + r = s.certs.issue(thing_id=thing_id, valid= "10h", token=token) + assert r.error.status == 1 + assert r.error.message == "Missing or invalid access token provided." + +def test_view_by_thing(requests_mock): + requests_mock.register_uri( "GET", url + "/serials" + "/" + thing_id, json=certs, status_code=200) + r = s.certs.view_by_thing(thing_id=thing_id, token=token) + assert r.error.status == 0 + assert certs == r.value + +def test_view_by_thing_bad_token(requests_mock): + requests_mock.register_uri( "GET", url + "/serials" + "/" + thing_id, json=certs, status_code=404) + r = s.certs.view_by_thing(thing_id=thing_id, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed to retrieve corresponding certificate." + +def test_view_by_serial(requests_mock): + requests_mock.register_uri( "GET", url + "/certs" + "/" + cert_id, json=certs, status_code=200) + r = s.certs.view_by_serial(cert_id=cert_id, token=token) + assert r.error.status == 0 + assert certs == r.value + +def test_view_by_serial_bad_token(requests_mock): + requests_mock.register_uri( "GET", url + "/certs" + "/" + cert_id, json=certs, status_code=404) + r = s.certs.view_by_serial(cert_id=cert_id, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed to retrieve corresponding certificate." + +def test_revoke(requests_mock): + requests_mock.register_uri( "DELETE", url + "/certs" + "/" + thing_id, status_code=200) + r = s.certs.revoke(thing_id=thing_id, token=token) + assert r.error.status == 0 + +def test_revoke_bad_response(requests_mock): + requests_mock.register_uri( "DELETE", url + "/certs" + "/" + thing_id, status_code=404) + r = s.certs.revoke(thing_id=thing_id, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed to revoke corresponding certificate." diff --git a/tests/test_channels.py b/tests/test_channels.py index 7ed9cf7..a7da7fb 100644 --- a/tests/test_channels.py +++ b/tests/test_channels.py @@ -5,7 +5,21 @@ s = sdk.SDK() -channel = {"channel_name": "channel"} +channel ={ + "id": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "name": "channelName", + "owner_id": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "parent_id": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "description": "long channel description", + "metadata": { + "role": "general" + }, + "path": "bb7edb32-2eac-4aad-aebe-ed96fe073879.bb7edb32-2eac-4aad-aebe-ed96fe073879", + "level": 2, + "created_at": "2019-11-26 13:31:52", + "updated_at": "2019-11-26 13:31:52", + "status": "enabled" +} channel_id = "123-456-789" channel_id1 = "123-223-333" thing_id = "332-665-998" @@ -16,120 +30,99 @@ def test_create_channel(requests_mock): - requests_mock.register_uri( - "POST", url + "/channels", headers={"location": "/channels/" + channel_id}, status_code=201) + requests_mock.register_uri( "POST", url + "/channels", headers={"location": "/channels/" + channel_id}, json=channel, status_code=201) r = s.channels.create(channel=channel, token=token) assert r.error.status == 0 - assert channel_id == r.value - + assert channel == r.value def test_create_channel_entity_exist(requests_mock): - requests_mock.register_uri( - "POST", url + "/channels", headers={"location": "/channels/" + channel_id}, status_code=409) + requests_mock.register_uri("POST", url + "/channels", headers={"location": "/channels/" + channel_id}, status_code=409) r = s.channels.create(channel=channel, token=token) assert r.error.status == 1 - assert r.error.message == "Entity already exist." - + assert r.error.message == "Failed due to using an existing identity." def test_create_bulk_channels(requests_mock): - requests_mock.register_uri("POST", url + "/channels/bulk", json=[ - channel_id, channel_id1], headers={"location": "/channels/channel_ids"}, status_code=201) + requests_mock.register_uri("POST", url + "/channels/bulk", json=[channel_id, channel_id1], headers={"location": "/channels/channel_ids"}, status_code=200) r = s.channels.create_bulk(channel_id, token=token) assert r.error.status == 0 assert [channel_id, channel_id1] == r.value - def test_create_bulk_channels_server_error(requests_mock): - requests_mock.register_uri("POST", url + "/channels/bulk", json=[ - channel_id, channel_id1], headers={"location": "/channels/channel_ids"}, status_code=500) + requests_mock.register_uri("POST", url + "/channels/bulk", json=[channel_id, channel_id1], headers={"location": "/channels/channel_ids"}, status_code=401) r = s.channels.create_bulk(channel_id, token=token) assert r.error.status == 1 - assert r.error.message == "Unexpected server-side error occurred." - + assert r.error.message == "Missing or invalid access token provided." def test_get_channel(requests_mock): - requests_mock.register_uri( - "GET", url + "/channels/" + channel_id, json=channel, status_code=200) + requests_mock.register_uri("GET", url + "/channels/" + channel_id, json=channel, status_code=200) r = s.channels.get(channel_id=channel_id, token=token) assert r.error.status == 0 assert channel == r.value - def test_get_channel_bad_token(requests_mock): - requests_mock.register_uri( - "GET", url + "/channels/" + channel_id, json=channel, status_code=401) + requests_mock.register_uri("GET", url + "/channels/" + channel_id, json=channel, status_code=401) r = s.channels.get(channel_id=channel_id, token=token) assert r.error.status == 1 assert r.error.message == "Missing or invalid access token provided." - def test_get_all_channels(requests_mock): - requests_mock.register_uri( - "GET", url + "/channels", json=[channel_id, channel_id1], status_code=200) + requests_mock.register_uri("GET", url + "/channels", json=[channel_id, channel_id1], status_code=200) r = s.channels.get_all(token=token, query_params=params) assert r.error.status == 0 assert [channel_id, channel_id1] == r.value - def test_get_all_channels_channel_does_not_exist(requests_mock): - requests_mock.register_uri( - "GET", url + "/channels", json=[channel_id, channel_id1], status_code=404) + requests_mock.register_uri("GET", url + "/channels", json=[channel_id, channel_id1], status_code=404) r = s.channels.get_all(token=token, query_params=params) assert r.error.status == 1 assert r.error.message == "Channel does not exist." - def test_get_by_thing(requests_mock): - requests_mock.register_uri("GET", url + "/things/" + thing_id + "/channels", json=channel_id, - headers={"Authorization": "/channels/" + channel_id + "/things"}, status_code=200) + requests_mock.register_uri("GET", url + "/things/" + thing_id + "/channels", json=channel_id, headers={"Authorization": "/channels/" + channel_id + "/things"}, status_code=200) r = s.channels.get_by_thing( thing_id=thing_id, query_params=params, token=token) assert r.error.status == 0 assert channel_id == r.value - def test_get_by_thing_does_not_exist(requests_mock): - requests_mock.register_uri("GET", url + "/things/" + thing_id + "/channels", json=channel_id, - headers={"Authorization": "/channels/" + channel_id + "/things"}, status_code=404) + requests_mock.register_uri("GET", url + "/things/" + thing_id + "/channels", json=channel_id, headers={"Authorization": "/channels/" + channel_id + "/things"}, status_code=404) r = s.channels.get_by_thing( thing_id=thing_id, query_params=params, token=token) assert r.error.status == 1 assert r.error.message == "Thing does not exist." - def test_update_channel(requests_mock): - requests_mock.register_uri( - "PUT", url + "/channels/" + channel_id, json=json.dumps(channel), status_code=200) + requests_mock.register_uri("PUT", url + "/channels/" + channel_id, json=channel, status_code=200) r = s.channels.update(channel_id=channel_id, token=token, channel=channel) assert r.error.status == 0 - def test_update_channel_does_not_exist(requests_mock): - requests_mock.register_uri( - "PUT", url + "/channels/" + channel_id, json=json.dumps(channel), status_code=404) + requests_mock.register_uri("PUT", url + "/channels/" + channel_id, json=channel, status_code=404) r = s.channels.update(channel_id=channel_id, token=token, channel=channel) assert r.error.status == 1 assert r.error.message == "Channel does not exist." - def test_delete_channel(requests_mock): - requests_mock.register_uri( - "DELETE", url + "/channels/" + channel_id, status_code=204) - r = s.channels.delete(channel_id=channel_id, token=token) + requests_mock.register_uri("POST", url + "/channels/" + channel_id + "/disable", status_code=200) + r = s.channels.disable(channel_id=channel_id, token=token) assert r.error.status == 0 - def test_delete_channel_malformed_channel_id(requests_mock): - requests_mock.register_uri( - "DELETE", url + "/channels/" + channel_id, status_code=400) - r = s.channels.delete(channel_id=channel_id, token=token) + requests_mock.register_uri("POST", url + "/channels/" + channel_id + "/disable", status_code=400) + r = s.channels.disable(channel_id=channel_id, token=token) assert r.error.status == 1 assert r.error.message == "Failed due to malformed channel's ID." - def test_identify_thing(requests_mock): requests_mock.register_uri( "POST", url + "/identify", json=thing_key, status_code=200) r = s.channels.identify_thing(thing_key) assert r.error.status == 0 assert thing_key == r.value + +def test_identify_thing_bad_key(requests_mock): + requests_mock.register_uri( + "POST", url + "/identify", json=thing_key, status_code=401) + r = s.channels.identify_thing(thing_key) + assert r.error.status == 1 + assert r.error.message == "Thing and channel are not connected, or thing with specified key doesn't exist." diff --git a/tests/test_groups.py b/tests/test_groups.py index 68f1bb1..341e1d4 100644 --- a/tests/test_groups.py +++ b/tests/test_groups.py @@ -1,25 +1,43 @@ from mainflux import sdk -import json, requests_mock +import requests_mock s = sdk.SDK() channel_id = "654-559-774" members = {"members": channel_id, "type": "channels"} -group = {"group_name": "group"} +members_ids ="4e5532c0-cf92-4ef3-ab7b-65ee30151c99" +member_id= "4e5532c0-cf92-4ef3-ab7b-65ee30151c99" +group ={ + "id": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "name": "groupName", + "owner_id": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "parent_id": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "description": "long group description", + "metadata": { + "role": "general" + }, + "path": "bb7edb32-2eac-4aad-aebe-ed96fe073879.bb7edb32-2eac-4aad-aebe-ed96fe073879", + "level": 2, + "created_at": "2019-11-26 13:31:52", + "updated_at": "2019-11-26 13:31:52", + "status": "enabled" +} +query_params= { + "offset": 0, + "limit": 5 +} group_id = "888-888-888" group_id1 = "989-787-686" thing_group_id = "868-464-262" token = "9a8b7c6d5e4f3g21" url = "http://localhost" - def test_create_group(requests_mock): - requests_mock.register_uri("POST", url + "/groups", headers={"location": "/groups/" + group_id}, status_code=201) + requests_mock.register_uri("POST", url + "/groups", headers={"location": "/groups/" + group_id}, json=group, status_code=201) r = s.groups.create(group=group, token=token) assert r.error.status == 0 - assert group_id == r.value - + assert group == r.value def test_create_group_existing_email_address(requests_mock): requests_mock.register_uri("POST", url + "/groups", headers={"location": "/groups/" + group_id}, status_code=409) @@ -27,20 +45,41 @@ def test_create_group_existing_email_address(requests_mock): assert r.error.status == 1 assert r.error.message == "Failed due to using an existing email address." - def test_get_group(requests_mock): requests_mock.register_uri("GET", url + "/groups/" + group_id, json=group, status_code=200) r = s.groups.get(group_id=group_id, token=token) assert r.error.status == 0 assert group == r.value - def test_get_group_does_not_exist(requests_mock): requests_mock.register_uri("GET", url + "/groups/" + group_id, json=group, status_code=404) r = s.groups.get(group_id=group_id, token=token) assert r.error.status == 1 assert r.error.message == "Group does not exist." + +def test_get_parents(requests_mock): + requests_mock.register_uri("GET", url + "/groups/" + group_id + "/parents", json=group, status_code=200) + r = s.groups.parents(group_id=group_id, query_params= query_params, token=token) + assert r.error.status == 0 + assert group == r.value +def test_get_parents_bad_json(requests_mock): + requests_mock.register_uri("GET", url + "/groups/" + group_id + "/parents", status_code=400) + r = s.groups.parents(group_id=group_id, query_params= query_params, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed due to malformed query parameters." + +def test_get_children(requests_mock): + requests_mock.register_uri("GET", url + "/groups/" + group_id + "/children", json=group, status_code=200) + r = s.groups.children(group_id=group_id, query_params= query_params, token=token) + assert r.error.status == 0 + assert group == r.value + +def test_get_children_bad_json(requests_mock): + requests_mock.register_uri("GET", url + "/groups/" + group_id + "/children", status_code=400) + r = s.groups.children(group_id=group_id, query_params= query_params, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed due to malformed query parameters." def test_get_all_groups(requests_mock): requests_mock.register_uri("GET", url + "/groups", json=group_id, status_code=200) @@ -48,80 +87,75 @@ def test_get_all_groups(requests_mock): assert r.error.status == 0 assert group_id == r.value - def test_get_all_groups_malformed_query(requests_mock): requests_mock.register_uri("GET", url + "/groups", json=group_id, status_code=400) r = s.groups.get_all(query_params=None, token=token) assert r.error.status == 1 assert r.error.message == "Failed due to malformed query parameters." - def test_update_group(requests_mock): - requests_mock.register_uri("PUT", url + "/groups/" + group_id, json=json.dumps(group), status_code=200) - r = s.groups.update(group_id=group_id, token=token, group=group) + requests_mock.register_uri("PUT", url + "/groups/" + group_id, json=group, status_code=200) + r = s.groups.update(group_id=group_id, group=group, token=token) assert r.error.status == 0 - -def test_update_group_sverver_error(requests_mock): - requests_mock.register_uri("PUT", url + "/groups/" + group_id, json=json.dumps(group), status_code=500) - r = s.groups.update(group_id=group_id, token=token, group=group) +def test_update_group_server_error(requests_mock): + requests_mock.register_uri("PUT", url + "/groups/" + group_id, json=group, status_code=500) + r = s.groups.update(group_id=group_id, group=group, token=token) assert r.error.status == 1 assert r.error.message == "Unexpected server-side error occurred." - def test_members(requests_mock): - requests_mock.register_uri("GET", url + "/groups/" + group_id + "/members", status_code=200) + requests_mock.register_uri("GET", url + "/groups/" + group_id + "/members",json=group_id, status_code=200) r = s.groups.members(group_id=group_id, query_params=None, token=token) assert r.error.status == 0 - def test_members_bad_content_type(requests_mock): requests_mock.register_uri("GET", url + "/groups/" + group_id + "/members", status_code=415) r = s.groups.members(group_id=group_id, query_params=None, token=token) assert r.error.status == 1 assert r.error.message == "Missing or invalid content type." +def test_membership(requests_mock): + requests_mock.register_uri("GET", url + "/users/" + member_id + "/memberships", json=members_ids, status_code=200) + r = s.groups.memberships(member_id=member_id, query_params=query_params, token=token) + assert r.error.status == 0 + +def test_membership_bad_content_type(requests_mock): + requests_mock.register_uri("GET", url + "/users/" + member_id + "/memberships", status_code=400) + r = s.groups.memberships(member_id=member_id, query_params=query_params, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed due to malformed query parameters." def test_assign(requests_mock): - requests_mock.register_uri("POST", url + "/groups/" + group_id + "/members", status_code=200) - r = s.groups.assign(group_id=group_id, token=token, members_ids=members, member_type="things") + requests_mock.register_uri("POST", url + "/users/policies" , status_code=201) + r = s.groups.assign(group_id=group_id, token=token, member_id=members, member_type= ["m_read"]) assert r.error.status == 0 - + assert r.value == "Policy created" def test_assign_malformed_json(requests_mock): - requests_mock.register_uri("POST", url + "/groups/" + group_id + "/members", status_code=400) - r = s.groups.assign(group_id=group_id, token=token, members_ids=members, member_type="things") + requests_mock.register_uri("POST", url + "/users/policies", status_code=400) + r = s.groups.assign(group_id=group_id, token=token, member_id=members, member_type= ["m_read"]) assert r.error.status == 1 assert r.error.message == "Failed due to malformed JSON." - def test_unassign(requests_mock): - requests_mock.register_uri("DELETE", url + "/groups/" + group_id + "/members", status_code=204) - r = s.groups.unassign(group_id=group_id, token=token, members_ids=members) + requests_mock.register_uri("DELETE", url + "/users/policies" + "/" + members_ids + "/" + group_id, json=group, status_code=204) + r = s.groups.unassign(group_id=group_id, members_ids=members_ids, token=token) assert r.error.status == 0 - - + def test_unassign_bad_token(requests_mock): - requests_mock.register_uri("DELETE", url + "/groups/" + group_id + "/members", status_code=403) - r = s.groups.unassign(group_id=group_id, token=token, members_ids=members) + requests_mock.register_uri("DELETE", url + "/users/policies" + "/" + members_ids + "/" + group_id, status_code=403) + r = s.groups.unassign(group_id=group_id, token=token, members_ids=members_ids) assert r.error.status == 1 assert r.error.message == "Missing or invalid access token provided." - def test_delete_group(requests_mock): - requests_mock.register_uri("DELETE", url + "/groups/" + group_id, status_code=204) - r = s.groups.delete(group_id=group_id, token=token) + requests_mock.register_uri("POST", url + "/groups/" + group_id + "/disable", status_code=200) + r = s.groups.disable(group_id=group_id, user_token=token) assert r.error.status == 0 - def test_delete_group_does_not_exist(requests_mock): - requests_mock.register_uri("DELETE", url + "/groups/" + group_id, status_code=404) - r = s.groups.delete(group_id=group_id, token=token) + requests_mock.register_uri("POST", url + "/groups/" + group_id + "/disable", status_code=404) + r = s.groups.disable(group_id=group_id, user_token=token) assert r.error.status == 1 assert r.error.message == "Group does not exist." - -def test_share_groups(requests_mock): - requests_mock.register_uri("POST", url + "/groups/" + group_id + "/share", status_code=200, - headers={"Authorization": token}, json={"thing_group_id": thing_group_id}) - r = s.groups.share_groups(token=token, user_group_id=group_id, thing_group_id=thing_group_id) - assert r.error.status == 0 diff --git a/tests/test_keys.py b/tests/test_keys.py deleted file mode 100644 index 8a1910c..0000000 --- a/tests/test_keys.py +++ /dev/null @@ -1,47 +0,0 @@ -from mainflux import sdk - -import json, requests_mock - -s = sdk.SDK() - -token = "9a8b7c6d5e4f3g21" -duration_params = {"type":2, "duration":10000} -key_id = "123-456-789" -url = "http://localhost" -params = None - - -def test_issue(requests_mock): - requests_mock.register_uri("POST", url + "/keys", json=token, status_code=201) - r = s.keys.issue(token=token, duration=duration_params) - assert r.error.status == 0 - assert token == r.value - -def test_create_existing_key(requests_mock): - requests_mock.register_uri("POST", url + "/keys", status_code=409) - r = s.keys.issue(token=token, duration=duration_params) - assert r.error.status == 1 - assert r.error.message == "Entity already exist." - -def test_get_key_details(requests_mock): - requests_mock.register_uri("GET", url + "/keys/" + key_id, json=key_id, status_code=200) - r = s.keys.get_key_details(token=token, key_id=key_id) - assert r.error.status == 0 - assert key_id == r.value - -def test_get_key_details_missing_token(requests_mock): - requests_mock.register_uri("GET", url + "/keys/" + key_id, json=key_id, status_code=403) - r = s.keys.get_key_details(token=token, key_id=key_id) - assert r.error.status == 1 - assert r.error.message == "Missing or invalid access token provided." - -def test_revoke(requests_mock): - requests_mock.register_uri("DELETE", url + "/keys/" + key_id, status_code=204) - r = s.keys.revoke(token=token, key_id=key_id) - assert r.error.status == 0 - -def test_revoke_bad_key(requests_mock): - requests_mock.register_uri("DELETE", url + "/keys/" + key_id, status_code=403) - r = s.keys.revoke(token=token, key_id=key_id) - assert r.error.status == 1 - assert r.error.message == "Missing or invalid access token provided." diff --git a/tests/test_messages.py b/tests/test_messages.py index a3c4b0a..d284216 100644 --- a/tests/test_messages.py +++ b/tests/test_messages.py @@ -19,3 +19,15 @@ def test_read(requests_mock): r = s.messages.read(channel_id=channel_id, token=token) assert r.error.status == 0 assert msg == r.value + +def test_send_malformed_channel_id(requests_mock): + requests_mock.register_uri("POST", url + "/http/channels/" + channel_id + "/messages/", status_code=400) + r = s.messages.send(channel_id=channel_id, msg=msg, thing_key=token) + assert r.error.status == 1 + assert r.error.message == "Message discarded due to its malformed content." + +def test_read_malformed_query_params(requests_mock): + requests_mock.register_uri("GET", url + "/channels/" + channel_id + "/messages", json=msg, status_code=400) + r = s.messages.read(channel_id=channel_id, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed due to malformed query parameters." diff --git a/tests/test_things.py b/tests/test_things.py index 88d1fa6..795afc6 100644 --- a/tests/test_things.py +++ b/tests/test_things.py @@ -1,176 +1,275 @@ from mainflux import sdk -import json import requests_mock s = sdk.SDK() -thing = {"name": "thing"} -things = [{"name": "thing1"}, {"name": "thing2"}] +thing = { + "id": "35ad0272-94bb-4701-9785-ff32334327a0", + "name": "thing", + "tags": [ + "tag1", + "tag2" + ], + "owner": "edc876eb-27e2-4bc9-8599-4faf21d2a12f", + "credentials": { + "identity": "thingidentity", + "secret": "f002d93b-fa40-435e-b9d9-37f991e47e9f" + }, + "metadata": { + "domain": "example.com" + }, + "status": "enabled" +} +things = [{ + "id": "4e5532c0-cf92-4ef3-ab7b-65ee30151c99", + "name": "thing1", + "tags": [ + "tag1", + "tag2" + ], + "owner": "edc876eb-27e2-4bc9-8599-4faf21d2a12f", + "credentials": { + "identity": "thingidentity", + "secret": "47749f5e-1e32-4834-9a1d-2d38871d4e1e" + }, + "metadata": { + "domain": "example.com" + }, + "status": "enabled" +}, { + "id": "53ed347d-f277-4e2b-9ee1-442389ad1a9a", + "name": "thing2", + "tags": [ + "tag1", + "tag2" + ], + "owner": "edc876eb-27e2-4bc9-8599-4faf21d2a12f", + "credentials": { + "identity": "thingidentity", + "secret": "566eff24-0b55-4033-9ab1-b4df0d9a3266" + }, + "metadata": { + "domain": "example.com" + }, + "status": "enabled" +}] thing_id = "123-456-789" thing_id1 = "123-223-333" channel_id = "654-654-654" channel_id1 = "654-654-654" -token = "9a8b7c6d5e4f3g21" +user_id= "123-679-773" +action= "m_read" +token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" url = "http://localhost" params = None - +access_request= { + "subject": "123-456-789", + "object": "654-654-654", + "action": "m_read", + "entity_type": "group" +} +policies= { + "policies": [ + { + "owner_id": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "subject": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "object": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "actions": [ + "m_write", + "g_add" + ], + "created_at": "2019-11-26 13:31:52", + "updated_at": "2019-11-26 13:31:52" + } + ], + "total": 1, + "offset": 0, + "limit": 10 +} def test_create_thing(requests_mock): - requests_mock.register_uri( - "POST", url + "/things", headers={"location": "/things/" + thing_id}, status_code=201) + requests_mock.register_uri("POST", url + "/things", headers={"location": "/things/" + thing["id"]}, json=thing, status_code=201) r = s.things.create(thing=thing, token=token) assert r.error.status == 0 - assert thing_id == r.value - + assert thing == r.value def test_create_existing_thing(requests_mock): - requests_mock.register_uri( - "POST", url + "/things", headers={"location": "/things/" + thing_id}, status_code=409) + requests_mock.register_uri("POST", url + "/things", headers={"location": "/things/" + thing_id}, status_code=409) r = s.things.create(thing=thing, token=token) assert r.error.status == 1 assert r.error.message == "Entity already exist." - def test_create_bulk_things(requests_mock): - requests_mock.register_uri("POST", url + "/things/bulk", json=[ - thing_id, thing_id1], headers={"location": "/things/" + thing_id}, status_code=201) + requests_mock.register_uri("POST", url + "/things/bulk", json=things, status_code=200) r = s.things.create_bulk(things=things, token=token) assert r.error.status == 0 - assert [thing_id, thing_id1] == r.value - + assert things == r.value def test_create_bulk_things_missing_token(requests_mock): - requests_mock.register_uri("POST", url + "/things/bulk", json=[ - thing_id, thing_id1], headers={"location": "/things/" + thing_id}, status_code=401) + requests_mock.register_uri("POST", url + "/things/bulk", json=[thing_id, thing_id1], headers={"location": "/things/" + thing_id}, status_code=401) r = s.things.create_bulk(things=things, token=token) assert r.error.status == 1 assert r.error.message == "Missing or invalid access token provided." - def test_get_thing(requests_mock): - requests_mock.register_uri( - "GET", url + "/things/" + thing_id, json=thing, status_code=200) + requests_mock.register_uri("GET", url + "/things/" + thing_id, json=thing, status_code=200) r = s.things.get(thing_id=thing_id, token=token) assert r.error.status == 0 assert thing == r.value - def test_get_thing_malformed_query(requests_mock): - requests_mock.register_uri( - "GET", url + "/things/" + thing_id, json=thing, status_code=400) + requests_mock.register_uri("GET", url + "/things/" + thing_id, json=thing, status_code=400) r = s.things.get(thing_id=thing_id, token=token) assert r.error.status == 1 assert r.error.message == "Failed due to malformed query parameters." - def test_get_all_things(requests_mock): - requests_mock.register_uri( - "GET", url + "/things", json=[thing_id, thing_id1], status_code=200) + requests_mock.register_uri("GET", url + "/things", json=[thing_id, thing_id1], status_code=200) r = s.things.get_all(token=token, query_params=params) assert r.error.status == 0 assert [thing_id, thing_id1] == r.value - def test_get_all_thing_does_not_exist(requests_mock): - requests_mock.register_uri( - "GET", url + "/things", json=[thing_id, thing_id1], status_code=404) + requests_mock.register_uri("GET", url + "/things", json=[thing_id, thing_id1], status_code=404) r = s.things.get_all(token=token, query_params=params) assert r.error.status == 1 assert r.error.message == "Thing does not exist." - def test_get_by_channel(requests_mock): - requests_mock.register_uri("GET", url + "/channels/" + channel_id + "/things", json=channel_id, - headers={"Authorization": "/channels/" + channel_id + "/things"}, status_code=200) - r = s.things.get_by_channel( - channel_id=channel_id, query_params=params, token=token) + requests_mock.register_uri("GET", url + "/channels/" + channel_id + "/things", json=channel_id, headers={"Authorization": "/channels/" + channel_id + "/things"}, status_code=200) + r = s.things.get_by_channel(channel_id=channel_id, query_params=params, token=token) assert r.error.status == 0 assert channel_id == r.value - def test_get_by_channel_missing_token(requests_mock): - requests_mock.register_uri("GET", url + "/channels/" + channel_id + "/things", json=channel_id, - headers={"Authorization": "/channels/" + channel_id + "/things"}, status_code=401) - r = s.things.get_by_channel( - channel_id=channel_id, query_params=params, token=token) + requests_mock.register_uri("GET", url + "/channels/" + channel_id + "/things", json=channel_id, headers={"Authorization": "/channels/" + channel_id + "/things"}, status_code=401) + r = s.things.get_by_channel(channel_id=channel_id, query_params=params, token=token) assert r.error.status == 1 assert r.error.message == "Missing or invalid access token provided." - def test_update_thing(requests_mock): - requests_mock.register_uri( - "PUT", url + "/things/" + thing_id, json=json.dumps(thing), status_code=200) - r = s.things.update(thing_id=thing_id, token=token, thing=thing) + requests_mock.register_uri("PATCH", url + "/things/" + thing["id"], json=thing, status_code=200) + r = s.things.update(thing_id=thing["id"], token=token, thing=thing) assert r.error.status == 0 - + assert thing== r.value def test_update_thing_bad_json(requests_mock): - requests_mock.register_uri( - "PUT", url + "/things/" + thing_id, json=json.dumps(thing), status_code=400) - r = s.things.update(thing_id=thing_id, token=token, thing=thing) + requests_mock.register_uri("PATCH", url + "/things/" + thing["id"], json=thing, status_code=404) + r = s.things.update(thing_id=thing["id"], token=token, thing=thing) assert r.error.status == 1 - assert r.error.message == "Failed due to malformed JSON." + assert r.error.message == "Thing does not exist." +def test_update_thing_secret(requests_mock): + requests_mock.register_uri("PATCH", url + "/things/" + thing["id"] + "/secret", json=thing, status_code=200) + r = s.things.update_thing_secret(thing_id=thing["id"], token=token, thing=thing) + assert r.error.status == 0 + assert thing== r.value + +def test_update_thing_secret_bad_token(requests_mock): + requests_mock.register_uri("PATCH", url + "/things/" + thing["id"] + "/secret", json=thing, status_code=401) + r = s.things.update_thing_secret(thing_id=thing["id"], token=token, thing=thing) + assert r.error.status == 1 + assert r.error.message == "Missing or invalid access token provided." + +def test_update_thing_tags(requests_mock): + requests_mock.register_uri("PATCH", url + "/things/" + thing["id"] + "/tags", json=thing, status_code=200) + r = s.things.update_thing_tags(thing_id=thing["id"], token=token, thing=thing) + assert r.error.status == 0 + assert thing== r.value + +def test_update_thing_tags_bad_token(requests_mock): + requests_mock.register_uri("PATCH", url + "/things/" + thing["id"] + "/tags", json=thing, status_code=401) + r = s.things.update_thing_tags(thing_id=thing["id"], token=token, thing=thing) + assert r.error.status == 1 + assert r.error.message == "Missing or invalid access token provided." -def test_delete_thing(requests_mock): - requests_mock.register_uri( - "DELETE", url + "/things/" + thing_id, status_code=204) - r = s.things.delete(thing_id=thing_id, token=token) +def test_update_thing_owner(requests_mock): + requests_mock.register_uri("PATCH", url + "/things/" + thing["id"] + "/owner", json=thing, status_code=200) + r = s.things.update_thing_owner(thing_id=thing["id"], token=token, thing=thing) assert r.error.status == 0 + assert thing== r.value + +def test_update_thing_owner_bad_token(requests_mock): + requests_mock.register_uri("PATCH", url + "/things/" + thing["id"] + "/owner", json=thing, status_code=401) + r = s.things.update_thing_owner(thing_id=thing["id"], token=token, thing=thing) + assert r.error.status == 1 + assert r.error.message == "Missing or invalid access token provided." +def test_disable_thing(requests_mock): + requests_mock.register_uri("POST", url + "/things/" + thing["id"] + "/disable", status_code=200) + r = s.things.disable(thing_id=thing["id"], token=token) + assert r.error.status == 0 -def test_delete_bad_thing_id(requests_mock): - requests_mock.register_uri( - "DELETE", url + "/things/" + thing_id, status_code=400) - r = s.things.delete(thing_id=thing_id, token=token) +def test_disable_bad_thing_id(requests_mock): + requests_mock.register_uri("POST", url + "/things/" + thing["id"] + "/disable", status_code=400) + r = s.things.disable(thing_id=thing["id"], token=token) assert r.error.status == 1 assert r.error.message == "Failed due to malformed thing's ID." - def test_connect_thing(requests_mock): - requests_mock.register_uri( - "PUT", url + "/channels/" + channel_id + "/things/"+thing_id, json=[channel_id, thing_id], status_code=200) - r = s.things.connect(channel_id=channel_id, thing_id=thing_id, token=token) + requests_mock.register_uri("POST", url + "/policies", status_code=201) + r = s.things.connect(channel_id=channel_id, thing_id=thing_id, token=token, action=["m_read"]) assert r.error.status == 0 assert r.value == "connected" +def test_connects_things(requests_mock): + requests_mock.register_uri("POST", url + "/connect", json=policies, status_code=201) + r = s.things.connects(channel_ids=[channel_id], thing_ids=[thing_id, thing_id1], token=token, actions=["m_read"]) + assert r.error.status == 0 + +def test_connects_things_non_existing_entity(requests_mock): + requests_mock.register_uri("POST", url + "/connect", status_code=400) + r = s.things.connects(channel_ids=[channel_id], thing_ids=[thing_id, thing_id1], token=token, actions=["m_read"]) + assert r.error.status == 1 + assert r.error.message == "A non-existent entity request." def test_connect_non_existing_entity(requests_mock): - requests_mock.register_uri( - "PUT", url + "/channels/" + channel_id + "/things/"+thing_id, json=[channel_id, thing_id], status_code=404) - r = s.things.connect(channel_id=channel_id, thing_id=thing_id, token=token) + requests_mock.register_uri("POST", url + "/policies", status_code=404) + r = s.things.connect(channel_id=channel_id, thing_id=thing_id, token=token, action="m_read") assert r.error.status == 1 assert r.error.message == "A non-existent entity request." - def test_disconnect_thing(requests_mock): - requests_mock.register_uri( - "DELETE", url + "/channels/" + channel_id + "/things/" + thing_id, status_code=204) - r = s.things.disconnect(channel_id=channel_id, - thing_id=thing_id, token=token) + requests_mock.register_uri("DELETE", url + "/policies" + "/" + thing_id + "/" + channel_id, status_code=204) + r = s.things.disconnect(channel_id=channel_id, thing_id=thing_id, token=token) assert r.error.status == 0 - - + def test_disconnect_thing_or_channel_does_not_exist(requests_mock): - requests_mock.register_uri( - "DELETE", url + "/channels/" + channel_id + "/things/" + thing_id, status_code=404) - r = s.things.disconnect(channel_id=channel_id, - thing_id=thing_id, token=token) + requests_mock.register_uri("DELETE", url + "/policies" + "/" + thing_id + "/" + channel_id, status_code=404) + r = s.things.disconnect(channel_id=channel_id, thing_id=thing_id, token=token) assert r.error.status == 1 assert r.error.message == "Channel or thing does not exist." - def test_disconnects(requests_mock): - requests_mock.register_uri("DELETE", url + "/disconnect", status_code=200) - r = s.things.disconnects(channel_ids=[channel_id], thing_ids=[ - thing_id, thing_id1], token=token) + requests_mock.register_uri("POST", url + "/disconnect", status_code=204) + r = s.things.disconnects(channel_ids=[channel_id], thing_ids=[thing_id, thing_id1], token=token) + assert r.error.status == 0 + +def test_disconnects_bad_json(requests_mock): + requests_mock.register_uri("POST", url + "/disconnect", status_code=404) + r = s.things.disconnects(channel_ids=[channel_id], thing_ids=[thing_id, thing_id1], token=token) assert r.error.status == 1 + assert r.error.message == "Channel or thing does not exist." +def test_share_thing(requests_mock): + requests_mock.register_uri("POST", url + "/policies", status_code=201) + r = s.things.share_thing(channel_id=channel_id, user_id=user_id, actions= action, token=token) + assert r.error.status == 0 -def test_disconnects_bad_json(requests_mock): - requests_mock.register_uri("DELETE", url + "/disconnect", status_code=400) - r = s.things.disconnects(channel_ids=[channel_id], thing_ids=[ - thing_id, thing_id1], token=token) +def test_share_thing_bad_token(requests_mock): + requests_mock.register_uri("POST", url + "/policies", status_code=400) + r = s.things.share_thing(channel_id=channel_id, user_id=user_id, actions= action, token=token) assert r.error.status == 1 - assert r.error.message == "Failed due to malformed query parameters." + assert r.error.message == "A non-existent entity request." + +def test_authorise_thing(requests_mock): + requests_mock.register_uri("POST", url + "/channels/object/access", status_code=200) + r = s.things.authorise_thing(access_request=access_request , token=token) + assert r.error.status == 0 + +def test_authorise_thing_bad_token(requests_mock): + requests_mock.register_uri("POST", url + "/channels/object/access", status_code=403) + r = s.things.authorise_thing(access_request=access_request , token=token) + assert r.error.status == 1 + assert r.error.message == "False" diff --git a/tests/test_users.py b/tests/test_users.py index 9aafe74..3d56a14 100644 --- a/tests/test_users.py +++ b/tests/test_users.py @@ -1,88 +1,252 @@ from mainflux import sdk -import json, requests_mock +import requests_mock s = sdk.SDK() -user = {"email": "test@email.com", "password": "12345678"} -user1 = {"email": "test1@email.com", "password": "12345678"} -user2 = {"email": "test2@email.com", "password": "12345678"} -old_password = {"old_password": "12345678"} -password = {"password": "dsa"} +user = { + "id": "bb7edb32-2eac-4aad-aebe-ed96fe073879", + "name": "userName", + "tags": [ + "tag1", + "tag2" + ], + "owner": "aa68f94c-de36-48dd-9c53-d8ac8b12d86b", + "credentials": { + "identity": "test@email.com" + }, + "metadata": { + "domain": "example.com" + }, + "status": "enabled", + "created_at": "2019-11-26 13:31:52", + "updated_at": "2019-11-26 13:31:52" +} +user1 = { + "id": "1d36ac54-9bb7-4079-a46b-63c34f7fc678", + "name": "userName1", + "tags": [ + "tag1", + "tag2" + ], + "owner": "aa68f94c-de36-48dd-9c53-d8ac8b12d86b", + "credentials": { + "identity": "test1@email.com" + }, + "metadata": { + "domain": "example.com" + }, + "status": "enabled", + "created_at": "2019-11-26 13:31:52", + "updated_at": "2019-11-26 13:31:52" +} + +user2 = { + "id": "e2c769b8-8b8c-4886-8b19-e155c4d363e6", + "name": "userName", + "tags": [ + "tag1", + "tag2" + ], + "owner": "aa68f94c-de36-48dd-9c53-d8ac8b12d86b", + "credentials": { + "identity": "test2@email.com" + }, + "metadata": { + "domain": "example.com" + }, + "status": "enabled", + "created_at": "2019-11-26 13:31:52", + "updated_at": "2019-11-26 13:31:52" +} +old_secret = {"old_secret": "12345678"} +new_secret = {"new_secret": "dsa"} user_id = "123-456" -token = "9a8b7c6d5e4f3g21" +token = { + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", + "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9", + "access_type": "access" +} +access_request ={ + "subject": "e2c769b8-8b8c-4886-8b19-e155c4d363e6", + "object": "f20e0b0e-b05e-401e-ac53-59b99eea3519", + "action": "g_add", + "entity_type": "client" +} +password= "12345678" +confirm_password="12345678" +email={"admin@example.com": "email"} url = "http://localhost" - +url2 = "http://localhost/password/reset-request" def test_create_user(requests_mock): - requests_mock.register_uri("POST", url + "/users", headers={"location": "/users/" + user_id}, status_code=201) + requests_mock.register_uri("POST", url + "/users", headers={"location": "/users/" + user_id}, json=user, status_code=201) r = s.users.create(user=user) assert r.error.status == 0 - assert user_id == r.value - + assert user == r.value +def test_create_user_bad_user(requests_mock): + requests_mock.register_uri("POST", url + "/users", headers={"location": "/users/" + user_id}, status_code=409) + r = s.users.create(user=user) + assert r.error.status == 1 + assert r.error.message == "Failed due to using an existing identity." + def test_login_user(requests_mock): - requests_mock.register_uri("POST", url + "/tokens", json={"token": token}, status_code=201) + requests_mock.register_uri("POST", url + "/users/tokens/issue", json=token, status_code=201) r = s.users.login(user=user) assert r.error.status == 0 assert token == r.value - def test_login_user_bad_email(requests_mock): - requests_mock.register_uri("POST", url + "/tokens", status_code=409) + requests_mock.register_uri("POST", url + "/users/tokens/issue", status_code=409) r = s.users.login(user=user) assert r.error.status == 1 assert r.error.message == "Failed due to using an existing email address." + +def test_refresh_token(requests_mock): + requests_mock.register_uri("POST", url + "/users/tokens/refresh", json=token, status_code=201) + r = s.users.refresh_token(refresh_token=token["refresh_token"]) + assert r.error.status == 0 + assert token == r.value +def test_refresh_token_bad_token(requests_mock): + requests_mock.register_uri("POST", url + "/users/tokens/refresh",json=token, status_code=404) + r = s.users.refresh_token(refresh_token=token["refresh_token"]) + assert r.error.status == 1 + assert r.error.message == "A non-existent entity request." def test_get_user(requests_mock): requests_mock.register_uri("GET", url + "/users/" + user_id, json=user, status_code=200) - r = s.users.get(user_id=user_id, token=token) + r = s.users.get(user_id=user_id, token=token["access_token"]) assert r.error.status == 0 assert user == r.value - def test_get_user_bad_token(requests_mock): requests_mock.register_uri("GET", url + "/users/" + user_id, json=user, status_code=401) - r = s.users.get(user_id=user_id, token=token) + r = s.users.get(user_id=user_id, token=token["access_token"]) assert r.error.status == 1 assert r.error.message == "Missing or invalid access token provided." - def test_get_all_users(requests_mock): requests_mock.register_uri("GET", url + "/users", json=[user, user2], status_code=200) - r = s.users.get_all(admin_token=token, query_params=None) + r = s.users.get_all(query_params=None, user_token=token["access_token"]) assert r.error.status == 0 assert [user, user2] == r.value - def test_get_all_user_bad_request(requests_mock): requests_mock.register_uri("GET", url + "/users" , json=user, status_code=422) - r = s.users.get_all(admin_token=token, query_params=None) + r = s.users.get_all(query_params=None, user_token=token["access_token"]) assert r.error.status == 1 assert r.error.message == "Database can't process request." - def test_update_user(requests_mock): - requests_mock.register_uri("PUT", url + "/users", json=json.dumps(user1), status_code=200) - r = s.users.update(user=user, user_token=token) + requests_mock.register_uri("PATCH", url + "/users/" + user["id"], json=user, status_code=200) + r = s.users.update(user=user, user_token=token["access_token"]) assert r.error.status == 0 - def test_non_existing_user_update(requests_mock): - requests_mock.register_uri("PUT", url + "/users", json=json.dumps(user1), status_code=404) - r = s.users.update(user=user, user_token=token) + requests_mock.register_uri("PATCH",url + "/users/" + user["id"], json=user, status_code=404) + r = s.users.update(user=user, user_token=token["access_token"]) assert r.error.status == 1 assert r.error.message == "Failed due to non existing user." +def test_update_user_identity(requests_mock): + requests_mock.register_uri("PATCH", url + "/users/" + user["id"] + "/identity", json=user, status_code=200) + r = s.users.update_user_identity(user=user, user_token=token["access_token"]) + assert r.error.status == 0 -def test_update_user_password(requests_mock): - requests_mock.register_uri("PATCH", url + "/password", status_code=201) - r = s.users.update_password(old_password=old_password, password=password, user_token=token) +def test_non_existing_user_identity_update(requests_mock): + requests_mock.register_uri("PATCH",url + "/users/" + user["id"] + "/identity", json=user, status_code=401) + r = s.users.update_user_identity(user=user, user_token=token["access_token"]) + assert r.error.status == 1 + assert r.error.message == "Missing or invalid access token provided." + +def test_update_user_tags(requests_mock): + requests_mock.register_uri("PATCH", url + "/users/" + user["id"] + "/tags", json=user, status_code=200) + r = s.users.update_user_tags(user=user, user_token=token["access_token"]) + assert r.error.status == 0 + + +def test_non_existing_user_tags_update(requests_mock): + requests_mock.register_uri("PATCH",url + "/users/" + user["id"] + "/tags", json=user, status_code=401) + r = s.users.update_user_tags(user=user, user_token=token["access_token"]) + assert r.error.status == 1 + assert r.error.message == "Missing or invalid access token provided." + +def test_update_user_owner(requests_mock): + requests_mock.register_uri("PATCH", url + "/users/" + user["id"] + "/owner", json=user, status_code=200) + r = s.users.update_user_owner(user=user, user_token=token["access_token"]) assert r.error.status == 0 +def test_non_existing_user_owner_update(requests_mock): + requests_mock.register_uri("PATCH",url + "/users/" + user["id"] + "/owner", json=user, status_code=401) + r = s.users.update_user_owner(user=user, user_token=token["access_token"]) + assert r.error.status == 1 + assert r.error.message == "Missing or invalid access token provided." + +def test_update_user_password(requests_mock): + requests_mock.register_uri("PATCH", url + "/users" + "/secret", status_code=200) + r = s.users.update_password(old_secret=old_secret, new_secret=new_secret, user_token=token["access_token"]) + assert r.error.status == 0 def test_update_user_password_bad_token(requests_mock): - requests_mock.register_uri("PATCH", url + "/password", status_code=415) - r = s.users.update_password(old_password=old_password, password=password, user_token=token) + requests_mock.register_uri("PATCH",url + "/users" + "/secret", status_code=415) + r = s.users.update_password(old_secret=old_secret, new_secret=new_secret, user_token=token["access_token"]) assert r.error.status == 1 assert r.error.message == "Missing or invalid content type." + +def test_reset_password_request(requests_mock): + requests_mock.register_uri("POST", url + "/password/reset-request", json=email, status_code=201) + r = s.users. reset_password_request(email=email, url=url2) + assert r.error.status == 0 + +def test_reset_password_request_bad_email(requests_mock): + requests_mock.register_uri("POST",url + "/password/reset-request", status_code=400) + r = s.users. reset_password_request(email=email, url=url2) + assert r.error.status == 1 + assert r.error.message == "Failed due to malformed JSON." + +def test_reset_password(requests_mock): + requests_mock.register_uri("PUT", url + "/password/reset", status_code=201) + r = s.users. reset_password(password=password, confirm_password=confirm_password, token=token) + assert r.error.status == 0 + +def test_reset_password_bad_token(requests_mock): + requests_mock.register_uri("PUT",url + "/password/reset", status_code=400) + r = s.users. reset_password(password=password, confirm_password=confirm_password, token=token) + assert r.error.status == 1 + assert r.error.message == "Failed due to malformed JSON." + +def test_enable_user(requests_mock): + requests_mock.register_uri("POST", url + "/users/" + user["id"] + "/enable", json=user, status_code=200) + r = s.users.enable(user_id=user["id"], user_token= token["access_token"]) + assert r.error.status == 0 + assert user == r.value + +def test_disable_user(requests_mock): + requests_mock.register_uri("POST", url + "/users/" + user["id"] + "/disable", json=user, status_code=200) + r = s.users.disable(user_id=user["id"], user_token= token["access_token"]) + assert r.error.status == 0 + assert user == r.value + +def test_enable_user_bad_user(requests_mock): + requests_mock.register_uri("POST", url + "/users/" + user["id"] + "/enable", json=user, status_code=404) + r = s.users.enable(user_id=user["id"], user_token= token["access_token"]) + assert r.error.status == 1 + assert r.error.message == "Failed due to non existing user." + +def test_disable_user_bad_user(requests_mock): + requests_mock.register_uri("POST", url + "/users/" + user["id"] + "/disable", json=user, status_code=404) + r = s.users.disable(user_id=user["id"], user_token= token["access_token"]) + assert r.error.status == 1 + assert r.error.message == "Failed due to non existing user." + +def test_authorise_user(requests_mock): + requests_mock.register_uri("POST", url + "/authorize", status_code=200) + r = s.users.authorise_user(access_request=access_request , token=token["access_token"]) + assert r.error.status == 0 + +def test_authorise_user_bad_token(requests_mock): + requests_mock.register_uri("POST", url + "/authorize", status_code=400) + r = s.users.authorise_user(access_request=access_request , token=token["access_token"]) + assert r.error.status == 1 + assert r.error.message == "Failed due to malformed JSON."