Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

🎉 square: added oauth support #6842

Merged
merged 27 commits into from
Jan 18, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
2754a2a
fixed test which check incorrect cred config
midavadim Oct 4, 2021
4b29f13
Added oauth2 authentication
midavadim Oct 6, 2021
633b967
Added oauth creds
midavadim Oct 6, 2021
9e11b0d
fixed formatting
midavadim Oct 7, 2021
2993e77
added oauth2 spec section, added missing type hints
midavadim Oct 15, 2021
2656996
Merge branch 'master' into midavadim/source-square-oauth-support
etsybaev Oct 27, 2021
d5f7e0a
Merge branch 'master' into midavadim/source-square-oauth-support
etsybaev Oct 28, 2021
fd940c7
Added java part of Square OAuth
etsybaev Nov 1, 2021
1e93f2e
fixed checkstyle
etsybaev Nov 1, 2021
b7a1fee
removed commented code
etsybaev Nov 1, 2021
21a0117
added support for old format of spec.json files, updated change logs …
midavadim Nov 2, 2021
b683481
Merge remote-tracking branch 'origin/master' into midavadim/source-sq…
midavadim Nov 30, 2021
1367280
renamed spec property 'authentication' to default 'credentials'. fixe…
midavadim Dec 1, 2021
c7c946c
Merge remote-tracking branch 'origin/master' into midavadim/source-sq…
midavadim Dec 1, 2021
ce91036
recovered empty files
midavadim Dec 1, 2021
a8d6c70
updated OAuthImplementationFactory.java
midavadim Dec 2, 2021
ce2477e
fixed issue with autheticator for sub streams, added config catalog w…
midavadim Dec 2, 2021
222e067
Merge remote-tracking branch 'origin/master' into midavadim/source-sq…
midavadim Dec 10, 2021
9d7c3a3
use advanced_auth
midavadim Dec 27, 2021
7f99a15
Merge remote-tracking branch 'origin/master' into midavadim/source-sq…
midavadim Jan 11, 2022
27020e7
added advanced_auth
midavadim Jan 11, 2022
b7ba86d
moved scopes to private property
midavadim Jan 11, 2022
ce3d061
updated source version
midavadim Jan 17, 2022
d6f3447
Merge remote-tracking branch 'origin/master' into midavadim/source-sq…
midavadim Jan 17, 2022
64e2835
Revert "updated source version"
midavadim Jan 17, 2022
0f67641
updated source version
midavadim Jan 18, 2022
d9f0ca7
added new version for airbyte index
midavadim Jan 18, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/publish-command.yml
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ jobs:
SNOWFLAKE_S3_COPY_INTEGRATION_TEST_CREDS: ${{ secrets.SNOWFLAKE_S3_COPY_INTEGRATION_TEST_CREDS }}
SNOWFLAKE_GCS_COPY_INTEGRATION_TEST_CREDS: ${{ secrets.SNOWFLAKE_GCS_COPY_INTEGRATION_TEST_CREDS }}
SOURCE_SQUARE_CREDS: ${{ secrets.SOURCE_SQUARE_CREDS }}
SOURCE_SQUARE_CREDS_OAUTH: ${{ secrets.SOURCE_SQUARE_CREDS_OAUTH }}
SOURCE_MARKETO_TEST_CREDS: ${{ secrets.SOURCE_MARKETO_TEST_CREDS }}
SOURCE_MARKETO_SINGER_INTEGRATION_TEST_CONFIG: ${{ secrets.SOURCE_MARKETO_SINGER_INTEGRATION_TEST_CONFIG }}
SOURCE_RECURLY_INTEGRATION_TEST_CREDS: ${{ secrets.SOURCE_RECURLY_INTEGRATION_TEST_CREDS }}
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-command.yml
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ jobs:
SNOWFLAKE_S3_COPY_INTEGRATION_TEST_CREDS: ${{ secrets.SNOWFLAKE_S3_COPY_INTEGRATION_TEST_CREDS }}
SNOWFLAKE_GCS_COPY_INTEGRATION_TEST_CREDS: ${{ secrets.SNOWFLAKE_GCS_COPY_INTEGRATION_TEST_CREDS }}
SOURCE_SQUARE_CREDS: ${{ secrets.SOURCE_SQUARE_CREDS }}
SOURCE_SQUARE_CREDS_OAUTH: ${{ secrets.SOURCE_SQUARE_CREDS_OAUTH }}
SOURCE_MARKETO_TEST_CREDS: ${{ secrets.SOURCE_MARKETO_TEST_CREDS }}
SOURCE_MARKETO_SINGER_INTEGRATION_TEST_CONFIG: ${{ secrets.SOURCE_MARKETO_SINGER_INTEGRATION_TEST_CONFIG }}
SOURCE_RECURLY_INTEGRATION_TEST_CREDS: ${{ secrets.SOURCE_RECURLY_INTEGRATION_TEST_CREDS }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,23 @@ tests:
connection:
- config_path: "secrets/config.json"
status: "succeed"
- config_path: "secrets/config_oauth.json"
status: "succeed"
- config_path: "integration_tests/invalid_config.json"
status: "failed"
discovery:
- config_path: "secrets/config.json"
midavadim marked this conversation as resolved.
Show resolved Hide resolved
basic_read:
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog.json"
- config_path: "secrets/config_oauth.json"
configured_catalog_path: "integration_tests/configured_catalog_oauth.json"
incremental:
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog.json"
future_state_path: "integration_tests/abnormal_state.json"
full_refresh:
- config_path: "secrets/config.json"
configured_catalog_path: "integration_tests/configured_catalog.json"
- config_path: "secrets/config_oauth.json"
midavadim marked this conversation as resolved.
Show resolved Hide resolved
configured_catalog_path: "integration_tests/configured_catalog_oauth.json"
Original file line number Diff line number Diff line change
Expand Up @@ -143,18 +143,6 @@
"sync_mode": "full_refresh",
"cursor_field": ["id"],
"destination_sync_mode": "overwrite"
},
{
bazarnov marked this conversation as resolved.
Show resolved Hide resolved
"stream": {
"name": "orders",
"json_schema": {},
"supported_sync_modes": ["full_refresh"],
"source_defined_cursor": true,
"default_cursor_field": ["id"]
},
"sync_mode": "full_refresh",
"cursor_field": ["id"],
"destination_sync_mode": "overwrite"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"streams": [
{
"stream": {
"name": "locations",
"json_schema": {},
"supported_sync_modes": ["full_refresh"],
"source_defined_cursor": true,
"default_cursor_field": ["id"]
},
"sync_mode": "full_refresh",
"cursor_field": ["id"],
"destination_sync_mode": "overwrite"
},
{
"stream": {
"name": "team_members",
"json_schema": {},
"supported_sync_modes": ["full_refresh"],
"source_defined_cursor": true,
"default_cursor_field": ["id"]
},
"sync_mode": "full_refresh",
"cursor_field": ["id"],
"destination_sync_mode": "overwrite"
},
{
"stream": {
"name": "team_member_wages",
"json_schema": {},
"supported_sync_modes": ["full_refresh"],
"source_defined_cursor": true,
"default_cursor_field": ["id"]
},
"sync_mode": "full_refresh",
"cursor_field": ["id"],
"destination_sync_mode": "overwrite"
}
]
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"api_key": "API_KEY",
"is_sandbox": true,
"start_date": "START_DATE",
"include_deleted_objects": false
"authorization": {"auth_type": "Apikey", "api_key": "bla"},
midavadim marked this conversation as resolved.
Show resolved Hide resolved
"is_sandbox": true,
"start_date": "2021-06-01",
"include_deleted_objects": false
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from airbyte_cdk.sources import AbstractSource
from airbyte_cdk.sources.streams import Stream
from airbyte_cdk.sources.streams.http import HttpStream
from airbyte_cdk.sources.streams.http.auth import TokenAuthenticator
from airbyte_cdk.sources.streams.http.requests_native_auth import Oauth2Authenticator, TokenAuthenticator
from source_square.utils import separate_items_by_count


Expand Down Expand Up @@ -358,16 +358,73 @@ def stream_slices(self, **kwargs) -> Iterable[Optional[Mapping[str, Any]]]:
yield {"location_ids": location}


class Oauth2AuthenticatorSquare(Oauth2Authenticator):

def refresh_access_token(self):
midavadim marked this conversation as resolved.
Show resolved Hide resolved
"""Handle differences in expiration attr:
from API: "expires_at": "2021-11-05T14:26:57Z"
expected: "expires_in": number of seconds
"""
token, expires_at = super().refresh_access_token()
expires_in = pendulum.parse(expires_at) - pendulum.now()
return token, expires_in.seconds


class SourceSquare(AbstractSource):
api_version = "2021-06-16" # Latest Stable Release
api_version = "2021-09-15" # Latest Stable Release

@staticmethod
def get_auth(config):
midavadim marked this conversation as resolved.
Show resolved Hide resolved

authorization = config.get("authorization", {})
auth_type = authorization.get("auth_type")
if auth_type == "Oauth":
scopes = [
"CUSTOMERS_READ",
"EMPLOYEES_READ",
"ITEMS_READ",
"MERCHANT_PROFILE_READ",
"ORDERS_READ",
"PAYMENTS_READ",
"TIMECARDS_READ",
# OAuth Permissions:
midavadim marked this conversation as resolved.
Show resolved Hide resolved
# https://developer.squareup.com/docs/oauth-api/square-permissions
# https://developer.squareup.com/reference/square/enums/OAuthPermission
# "DISPUTES_READ",
# "GIFTCARDS_READ",
# "INVENTORY_READ",
# "INVOICES_READ",
# "TIMECARDS_SETTINGS_READ",
# "LOYALTY_READ",
# "ONLINE_STORE_SITE_READ",
# "ONLINE_STORE_SNIPPETS_READ",
# "SUBSCRIPTIONS_READ",
midavadim marked this conversation as resolved.
Show resolved Hide resolved
]

auth = Oauth2AuthenticatorSquare(
token_refresh_endpoint="https://connect.squareup.com/oauth2/token",
client_secret=authorization.get("client_secret"),
client_id=authorization.get("client_id"),
refresh_token=authorization.get("refresh_token"),
scopes=scopes,
expires_in_name="expires_at",
)
elif auth_type == "Apikey":
auth = TokenAuthenticator(token=authorization.get("api_key"))
else:
raise Exception(f"Invalid auth type: {auth_type}")

return auth

def check_connection(self, logger, config) -> Tuple[bool, any]:
midavadim marked this conversation as resolved.
Show resolved Hide resolved

headers = {
midavadim marked this conversation as resolved.
Show resolved Hide resolved
"Square-Version": self.api_version,
"Authorization": "Bearer {}".format(config["api_key"]),
"Content-Type": "application/json",
}
auth = self.get_auth(config)
headers.update(auth.get_auth_header())

url = "https://connect.squareup{}.com/v2/catalog/info".format("sandbox" if config["is_sandbox"] else "")

try:
Expand All @@ -383,9 +440,8 @@ def check_connection(self, logger, config) -> Tuple[bool, any]:

def streams(self, config: Mapping[str, Any]) -> List[Stream]:

auth = TokenAuthenticator(token=config["api_key"])
args = {
"authenticator": auth,
"authenticator": self.get_auth(config),
"is_sandbox": config["is_sandbox"],
"api_version": self.api_version,
"start_date": config["start_date"],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,90 @@
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Square Source CDK Specifications",
"type": "object",
"required": ["api_key", "is_sandbox"],
"required": ["authorization", "is_sandbox"],
"additionalProperties": false,
"properties": {
"api_key": {
"type": "string",
"description": "The API key for a Square application",
"airbyte_secret": true
},
"is_sandbox": {
"type": "boolean",
"description": "Determines the sandbox (true) or production (false) API version",
"examples": [true, false],
"default": true
"default": false
},
"start_date": {
"type": "string",
"description": "The start date to sync data. Leave blank for full sync. Format: YYYY-MM-DD.",
"examples": ["2021-01-01"],
"default": "1970-01-01",
"default": "2021-01-01",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}$"
},
"include_deleted_objects": {
"type": "boolean",
"description": "In some streams there is and option to include deleted objects (Items, Categories, Discounts, Taxes)",
"examples": [true, false],
"default": false
},
"authorization": {
"type": "object",
"title": "Authentication Type",
"oneOf": [
{
"title": "Oauth authentication",
"type": "object",
"required": [
"auth_type",
"client_id",
"client_secret",
"refresh_token"
],
"properties": {
"auth_type": {
"type": "string",
"const": "Oauth",
"enum": ["Oauth"],
"default": "Oauth",
"order": 0
},
"client_id": {
"title": "Client ID",
"type": "string",
"description": "The Square-issued ID of your application",
"airbyte_secret": true
},
"client_secret": {
"title": "Client Secret",
"type": "string",
"description": "The Square-issued application secret for your application",
"airbyte_secret": true
},
"refresh_token": {
"title": "Refresh Token",
"type": "string",
"description": "A refresh token generated using the above client ID and secret",
"airbyte_secret": true
}
}
},
{
"type": "object",
"title": "API Key",
"required": ["auth_type", "api_key"],
"properties": {
"auth_type": {
"type": "string",
"const": "Apikey",
"enum": ["Apikey"],
"default": "Apikey",
"order": 1
},
"api_key": {
"title": "API key token",
"type": "string",
"description": "The API key for a Square application",
"airbyte_secret": true
}
}
}
]
}
}
midavadim marked this conversation as resolved.
Show resolved Hide resolved
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,12 @@

def test_source_wrong_credentials():
source = SourceSquare()
status, error = source.check_connection(logger=AirbyteLogger(), config={"api_key": "wrong.api.key", "is_sandbox": True})
config = {
"authorization": {"auth_type": "Apikey", "api_key": "bla"},
"is_sandbox": True,
"start_date": "2021-06-01",
"include_deleted_objects": False,
}
status, error = source.check_connection(logger=AirbyteLogger(), config=config)
assert not status

1 change: 1 addition & 0 deletions tools/bin/ci_credentials.sh
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ write_standard_creds source-smartsheets "$SMARTSHEETS_TEST_CREDS"
write_standard_creds source-snapchat-marketing "$SOURCE_SNAPCHAT_MARKETING_CREDS"
write_standard_creds source-snowflake "$SNOWFLAKE_INTEGRATION_TEST_CREDS" "config.json"
write_standard_creds source-square "$SOURCE_SQUARE_CREDS"
write_standard_creds source-square "$SOURCE_SQUARE_CREDS_OAUTH" "config_oauth.json"
write_standard_creds source-stripe "$SOURCE_STRIPE_CREDS"
write_standard_creds source-stripe "$STRIPE_INTEGRATION_CONNECTED_ACCOUNT_TEST_CREDS" "connected_account_config.json"
write_standard_creds source-surveymonkey "$SURVEYMONKEY_TEST_CREDS"
Expand Down