Skip to content

Commit

Permalink
🐛 Source Amazon Seller Partner: fix OAuth (#32550)
Browse files Browse the repository at this point in the history
Co-authored-by: davydov-d <davydov-d@users.noreply.github.com>
  • Loading branch information
davydov-d and davydov-d authored Nov 17, 2023
1 parent 4c6e6a0 commit f8ae6f6
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@

from airbyte_cdk.entrypoint import launch
from source_amazon_seller_partner import SourceAmazonSellerPartner
from source_amazon_seller_partner.config_migrations import MigrateAccountType

if __name__ == "__main__":
source = SourceAmazonSellerPartner()
MigrateAccountType.migrate(sys.argv[1:], source)
launch(source, sys.argv[1:])
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ data:
connectorSubtype: api
connectorType: source
definitionId: e55879a8-0ef8-4557-abcf-ab34c53ec460
dockerImageTag: 2.0.0
dockerImageTag: 2.0.1
dockerRepository: airbyte/source-amazon-seller-partner
documentationUrl: https://docs.airbyte.com/integrations/sources/amazon-seller-partner
githubIssueLabel: source-amazon-seller-partner
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
#


import logging
from typing import Any, List, Mapping

from airbyte_cdk.config_observation import create_connector_config_control_message
from airbyte_cdk.entrypoint import AirbyteEntrypoint
from airbyte_cdk.sources.message import InMemoryMessageRepository, MessageRepository

from .source import SourceAmazonSellerPartner

logger = logging.getLogger("airbyte_logger")


class MigrateAccountType:
"""
This class stands for migrating the config at runtime,
while providing the backward compatibility when falling back to the previous source version.
Specifically, starting from `2.0.1`, the `account_type` property becomes required.
For those connector configs that do not contain this key, the default value of `Seller` will be used.
Reverse operation is not needed as this field is ignored in previous versions of the connector.
"""

message_repository: MessageRepository = InMemoryMessageRepository()
migration_key: str = "account_type"

@classmethod
def _should_migrate(cls, config: Mapping[str, Any]) -> bool:
"""
This method determines whether config requires migration.
Returns:
> True, if the transformation is neccessary
> False, otherwise.
"""
return cls.migration_key not in config

@classmethod
def _populate_with_default_value(cls, config: Mapping[str, Any], source: SourceAmazonSellerPartner = None) -> Mapping[str, Any]:
config[cls.migration_key] = "Seller"
return config

@classmethod
def _modify_and_save(cls, config_path: str, source: SourceAmazonSellerPartner, config: Mapping[str, Any]) -> Mapping[str, Any]:
# modify the config
migrated_config = cls._populate_with_default_value(config, source)
# save the config
source.write_config(migrated_config, config_path)
# return modified config
return migrated_config

@classmethod
def _emit_control_message(cls, migrated_config: Mapping[str, Any]) -> None:
# add the Airbyte Control Message to message repo
cls.message_repository.emit_message(create_connector_config_control_message(migrated_config))
# emit the Airbyte Control Message from message queue to stdout
for message in cls.message_repository.consume_queue():
print(message.json(exclude_unset=True))

@classmethod
def migrate(cls, args: List[str], source: SourceAmazonSellerPartner) -> None:
"""
This method checks the input args, should the config be migrated,
transform if neccessary and emit the CONTROL message.
"""
# get config path
config_path = AirbyteEntrypoint(source).extract_config(args)
# proceed only if `--config` arg is provided
if config_path:
# read the existing config
config = source.read_config(config_path)
# migration check
if cls._should_migrate(config):
cls._emit_control_message(
cls._modify_and_save(config_path, source, config),
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"required": [
"aws_environment",
"region",
"account_type",
"lwa_app_id",
"lwa_client_secret",
"refresh_token",
Expand Down Expand Up @@ -59,33 +60,41 @@
"type": "string",
"order": 2
},
"account_type": {
"title": "AWS Seller Partner Account Type",
"description": "Type of the Account you're going to authorize the Airbyte application by",
"enum": ["Seller", "Vendor"],
"default": "Seller",
"type": "string",
"order": 3
},
"lwa_app_id": {
"title": "LWA Client Id",
"description": "Your Login with Amazon Client ID.",
"order": 3,
"order": 4,
"airbyte_secret": true,
"type": "string"
},
"lwa_client_secret": {
"title": "LWA Client Secret",
"description": "Your Login with Amazon Client Secret.",
"airbyte_secret": true,
"order": 4,
"order": 5,
"type": "string"
},
"refresh_token": {
"title": "Refresh Token",
"description": "The Refresh Token obtained via OAuth flow authorization.",
"airbyte_secret": true,
"order": 5,
"order": 6,
"type": "string"
},
"replication_start_date": {
"title": "Start Date",
"description": "UTC date and time in the format 2017-01-25T00:00:00Z. Any data before this date will not be replicated.",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$",
"examples": ["2017-01-25T00:00:00Z"],
"order": 6,
"order": 7,
"type": "string",
"format": "date-time"
},
Expand All @@ -94,7 +103,7 @@
"description": "UTC date and time in the format 2017-01-25T00:00:00Z. Any data after this date will not be replicated.",
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$|^$",
"examples": ["2017-01-25T00:00:00Z"],
"order": 7,
"order": 8,
"type": "string",
"format": "date-time"
},
Expand All @@ -104,7 +113,7 @@
"description": "Will be used for stream slicing for initial full_refresh sync when no updated state is present for reports that support sliced incremental sync.",
"default": 90,
"minimum": 1,
"order": 8
"order": 9
},
"report_options": {
"title": "Report Options",
Expand All @@ -113,15 +122,15 @@
"{\"GET_BRAND_ANALYTICS_SEARCH_TERMS_REPORT\": {\"reportPeriod\": \"WEEK\"}}",
"{\"GET_SOME_REPORT\": {\"custom\": \"true\"}}"
],
"order": 9,
"order": 10,
"type": "string"
},
"max_wait_seconds": {
"title": "Max wait time for reports (in seconds)",
"description": "Sometimes report can take up to 30 minutes to generate. This will set the limit for how long to wait for a successful report.",
"default": 500,
"examples": ["500", "1980"],
"order": 10,
"order": 11,
"minimum": 1,
"type": "integer"
},
Expand All @@ -132,7 +141,7 @@
"{\"GET_SALES_AND_TRAFFIC_REPORT\": {\"availability_sla_days\": 3}}",
"{\"GET_SOME_REPORT\": {\"custom\": \"true\"}}"
],
"order": 11,
"order": 12,
"type": "string"
}
}
Expand All @@ -142,6 +151,19 @@
"predicate_key": ["auth_type"],
"predicate_value": "oauth2.0",
"oauth_config_specification": {
"oauth_user_input_from_connector_config_specification": {
"type": "object",
"properties": {
"region": {
"type": "string",
"path_in_connector_config": ["region"]
},
"account_type": {
"type": "string",
"path_in_connector_config": ["account_type"]
}
}
},
"complete_oauth_output_specification": {
"type": "object",
"additionalProperties": false,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#
# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
#


import json
from typing import Any, Mapping

from airbyte_cdk.models import OrchestratorType, Type
from airbyte_cdk.sources import Source
from source_amazon_seller_partner.config_migrations import MigrateAccountType
from source_amazon_seller_partner.source import SourceAmazonSellerPartner

CMD = "check"
TEST_NOT_MIGRATED_CONFIG_PATH = "unit_tests/test_migrations/not_migrated_config.json"
TEST_MIGRATED_CONFIG_PATH = "unit_tests/test_migrations/migrated_config.json"
SOURCE: Source = SourceAmazonSellerPartner()


def load_config(config_path: str = TEST_NOT_MIGRATED_CONFIG_PATH) -> Mapping[str, Any]:
with open(config_path, "r") as config:
return json.load(config)


def test_migrate_config(capsys):
config = load_config(TEST_NOT_MIGRATED_CONFIG_PATH)
assert "acount_type" not in config
migration_instance = MigrateAccountType()
migration_instance.migrate([CMD, "--config", TEST_NOT_MIGRATED_CONFIG_PATH], SOURCE)
control_msg = json.loads(capsys.readouterr().out)
assert control_msg["type"] == Type.CONTROL.value
assert control_msg["control"]["type"] == OrchestratorType.CONNECTOR_CONFIG.value
migrated_config = control_msg["control"]["connectorConfig"]["config"]
assert migrated_config["account_type"] == "Seller"


def test_should_not_migrate():
config = load_config(TEST_MIGRATED_CONFIG_PATH)
assert config["account_type"]
migration_instance = MigrateAccountType()
assert not migration_instance._should_migrate(config)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"refresh_token": "refresh_token",
"lwa_app_id": "amzn1.application-oa2-client.lwa_app_id",
"lwa_client_secret": "amzn1.oa2-cs.v1.lwa_client_secret",
"replication_start_date": "2022-09-01T00:00:00Z",
"aws_environment": "PRODUCTION",
"account_type": "Vendor",
"region": "US"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"refresh_token": "refresh_token",
"lwa_app_id": "amzn1.application-oa2-client.lwa_app_id",
"lwa_client_secret": "amzn1.oa2-cs.v1.lwa_client_secret",
"replication_start_date": "2022-09-01T00:00:00Z",
"aws_environment": "PRODUCTION",
"region": "US"
}
36 changes: 28 additions & 8 deletions docs/integrations/sources/amazon-seller-partner.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,39 @@ This page guides you through the process of setting up the Amazon Seller Partner

## Prerequisites

- Amazon Selling Partner account

<!-- env:cloud -->

**For Airbyte Cloud:**

- AWS Environment
- AWS Region
- Granted OAuth access
- Replication Start Date
<!-- /env:cloud -->

<!-- env:oss -->

**For Airbyte Open Source:**

- AWS Environment
- AWS Region
- LWA Client ID (LWA App ID)**
- LWA Client Secret**
- Refresh token**
- Replication Start Date
<!-- /env:oss -->

**not required for Airbyte Cloud
## Setup Guide

## Step 1: Set up Amazon Seller Partner

1. [Register](https://developer-docs.amazon.com/sp-api/docs/registering-your-application) Amazon Seller Partner application.
<!-- env:oss -->

**Airbyte Open Source setup steps**

- [Register](https://developer-docs.amazon.com/sp-api/docs/registering-your-application) Amazon Seller Partner application.
- The application must be published as Amazon does not allow external parties such as Airbyte to access draft applications.
2. [Create](https://docs.aws.amazon.com/general/latest/gr/aws-sec-cred-types.html) IAM user.

<!-- /env:oss -->

## Step 2: Set up the source connector in Airbyte

Expand All @@ -28,7 +47,7 @@ This page guides you through the process of setting up the Amazon Seller Partner
3. On the source setup page, select **Amazon Seller Partner** from the Source type dropdown and enter a name for this connector.
4. Click `Authenticate your account`.
5. Log in and Authorize to your Amazon Seller Partner account.
6. Paste all other data to required fields using your IAM user.
6. Paste all other data to required fields.
7. Click `Set up source`.

**For Airbyte Open Source:**
Expand All @@ -37,7 +56,7 @@ This page guides you through the process of setting up the Amazon Seller Partner
2. Go to local Airbyte page.
3. In the left navigation bar, click **Sources**. In the top-right corner, click **+ new source**.
4. On the Set up the source page, enter the name for the Amazon Seller Partner connector and select **Amazon Seller Partner** from the Source type dropdown.
5. Paste all data to required fields using your IAM user and developer account.
5. Paste all data to required fields.
6. Click `Set up source`.

## Supported sync modes
Expand Down Expand Up @@ -124,6 +143,7 @@ So, for any value that exceeds the limit, the `period_in_days` will be automatic

| Version | Date | Pull Request | Subject |
|:---------|:-----------|:--------------------------------------------------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `2.0.1` | 2023-11-16 | [\#32550](https://github.com/airbytehq/airbyte/pull/32550) | Fix the OAuth flow |
| `2.0.0` | 2023-11-23 | [\#32355](https://github.com/airbytehq/airbyte/pull/32355) | Remove Brand Analytics from Airbyte Cloud, permanently remove deprecated FBA reports |
| `1.6.2` | 2023-11-14 | [\#32508](https://github.com/airbytehq/airbyte/pull/32508) | Do not use AWS signature as it is no longer required by the Amazon API |
| `1.6.1` | 2023-11-13 | [\#32457](https://github.com/airbytehq/airbyte/pull/32457) | Fix report decompression |
Expand Down

0 comments on commit f8ae6f6

Please sign in to comment.