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

🎉 New Source: Retently #6966

Merged
merged 6 commits into from
Nov 2, 2021
Merged

Conversation

subhash-kloudio
Copy link
Contributor

@subhash-kloudio subhash-kloudio commented Oct 12, 2021

What

  • Source connector for Retently
  • Syncs customers, companies and reports from the SaaS application

How

  • HTTP API based source connector

Recommended reading order

  1. x.java
  2. y.python

Pre-merge Checklist

  • Community member? Grant edit access to maintainers (instructions)
  • Secrets in the connector's spec are annotated with airbyte_secret
  • Unit & integration tests added and passing. Community members, please provide proof of success locally e.g: screenshot or copy-paste unit, integration, and acceptance test output. To run acceptance tests for a Python connector, follow instructions in the README. For java connectors run ./gradlew :airbyte-integrations:connectors:<name>:integrationTest.
  • Code reviews completed
  • Documentation updated
    • Connector's README.md
    • Connector's bootstrap.md. See description and examples
    • docs/SUMMARY.md
    • docs/integrations/<source or destination>/<name>.md including changelog. See changelog example
    • docs/integrations/README.md
    • airbyte-integrations/builds.md
  • PR name follows PR naming conventions
  • Connector added to connector index like described here

@CLAassistant
Copy link

CLAassistant commented Oct 12, 2021

CLA assistant check
All committers have signed the CLA.

@github-actions github-actions bot added the area/connectors Connector related issues label Oct 12, 2021
@subhash-kloudio
Copy link
Contributor Author

Fixes airbytehq/connector-contest#66

@avaidyanatha avaidyanatha changed the title Source Retently: new connector 🎉 New Source: Retently Oct 12, 2021
@marcosmarxm
Copy link
Member

Thanks for your contribution! We're going to review this tomorrow max by Friday. Can you share some screenshots showing the integratinos tests are passing?

@sherifnada sherifnada added this to the Connectors 2021-10-29 milestone Oct 14, 2021
@subhash-kloudio
Copy link
Contributor Author

Sure!

Unit tests:

(.venv) (base) subhash@Subhashs-MacBook-Pro source-retently % python -m pytest unit_tests
Test session starts (platform: darwin, Python 3.8.5, pytest 6.2.5, pytest-sugar 0.9.4)
cachedir: .pytest_cache
rootdir: /Users/subhash/kloudio/airbyte, configfile: pytest.ini
plugins: sugar-0.9.4, mock-3.6.1, timeout-1.4.2
collecting ... 
 airbyte-integrations/connectors/source-retently/unit_tests/test_source.py::test_check_connection ✓    8% ▉         
 airbyte-integrations/connectors/source-retently/unit_tests/test_source.py::test_streams ✓            17% █▋        
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_request_params ✓    25% ██▌       
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_next_page_token ✓   33% ███▍      
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_parse_response ✓    42% ████▎     
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_request_headers ✓   50% █████     
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_http_method ✓       58% █████▉    
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_should_retry[HTTPStatus.OK-False] ✓67% ██████▋   
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_should_retry[HTTPStatus.BAD_REQUEST-False] ✓75% ███████▌  
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_should_retry[HTTPStatus.TOO_MANY_REQUESTS-True] ✓83% ████████▍ 
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_should_retry[HTTPStatus.INTERNAL_SERVER_ERROR-True] ✓92% █████████▎
 airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py::test_backoff_time ✓     100% ██████████
================================================= warnings summary =================================================
airbyte-integrations/connectors/source-retently/unit_tests/test_source.py::test_streams
  /Users/subhash/kloudio/airbyte/airbyte-integrations/connectors/source-retently/source_retently/source.py:26: DeprecationWarning: Call to deprecated class TokenAuthenticator. (Use airbyte_cdk.sources.streams.http.requests_native_auth.TokenAuthenticator instead) -- Deprecated since version 0.1.20.
    auth = TokenAuthenticator(token, auth_method=auth_method)

