diff --git a/README.md b/README.md index c0a4efe..64c4c81 100644 --- a/README.md +++ b/README.md @@ -54,20 +54,19 @@ import time import data_bridges_client from data_bridges_client.rest import ApiException from pprint import pprint +from data_bridges_client.token import WfpApiToken -# Defining the host is optional and defaults to https://api.wfp.org/vam-data-bridges/2.0.0 +# Configure OAuth2 access token for authorization: default +KEY = '' +SECRET = '' +token = WfpApiToken(api_key=KEY, api_secret=SECRET) + +# Defining the host is optional and defaults to https://api.wfp.org/vam-data-bridges/1.4.0 # See configuration.py for a list of all supported configuration parameters. configuration = data_bridges_client.Configuration( - host = "https://api.wfp.org/vam-data-bridges/2.0.0" + host = "https://api.wfp.org/vam-data-bridges/1.4.0" ) - -# The client must configure the authentication and authorization parameters -# in accordance with the API server security policy. -# Examples for each auth method are provided below, use the example that -# satisfies your auth use case. - -configuration.access_token = os.environ["ACCESS_TOKEN"] - +configuration.access_token = token.refresh() # Enter a context with an instance of the API client with data_bridges_client.ApiClient(configuration) as api_client: diff --git a/data_bridges_client/token.py b/data_bridges_client/token.py new file mode 100644 index 0000000..41416eb --- /dev/null +++ b/data_bridges_client/token.py @@ -0,0 +1,63 @@ +import httpx +import datetime +from typing import Optional, Any, List, Dict +import data_bridges_client +import urllib3 +import json + + +class WfpApiToken: + BASE_URL = "https://api.wfp.org" + + def __init__(self, api_key: str, api_secret: str): + """ + Args: + api_key: API key credential to make API requests + api_secret: API secrets credential to make API requests + """ + self.api_key = api_key + self.api_secret = api_secret + + def refresh(self, scopes: Optional[str] = None): + """ + Refreshes token to make API requests + Args: + scopes: API scopes. The default is None + """ + if scopes is None: + scopes = [] + resp = httpx.post( + f"{self.BASE_URL}/token", + data={"grant_type": "client_credentials", "scope": " ".join(scopes)}, + auth=(self.api_key, self.api_secret), + ) + resp.raise_for_status() + resp_data = resp.json() + received_scopes = set(resp_data["scope"].split(" ")) + if not set(scopes).issubset(received_scopes): + raise ValueError(f"Could not acquire requested scopes: {scopes}") + return resp_data["access_token"] + + def refresh_urllib3(self, scopes: Optional[str] = None): + """ + Refreshes token to make API requests + Same function as self.refresh but uses urllib3 + """ + + if scopes is None: + scopes = [] + resp = urllib3.request( + "POST", + f"{self.BASE_URL}/token", + body={"grant_type": "client_credentials", "scope": " ".join(scopes)}, + headers=urllib3.make_headers(basic_auth=f"{self.api_key}:{self.api_secret}"), + ) + return resp + + def refresh_configuration(self): + """ + Instantiate new client.Configuration with fresh OAuth2 access token + """ + configuration = data_bridges_client.Configuration() + configuration.access_token = self.refresh() + return configuration diff --git a/generate/README.md b/generate/README.md new file mode 100644 index 0000000..408b5ca --- /dev/null +++ b/generate/README.md @@ -0,0 +1,28 @@ +# Generating a new client + +Every time + + +1. Create a new branch `git checkout -b new_branch_name` +2. Install openapi-generator + +```npm install @openapitools/openapi-generator-cli -g``` + +2. Place the latest swagger.json in `./generate/swagger.json` + +3. Remove the current files except the `token.py` file + +``` +mv data_bridges_client/token.py . +rm -rf tests docs data_bridges_client +mkdir data_bridges_client +mv ./token.py data_bridges_client/ +``` + +5. Generate the client by running this command from the root of the repository. We pin the version to 5.4.0 as python templates from more recent versions don't seem to work. +``` +openapi-generator-cli version-manager set 5.4.0 +openapi-generator-cli generate -g python -i generate/swagger.json -o . --package-name data_bridges_client --git-user-id WFP-VAM --git-repo-id DataBridgesAPI +``` +4. Manually revert changes to `setup.py` and `README.md`. These relate to additional functionality we developed to handle the WFP token refresh workflow. +5. Commit, push and open a PR into `dev` for review. \ No newline at end of file diff --git a/generate/generate.sh b/generate/generate.sh new file mode 100644 index 0000000..1e2cb26 --- /dev/null +++ b/generate/generate.sh @@ -0,0 +1,3 @@ +#!/bin/bash +openapi-generator-cli version-manager set 5.4.0 +openapi-generator-cli generate -g python -i generate/swagger.json -o . --package-name data_bridges_client --git-user-id WFP-VAM --git-repo-id DataBridgesAPI \ No newline at end of file diff --git a/generate/install.sh b/generate/install.sh new file mode 100644 index 0000000..2d2baf5 --- /dev/null +++ b/generate/install.sh @@ -0,0 +1 @@ +npm install @openapitools/openapi-generator-cli -g \ No newline at end of file diff --git a/setup.py b/setup.py index f1ce016..508dec8 100644 --- a/setup.py +++ b/setup.py @@ -26,6 +26,7 @@ PYTHON_REQUIRES = ">=3.7" REQUIRES = [ "urllib3 >= 1.25.3, < 2.1.0", + "httpx", "python-dateutil", "pydantic >= 2", "typing-extensions >= 4.7.1",