|
6 | 6 | from pathlib import Path
|
7 | 7 | from typing import Any, Dict, Optional, Union, List, Iterable
|
8 | 8 |
|
| 9 | +from singer.schema import Schema |
9 | 10 |
|
10 | 11 | from singer_sdk.streams import RESTStream
|
| 12 | +from singer_sdk.helpers._util import utc_now |
| 13 | +from singer_sdk.plugin_base import PluginBase as TapBaseClass |
11 | 14 |
|
12 | 15 |
|
13 | 16 | from singer_sdk.authenticators import (
|
@@ -40,30 +43,64 @@ def oauth_request_body(self) -> dict:
|
40 | 43 | 'grant_type': 'refresh_token',
|
41 | 44 | 'client_id': self.config["client_id"],
|
42 | 45 | 'client_secret': self.config["client_secret"],
|
43 |
| - 'refresh_token': self.config["refresh_token"], |
| 46 | + 'refresh_token': self.config["refresh_token"] if not self.refresh_token else self.refresh_token, |
44 | 47 | 'redirect_uri': self.config["redirect_uri"]
|
45 | 48 | }
|
46 | 49 |
|
47 | 50 | return req
|
48 | 51 |
|
| 52 | + def update_access_token(self): |
| 53 | + """Update `access_token` along with: `last_refreshed` and `expires_in`.""" |
| 54 | + request_time = utc_now() |
| 55 | + auth_request_payload = self.oauth_request_payload |
| 56 | + token_response = requests.post(self.auth_endpoint, data=auth_request_payload) |
| 57 | + try: |
| 58 | + token_response.raise_for_status() |
| 59 | + self.logger.info("OAuth authorization attempt was successful.") |
| 60 | + except Exception as ex: |
| 61 | + raise RuntimeError( |
| 62 | + f"Failed OAuth login, response was '{token_response.json()}'. {ex}" |
| 63 | + ) |
| 64 | + token_json = token_response.json() |
| 65 | + self.access_token = token_json["access_token"] |
| 66 | + self.expires_in = token_json["expires_in"] |
| 67 | + self.last_refreshed = request_time |
| 68 | + |
| 69 | + if token_json.get("refresh_token") is not None: |
| 70 | + self.refresh_token = token_json["refresh_token"] |
| 71 | + |
49 | 72 |
|
50 | 73 | class ProcoreStream(RESTStream):
|
51 | 74 | """Procore stream class."""
|
52 | 75 |
|
| 76 | + def __init__( |
| 77 | + self, |
| 78 | + tap: TapBaseClass, |
| 79 | + name: Optional[str] = None, |
| 80 | + schema: Optional[Union[Dict[str, Any], Schema]] = None, |
| 81 | + path: Optional[str] = None, |
| 82 | + ): |
| 83 | + """Initialize the Procore stream.""" |
| 84 | + super().__init__(name=name, schema=schema, tap=tap, path=path) |
| 85 | + self._config = tap._config |
| 86 | + |
53 | 87 | @property
|
54 | 88 | def url_base(self) -> str:
|
55 | 89 | """Return the API URL root, configurable via tap settings."""
|
56 | 90 | return "https://sandbox.procore.com/rest/v1.0" if self.config["is_sandbox"] else "https://api.procore.com/rest/v1.0"
|
57 | 91 |
|
58 | 92 | @property
|
59 | 93 | def authenticator(self) -> APIAuthenticatorBase:
|
60 |
| - auth_endpoint = "https://login-sandbox.procore.com/oauth/token" if self.config[ |
61 |
| - "is_sandbox"] else "https://login.procore.com/oauth/token" |
| 94 | + if not self._config.get("authenticator"): |
| 95 | + auth_endpoint = "https://login-sandbox.procore.com/oauth/token" if self.config[ |
| 96 | + "is_sandbox"] else "https://login.procore.com/oauth/token" |
| 97 | + |
| 98 | + self._config["authenticator"] = ProcoreAuthenticator( |
| 99 | + stream=self, |
| 100 | + auth_endpoint=auth_endpoint |
| 101 | + ) |
62 | 102 |
|
63 |
| - return ProcoreAuthenticator( |
64 |
| - stream=self, |
65 |
| - auth_endpoint=auth_endpoint |
66 |
| - ) |
| 103 | + return self._config["authenticator"] |
67 | 104 |
|
68 | 105 |
|
69 | 106 | class CompaniesStream(ProcoreStream):
|
|
0 commit comments