airbyte-integrations/connectors/source-retently/unit_tests/test_source.py: 4 warnings
airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py: 10 warnings
  /Users/subhash/kloudio/elt/airbyte/connectors/source-retently/.venv/lib/python3.8/site-packages/deprecated/classic.py:173: DeprecationWarning: Call to deprecated class HttpAuthenticator. (Use requests.auth.AuthBase instead) -- Deprecated since version 0.1.20.
    return old_new1(cls, *args, **kwargs)

airbyte-integrations/connectors/source-retently/unit_tests/test_source.py: 3 warnings
airbyte-integrations/connectors/source-retently/unit_tests/test_streams.py: 10 warnings
  /Users/subhash/kloudio/elt/airbyte/connectors/source-retently/.venv/lib/python3.8/site-packages/airbyte_cdk/sources/streams/http/http.py:37: DeprecationWarning: Call to deprecated class NoAuth. (Set `authenticator=None` instead) -- Deprecated since version 0.1.20.
    self._authenticator = NoAuth()

-- Docs: https://docs.pytest.org/en/stable/warnings.html

Results (0.42s):
      12 passed

Integration tests:

(.venv) (base) subhash@Subhashs-MacBook-Pro source-retently % python -m pytest integration_tests -p integration_tests.acceptance
Test session starts (platform: darwin, Python 3.8.5, pytest 6.2.5, pytest-sugar 0.9.4)
cachedir: .pytest_cache
rootdir: /Users/subhash/kloudio/airbyte, configfile: pytest.ini
plugins: sugar-0.9.4, mock-3.6.1, timeout-1.4.2
collecting ... 
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestSpec.test_match_expected[inputs0] ✓9% ▉         
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestSpec.test_required[inputs0] ✓18% █▊        
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestSpec.test_optional[inputs0] ✓27% ██▊       
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestSpec.test_has_secret[inputs0] ✓36% ███▋      
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestSpec.test_secret_never_in_the_output[inputs0] ✓45% ████▋     
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestSpec.test_oauth_flow_parameters[inputs0] ✓55% █████▌    
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestConnection.test_check[inputs0] ✓64% ██████▍   
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestConnection.test_check[inputs1] ✓73% ███████▍  
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestDiscovery.test_discover[inputs0] ✓82% ████████▎ 
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestDiscovery.test_defined_cursors_exist_in_schema[inputs0] ✓91% █████████▏
 airbyte-integrations/bases/source-acceptance-test/source_acceptance_test/tests/test_core.py::TestBasicRead.test_read[inputs0] ✓100% ██████████
============================================= short test summary info ==============================================
SKIPPED [1] ../../bases/source-acceptance-test/source_acceptance_test/plugin.py:56: Skipping TestFullRefresh.test_sequential_reads because not found in the config
SKIPPED [1] ../../bases/source-acceptance-test/source_acceptance_test/plugin.py:56: Skipping TestIncremental.test_two_sequential_reads because not found in the config

Results (10.18s):
      11 passed

@marcosmarxm
Copy link
Member

Hi @subhash-kloudio we add your contribution to our sprint. Sorry to not review it yet, there are so many connectors from Hacktober.

@Zirochkaa Zirochkaa self-assigned this Oct 21, 2021
auth = TokenAuthenticator(token, auth_method=auth_method)
return [ Customers(auth), Companies(auth), Reports(auth) ]

class Stream(HttpStream):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class Stream(HttpStream):
class RetentlyStream(HttpStream):

Is a common pattern, can you change it?

Comment on lines 15 to 19
api_key = config["api_key"]
if not api_key:
return False, f"API key is missing"
else:
return True, None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better call a try/except block.
See

def check_connection(self, logger: AirbyteLogger, config: Mapping[str, Any]) -> Tuple[bool, Any]:
try:
authenticator = TokenAuthenticator(token=config["access_token"])
start_date = pendulum.parse(config["start_date"])
stream = Surveys(authenticator=authenticator, start_date=start_date)
records = stream.read_records(sync_mode=SyncMode.full_refresh)
next(records)
return True, None
except Exception as e:
return False, repr(e)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and api_key is a required field dont need to check if is null.

