Skip to content

Commit

Permalink
Lever hiring oauth backend (#7677)
Browse files Browse the repository at this point in the history
* Lever OAuth Initial Impl

* Unit test fixes for Lever OAuth

* lever oauth fix post merge

* Autoformatting Lever OAuth Flow

* Use URIBuilder for LeverOAuthFlow

* Lever OAuth, python code bugs, modernizing java portion to account for new class hierarchies

* Fix end to end oauth flow

* Connector based input configuration

* Fix lever unit test

* Post master merge fixes

* Update GithubOAuthFlowIntegrationTest.java

* Delete useless LeverOAuthFlowIntegrationTest

* Remove obsolete python class spec

* SAT fixes, bump versions, update docs

* flake8 fix

* small refactor for clarity

* missing schemas

* Dirty attempt to debug CI build error.

* fix test error

* flake violation

* fix unit test
  • Loading branch information
eliziario committed Dec 21, 2021
1 parent 1c48122 commit 7c0d195
Show file tree
Hide file tree
Showing 12 changed files with 419 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,5 @@ COPY source_lever_hiring ./source_lever_hiring
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]

LABEL io.airbyte.version=0.1.0
LABEL io.airbyte.version=0.1.1
LABEL io.airbyte.name=airbyte/source-lever-hiring
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,133 @@
"documentationUrl": "https://docs.airbyte.io/integrations/sources/lever-hiring",
"changelogUrl": "https://docs.airbyte.io/integrations/sources/lever-hiring#changelog",
"connectionSpecification": {
"title": "Lever Hiring Spec",
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Lever Hiring Source Spec",
"type": "object",
"required": ["start_date"],
"additionalProperties": true,
"properties": {
"client_id": {
"title": "Client Id",
"description": "The client application id as provided when registering the application with Lever.",
"type": "string"
"credentials": {
"order": 3,
"title": "Authentication Mechanism",
"description": "Choose how to authenticate to Lever Hiring.",
"type": "object",
"oneOf": [
{
"type": "object",
"title": "Authenticate via Lever (OAuth)",
"required": ["refresh_token"],
"properties": {
"auth_type": {
"type": "string",
"const": "Client",
"enum": ["Client"],
"default": "Client",
"order": 0
},
"client_id": {
"title": "Client ID",
"type": "string",
"description": "The Client ID of your application"
},
"client_secret": {
"title": "Client Secret",
"type": "string",
"description": "The client secret of your application",
"airbyte_secret": true
},
"option_title": {
"type": "string",
"title": "Credentials Title",
"description": "OAuth Credentials",
"const": "OAuth Credentials"
},
"refresh_token": {
"type": "string",
"title": "Refresh Token",
"description": "OAuth access token",
"airbyte_secret": true
}
}
}
]
},
"client_secret": {
"title": "Client Secret",
"description": "The application secret as provided when registering the application with Lever.",
"airbyte_secret": true,
"type": "string"
},
"refresh_token": {
"title": "Refresh Token",
"description": "The refresh token your application will need to submit to get a new access token after it's expired.",
"type": "string"
"start_date": {
"order": 0,
"type": "string",
"title": "Start Date",
"description": "The date from which you'd like to replicate data for Lever in the format YYYY-MM-DDT00:00:00Z. All data generated after this date will be replicated. Note that it will be used only in the following incremental streams: comments, commits and issues.",
"examples": ["2021-03-01T00:00:00Z"],
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z$"
},
"environment": {
"order": 1,
"type": "string",
"title": "Environment",
"description": "Sandbox or Production environment.",
"default": "Production",
"enum": ["Sandbox", "Production"],
"type": "string"
},
"start_date": {
"title": "Start Date",
"description": "UTC date and time in the format 2019-02-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": ["2021-04-25T00:00:00Z"],
"type": "string"
"description": "The environment in which you'd like to replicate data for Lever. This is used to determine which Lever API endpoint to use.",
"default": "Sandbox",
"enum": ["Production", "Sandbox"]
}
},
"required": ["client_id", "client_secret", "refresh_token", "start_date"]
}
},
"authSpecification": {
"auth_type": "oauth2.0",
"oauth2Specification": {
"oauthFlowInitParameters": [
["client_id"],
["client_secret"],
["refresh_token"]
]
"rootObject": ["credentials", 0],
"oauthFlowInitParameters": [["client_id"], ["client_secret"]],
"oauthFlowOutputParameters": [["refresh_token"]]
}
},
"advanced_auth": {
"auth_flow_type": "oauth2.0",
"predicate_key": ["credentials", "auth_type"],
"predicate_value": "Client",
"oauth_config_specification": {
"oauth_user_input_from_connector_config_specification": {
"type": "object",
"properties": {
"environment": {
"type": "string",
"path_in_connector_config": ["environment"]
}
}
},
"complete_oauth_output_specification": {
"type": "object",
"additionalProperties": false,
"properties": {
"refresh_token": {
"type": "string",
"path_in_connector_config": ["credentials", "refresh_token"]
}
}
},
"complete_oauth_server_input_specification": {
"type": "object",
"additionalProperties": false,
"properties": {
"client_id": {
"type": "string"
},
"client_secret": {
"type": "string"
}
}
},
"complete_oauth_server_output_specification": {
"type": "object",
"additionalProperties": false,
"properties": {
"client_id": {
"type": "string",
"path_in_connector_config": ["credentials", "client_id"]
},
"client_secret": {
"type": "string",
"path_in_connector_config": ["credentials", "client_secret"]
}
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,66 +2,52 @@
# Copyright (c) 2021 Airbyte, Inc., all rights reserved.
#


from typing import Any, List, Mapping, Tuple

from airbyte_cdk.models import AuthSpecification, ConnectorSpecification, OAuth2Specification
from airbyte_cdk.sources import AbstractSource
from airbyte_cdk.sources.streams import Stream
from airbyte_cdk.sources.streams.http.auth import Oauth2Authenticator
from pydantic import Field
from pydantic.main import BaseModel

from .streams import Applications, Interviews, Notes, Offers, Opportunities, Referrals, Users


class ConnectorConfig(BaseModel):
class Config:
title = "Lever Hiring Spec"

client_id: str = Field(
description="The client application id as provided when registering the application with Lever.",
)
client_secret: str = Field(
description="The application secret as provided when registering the application with Lever.",
airbyte_secret=True,
)
refresh_token: str = Field(
description="The refresh token your application will need to submit to get a new access token after it's expired.",
)
environment: str = Field(description="Sandbox or Production environment.", enum=["Sandbox", "Production"], default="Production")
start_date: str = Field(
description="UTC date and time in the format 2019-02-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=["2021-04-25T00:00:00Z"],
def _auth_from_config(config):
return Oauth2Authenticator(
client_id=config["credentials"]["client_id"],
client_secret=config["credentials"]["client_secret"],
refresh_token=config["credentials"]["refresh_token"],
token_refresh_endpoint=f"{SourceLeverHiring.URL_MAP_ACCORDING_ENVIRONMENT[config['environment']]['login']}oauth/token",
)


class SourceLeverHiring(AbstractSource):
URL_MAP_ACCORDING_ENVIRONMENT = {
"Sandbox": {"login": "https://sandbox-lever.auth0.com/", "api": "https://api.sandbox.lever.co/"},
"Production": {"login": "https://auth.lever.co/", "api": "https://api.lever.co/"},
"Sandbox": {
"login": "https://sandbox-lever.auth0.com/",
"api": "https://api.sandbox.lever.co/",
},
"Production": {
"login": "https://auth.lever.co/",
"api": "https://api.lever.co/",
},
}

def check_connection(self, logger, config: Mapping[str, Any]) -> Tuple[bool, any]:
authenticator = Oauth2Authenticator(
token_refresh_endpoint=f"{self.URL_MAP_ACCORDING_ENVIRONMENT[config['environment']]['login']}oauth/token",
client_id=config["client_id"],
client_secret=config["client_secret"],
refresh_token=config["refresh_token"],
)
authenticator = _auth_from_config(config)
_ = authenticator.get_auth_header()
return True, None

def streams(self, config: Mapping[str, Any]) -> List[Stream]:
authenticator = Oauth2Authenticator(
token_refresh_endpoint=f"{self.URL_MAP_ACCORDING_ENVIRONMENT[config['environment']]['login']}oauth/token",
client_id=config["client_id"],
client_secret=config["client_secret"],
refresh_token=config["refresh_token"],
)
full_refresh_params = {"authenticator": authenticator, "base_url": self.URL_MAP_ACCORDING_ENVIRONMENT[config["environment"]]["api"]}
stream_params_with_start_date = {**full_refresh_params, "start_date": config["start_date"]}

authenticator = _auth_from_config(config)
full_refresh_params = {
"authenticator": authenticator,
"base_url": self.URL_MAP_ACCORDING_ENVIRONMENT[config["environment"]]["api"],
}
stream_params_with_start_date = {
**full_refresh_params,
"start_date": config["start_date"],
}
return [
Applications(**stream_params_with_start_date),
Interviews(**stream_params_with_start_date),
Expand All @@ -71,14 +57,3 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]:
Referrals(**stream_params_with_start_date),
Users(**full_refresh_params),
]

def spec(self, *args, **kwargs) -> ConnectorSpecification:
return ConnectorSpecification(
documentationUrl="https://docs.airbyte.io/integrations/sources/lever-hiring",
changelogUrl="https://docs.airbyte.io/integrations/sources/lever-hiring#changelog",
connectionSpecification=ConnectorConfig.schema(),
authSpecification=AuthSpecification(
auth_type="oauth2.0",
oauth2Specification=OAuth2Specification(oauthFlowInitParameters=[["client_id"], ["client_secret"], ["refresh_token"]]),
),
)

0 comments on commit 7c0d195

Please sign in to comment.