custom bamboo hr: init connector
Signed-off-by: Alexis Pentori <alexis@status.im>
This commit is contained in:
parent
77f153e18e
commit
e0df9e7309
|
@ -0,0 +1,8 @@
|
||||||
|
FROM airbyte/python-connector-base:1.1.0
|
||||||
|
|
||||||
|
COPY . ./airbyte/integration_code
|
||||||
|
RUN pip install ./airbyte/integration_code
|
||||||
|
|
||||||
|
# The entrypoint and default env vars are already set in the base image
|
||||||
|
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
|
||||||
|
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]
|
|
@ -0,0 +1,60 @@
|
||||||
|
# BambooHR Fetcher Source
|
||||||
|
|
||||||
|
This is the repository for fetching data from BambooHR API, written in Python.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
This connector fetches employees data in BambooHR`.
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
|
||||||
|
The connector takes the following input:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
token: 'Authentication Token'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Output
|
||||||
|
|
||||||
|
The connector will return the following:
|
||||||
|
- `employees`: List employees.
|
||||||
|
- `employees_details` : Detail of each employees (team, gh name, ...)
|
||||||
|
|
||||||
|
## Local development
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
#### Activate Virtual Environment and install dependencies
|
||||||
|
From this connector directory, create a virtual environment:
|
||||||
|
```
|
||||||
|
python -m venv .venv
|
||||||
|
```
|
||||||
|
```
|
||||||
|
source .venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Locally running the connector
|
||||||
|
```
|
||||||
|
python main.py spec
|
||||||
|
python main.py check --config sample_files/config-example.json
|
||||||
|
python main.py discover --config sample_files/config-example.json
|
||||||
|
python main.py read --config sample_files/config-example.json --catalog sample_files/configured_catalog.json
|
||||||
|
```
|
||||||
|
|
||||||
|
### Locally running the connector docker image
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -t airbyte/twitter-fetcher:dev .
|
||||||
|
# Running the spec command against your patched connector
|
||||||
|
docker run airbyte/twitter-fetcher:dev spec
|
||||||
|
````
|
||||||
|
|
||||||
|
#### Run
|
||||||
|
Then run any of the connector commands as follows:
|
||||||
|
```
|
||||||
|
docker run --rm harbor.status.im/status-im/airbyte/source-custom-bamboohr-hr:dev spec
|
||||||
|
docker run --rm -v $(pwd)/sample_files:/sample_files harbor.status.im/status-im/custom-banboo-hr:dev check --config /sample_files/config-example.json
|
||||||
|
docker run --rm -v $(pwd)/sample_files:/sample_files harbor.status.im/status-im/custom-banboo-hr:dev discover --config /sample_files/config-example.json
|
||||||
|
docker run --rm -v $(pwd)/sample_files:/sample_files -v $(pwd)/sample_files:/sample_files harbor.status.im/status-im/custom-banboo-hr:dev read --config /sample_files/config-example.json --catalog /sample_files/configured_catalog.json
|
||||||
|
```
|
|
@ -0,0 +1,8 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
from source_custom_bamboo_hr.run import run
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
run()
|
|
@ -0,0 +1,32 @@
|
||||||
|
data:
|
||||||
|
allowedHosts:
|
||||||
|
registries:
|
||||||
|
oss:
|
||||||
|
enabled: true
|
||||||
|
cloud:
|
||||||
|
enabled: false
|
||||||
|
remoteRegistries:
|
||||||
|
pypi:
|
||||||
|
enabled: true
|
||||||
|
packageName: airbyte-source-custom-bamboo-hr
|
||||||
|
connectorBuildOptions:
|
||||||
|
# Please update to the latest version of the connector base image.
|
||||||
|
# https://hub.docker.com/r/airbyte/python-connector-base
|
||||||
|
# Please use the full address with sha256 hash to guarantee build reproducibility.
|
||||||
|
baseImage: docker.io/airbyte/python-connector-base:1.2.0@sha256:c22a9d97464b69d6ef01898edf3f8612dc11614f05a84984451dde195f337db9
|
||||||
|
connectorSubtype: api
|
||||||
|
connectorType: source
|
||||||
|
definitionId: a02261a4-413f-46a3-990b-f9260dfda049
|
||||||
|
dockerImageTag: 0.1.0
|
||||||
|
dockerRepository: harbor.status.im/status-im/airbyte/source-custom-bamboo-hr
|
||||||
|
githubIssueLabel: source-custom-bamboo-hr
|
||||||
|
icon: custom-bamboo-hr.svg
|
||||||
|
license: MIT
|
||||||
|
name: Custom Bamboo HR
|
||||||
|
releaseDate: TODO
|
||||||
|
supportLevel: community
|
||||||
|
releaseStage: alpha
|
||||||
|
documentationUrl: https://docs.airbyte.com/integrations/sources/custom-bamboo-hr
|
||||||
|
tags:
|
||||||
|
- language:python
|
||||||
|
metadataSpecVersion: "1.0"
|
|
@ -0,0 +1,28 @@
|
||||||
|
[build-system]
|
||||||
|
requires = [ "poetry-core>=1.0.0",]
|
||||||
|
build-backend = "poetry.core.masonry.api"
|
||||||
|
|
||||||
|
[tool.poetry]
|
||||||
|
version = "0.1.0"
|
||||||
|
name = "source-custom-bamboo-hr"
|
||||||
|
description = "Source implementation for custom-bamboo-hr."
|
||||||
|
authors = [ "Airbyte <contact@airbyte.io>",]
|
||||||
|
license = "MIT"
|
||||||
|
readme = "README.md"
|
||||||
|
documentation = "https://docs.airbyte.com/integrations/sources/custom-bamboo-hr"
|
||||||
|
homepage = "https://airbyte.com"
|
||||||
|
repository = "https://github.com/airbytehq/airbyte"
|
||||||
|
packages = [ { include = "source_custom_bamboo_hr" }, {include = "main.py" } ]
|
||||||
|
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
python = "^3.9,<3.12"
|
||||||
|
airbyte-cdk = "^0"
|
||||||
|
|
||||||
|
[tool.poetry.scripts]
|
||||||
|
source-custom-bamboo-hr = "source_custom_bamboo_hr.run:run"
|
||||||
|
|
||||||
|
[tool.poetry.group.dev.dependencies]
|
||||||
|
requests-mock = "*"
|
||||||
|
pytest-mock = "*"
|
||||||
|
pytest = "*"
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
-e .
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"token": "some-api-token",
|
||||||
|
"organisation": "statusim"
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
{
|
||||||
|
"streams": [
|
||||||
|
{
|
||||||
|
"stream": {
|
||||||
|
"name": "employees",
|
||||||
|
"json_schema": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"supported_sync_modes": [
|
||||||
|
"full_refresh", "incremental"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sync_mode": "incremental",
|
||||||
|
"destination_sync_mode": "overwrite"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"stream": {
|
||||||
|
"name": "employees_details",
|
||||||
|
"json_schema": {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"supported_sync_modes": [
|
||||||
|
"full_refresh", "incremental"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"sync_mode": "incremental",
|
||||||
|
"destination_sync_mode": "overwrite"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
from .source import SourceCustomBambooHr
|
||||||
|
|
||||||
|
__all__ = ["SourceCustomBambooHr"]
|
|
@ -0,0 +1,13 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from airbyte_cdk.entrypoint import launch
|
||||||
|
from .source import SourceCustomBambooHr
|
||||||
|
|
||||||
|
def run():
|
||||||
|
source = SourceCustomBambooHr()
|
||||||
|
launch(source, sys.argv[1:])
|
|
@ -0,0 +1,18 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"firstName": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"lastName": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"division": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"id": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"firstName": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"lastName": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"preferredName": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"displayedName": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"division": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"team": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"department": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"customENSUsername": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"customStatusPublicKey": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"customGitHubusername": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"customDiscordUsername": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"supervisor": {
|
||||||
|
"type": ["null", "string"]
|
||||||
|
},
|
||||||
|
"hireDate":{
|
||||||
|
"type": ["null", "string"],
|
||||||
|
"format": "date-time"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
#
|
||||||
|
# Copyright (c) 2024 Airbyte, Inc., all rights reserved.
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
from abc import ABC
|
||||||
|
from typing import Any, Iterable, List, Mapping, MutableMapping, Optional, Tuple
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import requests
|
||||||
|
from airbyte_cdk.sources import AbstractSource
|
||||||
|
from airbyte_cdk.sources.streams import Stream
|
||||||
|
from airbyte_cdk.sources.streams.http import HttpSubStream, HttpStream
|
||||||
|
from airbyte_cdk.sources.streams.http.auth import BasicHttpAuthenticator
|
||||||
|
|
||||||
|
logger = logging.getLogger("airbyte")
|
||||||
|
|
||||||
|
FIELDS_PARAMS = "id,firstName,lastName,displayedName,division,team,department,customENSUsername,customStatusPublicKey,customGitHubusername,customDiscordUsername,supervision,hireDate"
|
||||||
|
|
||||||
|
class CustomBambooHrStream(HttpStream, ABC):
|
||||||
|
url_base = "https://api.bamboohr.com/api/gateway.php/"
|
||||||
|
|
||||||
|
def __init__(self, organisation: str, **kwargs):
|
||||||
|
super().__init__(**kwargs)
|
||||||
|
self.organisation = organisation
|
||||||
|
|
||||||
|
def next_page_token(self, response: requests.Response) -> Optional[Mapping[str, Any]]:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def request_headers(
|
||||||
|
self, stream_state: Mapping[str, Any], stream_slice: Mapping[str, any] = None, next_page_token: Mapping[str, Any] = None
|
||||||
|
) -> MutableMapping[str, Any]:
|
||||||
|
return { "Accept" : "application/json"}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Employees(CustomBambooHrStream):
|
||||||
|
primary_key= "id"
|
||||||
|
|
||||||
|
|
||||||
|
def path(self, **kwargs) -> str:
|
||||||
|
return f"{self.organisation}/v1/employees/directory"
|
||||||
|
|
||||||
|
def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]:
|
||||||
|
logger.info("Response: %s - %s", response.status_code, response.json())
|
||||||
|
data = response.json()
|
||||||
|
for e in data.get("employees"):
|
||||||
|
yield e
|
||||||
|
|
||||||
|
class EmployeesDetails(HttpSubStream, Employees):
|
||||||
|
primary_key = "id"
|
||||||
|
|
||||||
|
def path(
|
||||||
|
self, stream_state: Mapping[str, Any] = None,
|
||||||
|
stream_slice: Mapping[str, Any] = None,
|
||||||
|
next_page_token: Mapping[str, Any] = None
|
||||||
|
) -> str:
|
||||||
|
employee_id = stream_slice.get("parent").get("id")
|
||||||
|
return f"{self.organisation}/v1/employees/{employee_id}?fields={FIELDS_PARAMS}"
|
||||||
|
|
||||||
|
def parse_response(self, response: requests.Response, **kwargs) -> Iterable[Mapping]:
|
||||||
|
logger.debug("Response: %s",response.json())
|
||||||
|
data = response.json()
|
||||||
|
yield data
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Source
|
||||||
|
class SourceCustomBambooHr(AbstractSource):
|
||||||
|
def check_connection(self, logger, config) -> Tuple[bool, any]:
|
||||||
|
return True, None
|
||||||
|
|
||||||
|
def streams(self, config: Mapping[str, Any]) -> List[Stream]:
|
||||||
|
auth = BasicHttpAuthenticator(username=config["token"], password="x")
|
||||||
|
employees = Employees(organisation=config["organisation"], authenticator=auth)
|
||||||
|
return [
|
||||||
|
employees,
|
||||||
|
EmployeesDetails(organisation=config["organisation"], authenticator=auth, parent=employees)
|
||||||
|
]
|
|
@ -0,0 +1,15 @@
|
||||||
|
documentationUrl: https://docsurl.com
|
||||||
|
connectionSpecification:
|
||||||
|
$schema: http://json-schema.org/draft-07/schema#
|
||||||
|
title: Custom Bamboo Hr Spec
|
||||||
|
type: object
|
||||||
|
required:
|
||||||
|
- token
|
||||||
|
properties:
|
||||||
|
organisation:
|
||||||
|
type: string
|
||||||
|
description: 'describe me'
|
||||||
|
token:
|
||||||
|
type: string
|
||||||
|
description: 'Token for authentication'
|
||||||
|
airbyte_secret: true
|
Loading…
Reference in New Issue