Copy link
Member

@marcosmarxm marcosmarxm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sorry for the delay in review! some small changes. I'll try to run tests locally to validate the code. Thanks @subhash-kloudio

@subhash-kloudio
Copy link
Contributor Author

sorry for the delay in review! some small changes. I'll try to run tests locally to validate the code. Thanks @subhash-kloudio

No problem. Comments have been incorporated, thanks

@@ -0,0 +1,15 @@
connector_image: airbyte/source-retently
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
connector_image: airbyte/source-retently
connector_image: airbyte/source-retently:dev

Comment on lines 2 to 3
"username": "foo",
"password": "bar"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the config only use api_key right?

ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]

LABEL io.airbyte.version=0.1.0
LABEL io.airbyte.name=airbyte/source_retently
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
LABEL io.airbyte.name=airbyte/source_retently
LABEL io.airbyte.name=airbyte/source-retently

Comment on lines 1 to 16
## [Quickrun](https://docs.airbyte.io/connector-development/tutorials/cdk-speedrun)

1. `python -m venv .venv`
2. `source .venv/bin/activate`
3. `pip install -r requirements.txt` (Alter path in requirements.txt)
4. `python main.py check --config sample_files/config.json` should report SUCCESS
5. `python main.py check --config sample_files/invalid_config.json` should report FAILED
6. `python main.py discover --config sample_files/config.json` presents schema
7. `python main.py read --config sample_files/config.json --catalog sample_files/configured_catalog.json` runs source
8. `docker build . -t <repo>/source-retently:<n>` builds docker image
10. `docker push <repo>/source-retently:<n>` .. and push
11. Add connector from http://localhost:8000/settings/source
12. `pip install .[tests]`
13. `python -m pytest unit_tests`
14. `python -m pytest integration_tests`
15. `python -m pytest integration_tests -p integration_tests.acceptance`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please keep the README as same from other connectors.

@@ -0,0 +1,3 @@
{
"api_key": "b89fe9a7-d33c-43ab-a215-373c27593a69"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you dont need to let your api key here, change to only an example.

"id": { "type": "string" },
"createdDate": { "type": "string" },
"tags": { "type": "array", "items": { "type": "string" } },
"cxMetrics": { "type": "object" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cxMetrics is not possible to create the object or is dynamic?

"type": "object",
"properties": {
"last": { "type": "object" },
"trend": { "type": "array" },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is not possible to add the nested objecT?
see API example

day" : "2020-01-12",
          "promoters" : 292,
          "passives" : 194,
          "detractors" : 25,
          "total" : 511,
          "score" : 52

Copy link
Member

@marcosmarxm marcosmarxm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments, integration tests are working. After solving the comments we can merge this. Please make sure to resolve all problems when running ./gradlew format there is some libs imported and not used.

Comment on lines 19 to 20
token = ""
auth = TokenAuthenticator(token, auth_method=auth_method)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
token = ""
auth = TokenAuthenticator(token, auth_method=auth_method)
auth = TokenAuthenticator(token="", auth_method=auth_method)

Comment on lines +42 to +45
@property
@abstractmethod
def json_path(self):
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this function?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is overriden in the specific stream classes to provide the path within the response that holds the stream data. Eg. subscribers for Customers

Comment on lines 29 to 32
def test_check_connection(mocker):
source = SourceRetently()
logger_mock, config_mock = MagicMock(), MagicMock()
assert source.check_connection(logger_mock, config_mock) == (True, None)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not working.

       >       assert source.check_connection(logger_mock, config_mock) == (True, None)
         E       assert (False,\n "HTTPError('401 Client Error: Unauthorized for url: "\n "https://app.retently.com/api/v2/companies')") == (True, None)
         E         At index 0 diff: False != True
         E         Full diff:
         E           (
         E         -  True,
         E         -  None,
         E         +  False,
         E         +  "HTTPError('401 Client Error: Unauthorized for url: "
         E         +  "https://app.retently.com/api/v2/companies')",
         E           )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@subhash-kloudio
Copy link
Contributor Author

Some comments, integration tests are working. After solving the comments we can merge this. Please make sure to resolve all problems when running ./gradlew format there is some libs imported and not used.

Thanks.

  1. All comments have been addressed
  2. Tests are working
  3. ./gradlew format - This comment is pending. I am not sure how to go about this. Please see error below. I am hoping you can guide me to get this to work
% gradle wrapper format --warning-mode all
Building all of Airbyte.
/Users/subhash/kloudio/airbyte/airbyte-integrations/connectors

> Configure project :buildSrc
The RepositoryHandler.jcenter() method has been deprecated. This is scheduled to be removed in Gradle 8.0. JFrog announced JCenter's sunset in February 2021. Use mavenCentral() instead. Consult the upgrading guide for further information: https://docs.gradle.org/7.2/userguide/upgrading_version_6.html#jcenter_deprecation
        at build_ag0ycgcg86xuhdfhg2sqeexnc$_run_closure1.doCall(/Users/subhash/kloudio/airbyte/buildSrc/build.gradle:6)
        (Run with --stacktrace to get the full stack trace of this deprecation warning.)

> Task :buildSrc:jar FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':buildSrc:jar'.
> Entry AirbytePythonPlugin.class is a duplicate but no duplicate handling strategy has been set. Please refer to https://docs.gradle.org/7.2/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 612ms
8 actionable tasks: 1 executed, 7 up-to-date

@marcosmarxm
Copy link
Member

Some comments, integration tests are working. After solving the comments we can merge this. Please make sure to resolve all problems when running ./gradlew format there is some libs imported and not used.

Thanks.

  1. All comments have been addressed
  2. Tests are working
  3. ./gradlew format - This comment is pending. I am not sure how to go about this. Please see error below. I am hoping you can guide me to get this to work
% gradle wrapper format --warning-mode all
Building all of Airbyte.
/Users/subhash/kloudio/airbyte/airbyte-integrations/connectors

> Configure project :buildSrc
The RepositoryHandler.jcenter() method has been deprecated. This is scheduled to be removed in Gradle 8.0. JFrog announced JCenter's sunset in February 2021. Use mavenCentral() instead. Consult the upgrading guide for further information: https://docs.gradle.org/7.2/userguide/upgrading_version_6.html#jcenter_deprecation
        at build_ag0ycgcg86xuhdfhg2sqeexnc$_run_closure1.doCall(/Users/subhash/kloudio/airbyte/buildSrc/build.gradle:6)
        (Run with --stacktrace to get the full stack trace of this deprecation warning.)

> Task :buildSrc:jar FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':buildSrc:jar'.
> Entry AirbytePythonPlugin.class is a duplicate but no duplicate handling strategy has been set. Please refer to https://docs.gradle.org/7.2/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy for details.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 612ms
8 actionable tasks: 1 executed, 7 up-to-date

You need to run ./gradlew format in the root folder

@subhash-kloudio
Copy link
Contributor Author

You need to run ./gradlew format in the root folder

Done! And reported problems fixed!

Please let me know if there's anything else @marcosmarxm

@sherifnada sherifnada removed this from the Connectors 2021-10-29 milestone Oct 29, 2021
Copy link
Member

@marcosmarxm marcosmarxm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Great work @subhash-kloudio

@marcosmarxm marcosmarxm merged commit 63d6301 into airbytehq:master Nov 2, 2021
lmossman pushed a commit that referenced this pull request Nov 3, 2021
* retently

* PR comments

* PR comments

* mask api keys

* README

* keep gradle happy
schlattk pushed a commit to schlattk/airbyte that referenced this pull request Jan 4, 2022
* retently

* PR comments

* PR comments

* mask api keys

* README

* keep gradle happy
@sajarin sajarin added internal and removed bounty labels Oct 24, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants