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

[VAN-126440] README added. #87

Merged
merged 8 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
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
249 changes: 247 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,247 @@
# python-sdk
Visier Software Development Kit for Python
# Visier API Python SDK (Beta)
Welcome to the Visier API Python SDK! This SDK provides a convenient way to interact with the Visier API.

**Note: This SDK is currently in beta.**

## Description
For detailed documentation of the Visier API, please visit the [Visier API Documentation](https://docs.visier.com/developer/apis/apis.htm).
The Visier API is divided into five main categories:

- [Authentication](https://docs.visier.com/developer/apis/authentication/swagger/current/index.html): Manage user
authentication and tokens.
- [Administration](https://docs.visier.com/developer/apis/administration/swagger/current/index.html): Manage your tenant
or tenants in Visier.
- [Analytic Model](https://docs.visier.com/developer/apis/analytic-model/swagger/current/index.html): Configure and
manage the Analytic Model.
- [Data In](https://docs.visier.com/developer/apis/data-in/swagger/current/index.html): Upload data to the Visier
Platform.
- [Data Out](https://docs.visier.com/developer/apis/data-out/swagger/current/index.html): Download data from the Visier
Platform.

The Visier API Python SDK consists of five main packages, each corresponding to a specific API category:

- `visier-api-core` - It contains logic for authenticating, configuring, and contains classes to make requests.
This package is required to be installed to use any other package of the SDK.
- `visier-api-administration` - for managing your tenant or tenants in Visier.
- `visier-api-analytic-model` - to configure and manage the Analytic Model.
- `visier-api-data-in` - APIs to load data in to the Visier Platform.
- `visier-api-data-out` - APIs get data out from the Visier Platform.

Each package, except `visier-api-core`, contains API classes which are used to interact with different APIs in the Visier API.

## Installation

Install the packages individually based on the required functionality.
The `visier-api-core` package is a dependency for all other packages and will be installed automatically.

```bash
pip install visier-api-administration
pip install visier-api-analytic-model
pip install visier-api-data-in
pip install visier-api-data-out
```
**Note**: This SDK supports Python 3.8 and above.

## Usage
To use the API, you need to provide the `ApiClient` with a `Configuration` object.
The configuration can be created in three ways:
- From environment variables.
- From dictionary which could be loaded from env file.
- By explicitly setting the configuration parameters.

Configure the environment variables as described below, depending on the type of authentication you want to use:
```env
# Necessary for all types of auth
VISIER_HOST=https://customer-specific.api.visier.io
VISIER_APIKEY='visier-provided-api-key'
VISIER_VANITY='visier-tenant-vanity-name'

# For OAuth 2.0 authentication
VISIER_CLIENT_ID='client-id-from-registration' # OAuth client ID (obtain from Visier app registration)
VISIER_CLIENT_SECRET='client-secret-from-registration' # OAuth client secret (obtain from Visier app registration)

# For 3-legged OAuth 2.0, include the redirect URI along with VISIER_CLIENT_ID and VISIER_CLIENT_SECRET.
# The client will start a local server to handle the callback.
# Register this redirect URI on the Visier platform. The default is http://localhost:5000/oauth2/callback.
VISIER_REDIRECT_URI='http://localhost:5000/oauth2/callback'

# For Basic authentication, provide ONLY the following and leave VISIER_CLIENT_ID and VISIER_CLIENT_SECRET blank:
# For 2-legged OAuth 2.0, provide the following IN ADDITION TO VISIER_CLIENT_ID and VISIER_CLIENT_SECRET
Copy link
Collaborator

Choose a reason for hiding this comment

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

this is good. nice and clear

VISIER_USERNAME='visier-username'
VISIER_PASSWORD='visier-password'
```

After setting the environment variables, you can create a `Configuration` object using the `from_env` method:
```python
from visier_api_core import Configuration

config = Configuration.from_env()
```

Another way is to store environment variables in a .env file and load them using dotenv library.
```python
from dotenv import dotenv_values
from visier_api_core import Configuration

config_dict = dotenv_values(".env")
config = Configuration.from_dict(config_dict)
```

Also, you can explicitly set the configuration parameters. E.g. for 3-legged OAuth 2.0 authentication:

```python
from visier_api_core import Configuration

def get_secret(secret_name):
"""Your secret retrieval logic"""
pass

config = Configuration(
host="https://api.visier.com",
api_key=get_secret("api_key"),
client_id=get_secret("client_id"),
client_secret=get_secret("client_id"),
vanity="your_vanity"
)
```
> **Warning:** Don't store sensitive information in code or in a repository.
> Use environment variables or a secure storage solution.

After you have the configuration object you can create the API client and start using the API.

```python
from visier_api_core import ApiClient, Configuration
from visier_api_data_out import DataQueryApi

config = Configuration.from_env()
client = ApiClient(config)
data_query_api = DataQueryApi(client)
```

You can create the API client using default configuration object.
```python
from visier_api_data_out import DataQueryApi
data_query_api = DataQueryApi()
```
Internally, the configuration object is created using values from environment variables.
You can change the default configuration object using the `Configuration.set_default` method.
By default, this configuration object is used to create the default `ApiClient` object.
The `ApiClient` object also has a `set_default` method. This default `ApiClient` object is used to create API objects implicitly.

```python
from dotenv import dotenv_values
from visier_api_core import Configuration, ApiClient
from visier_api_data_in import DataUploadApi, DataIntakeApi

config_dict = dotenv_values(".env")
config = Configuration.from_dict(config_dict)
Configuration.set_default(config)

# ApiClient will be created using default configuration object
api_client = ApiClient()
data_upload_api = DataUploadApi(api_client)

# ApiClient also has a method to set default ApiClient
ApiClient.set_default(ApiClient(config))

# You can create APIs objects implicitly using default ApiClient object
data_intake_api = DataIntakeApi()
```

Every API method has 3 different ways to be called:

```python
from visier_api_analytic_model import DataModelApi, PropertiesDTO

analytic_object_id = 'Employee'
data_model_api = DataModelApi()

# DTO format
properties = data_model_api.properties(analytic_object_id)

# ApiResponse
api_response = data_model_api.properties_with_http_info(analytic_object_id)
if api_response.status_code == 200:
properties = api_response.data

# RESTResponseType
# If you need to work with raw data, you can use this method.
rest_response = data_model_api.properties_without_preload_content(analytic_object_id)
if rest_response.status == 200:
properties = PropertiesDTO.from_json(rest_response.data.decode())
```

All API DTOs are described in documentation for each API, e.g. [Data Out API DTOs](https://docs.visier.com/developer/apis/data-in/swagger/current/index.html#:~:text=files/%7Bfilename%7D-,Schemas,-Data%20types%20and).
Each DTOs has a method `from_json` to create DTO object from json string.
In some cases, you may need to switch from DTO format to CSV format.
To do this, set the `Accept` header to `text/csv` when creating the `ApiClient` or when making a request.

```python
from visier_api_core import ApiClient
from visier_api_data_out import DataQueryApi, AggregationQueryExecutionDTO

with open('query_examples/aggregate/applicants-source.json') as f:
headcount_json = f.read()
aggr_query_dto = AggregationQueryExecutionDTO.from_json(headcount_json)

# Configure the 'Accept' header to 'text/csv' either in the constructor
# ApiClient(header_name='Accept', header_value='text/csv') or by using the set_default_header method.
# The set_default_header method allows you to add additional headers if needed.
api_client = ApiClient()
api_client.set_default_header('Accept', 'text/csv')

query_api = DataQueryApi(api_client)
response = query_api.aggregate_without_preload_content(aggr_query_dto)

# Passing 'Accept' header to request
query_api = DataQueryApi()
response = query_api.aggregate_without_preload_content(aggr_query_dto, _headers={'Accept': 'text/csv'})

with open('data.csv', mode='w') as f:
f.write(response.data.decode())
```
You can find additional query body examples in the [query_examples](query_examples) directory.

## Error Handling

This Python SDK handles exceptions using custom exception classes derived from `OpenApiException`. Below are the main exception classes and their usage:

- `OpenApiException`: The base exception class for all API exceptions.
- `ApiValueError`: Raised when a function receives an argument with an inappropriate value, such as when an authentication token is not set, or when both `body` and `post_params` are provided.
- `ApiException`: Raised for general HTTP API exceptions. This class is subclassed into:
- `BadRequestException`: Raised for HTTP 400 errors.
- `UnauthorizedException`: Raised for HTTP 401 errors.
- `ForbiddenException`: Raised for HTTP 403 errors.
- `NotFoundException`: Raised for HTTP 404 errors.
- `ServiceException`: Raised for HTTP 500-599 errors.

### Fields in `ApiException`

- `status`: The HTTP status code of the response.
- `reason`: The reason phrase returned by the server.
- `body`: The body of the HTTP response.
- `data`: The data parsed from the HTTP response.
- `headers`: The headers of the HTTP response.

Below is an example of how to handle these exceptions:

```python
from visier_api_core.exceptions import ApiException, BadRequestException, UnauthorizedException
from visier_api_analytic_model import DataModelApi

analytic_object_id = 'Employee'
data_model_api = DataModelApi()

try:
# Requesting properties of an analytic object
properties = data_model_api.properties(analytic_object_id)
except BadRequestException as e:
print(f"Bad request: {e}")
except UnauthorizedException as e:
print(f"Unauthorized: {e}")
except ApiException as e:
print(f"An error occurred: {e}")
```

### More Examples
You can find more examples at [github.com/visier/api-samples](https://github.com/visier/api-samples).
48 changes: 48 additions & 0 deletions query_examples/aggregate/applicants-source.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"query": {
"source": {
"metric": "applicantCount"
},
"axes": [
{
"dimensionLevelSelection": {
"dimension" : {
"name": "Application_Source",
"qualifyingPath": "Applicant"
},
"levelIds": [
"Application_Source"
]
}
},
{
"dimensionLevelSelection": {
"dimension" : {
"name": "Applicant_Stage",
"qualifyingPath": "Applicant"
},
"levelIds": [
"Applicant_Stage"
]
}
}
],
"filters": [
{
"selectionConcept": {
"name": "isActiveApplicant",
"qualifyingPath": "Applicant"
}
}
],
"timeIntervals": {
"fromDateTime": "2020-10-01",
"intervalPeriodType": "MONTH",
"intervalPeriodCount": 3
}
},
"options": {
"zeroVisibility": "ELIMINATE",
"nullVisibility": "ELIMINATE"
}
}
37 changes: 37 additions & 0 deletions query_examples/detail/employee-pay_level.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"source": {
"analyticObject": "Employee"
},
"columns": [
{
"columnDefinition": {
"property": {
"name": "Employee.Full_Name",
"qualifyingPath": "Employee"
}
}
},
{
"columnDefinition": {
"property": {
"name": "Employee.Pay_Level",
"qualifyingPath": "Employee"
}
}
}
],
"filters": [
{
"selectionConcept": {
"name": "isFemale",
"qualifyingPath": "Employee"
}
}
],
"timeInterval": {
"fromDateTime": "2020-10-01",
"intervalPeriodType": "MONTH",
"intervalPeriodCount": 1
}
}

52 changes: 52 additions & 0 deletions query_examples/snapshot/employee-info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"source": {
"metric": "employeeCount"
},
"columns": [
{
"columnDefinition": {
"property": {
"name": "Employee.Age",
"qualifyingPath": "Employee"
}
}
},
{
"columnDefinition": {
"property": {
"name": "Employee.Start_Date",
"qualifyingPath": "Employee"
}
}
},
{
"columnDefinition": {
"property": {
"name": "Employee.Market_Compensation_Ratio",
"qualifyingPath": "Employee"
}
}
},
{
"columnDefinition": {
"property": {
"name": "Employee.Full_Name",
"qualifyingPath": "Employee"
}
}
},
{
"columnName": "Effective time",
"columnDefinition": {
"effectiveDateProperty": {}
}
}
],
"timeIntervals": {
"fromDateTime": "2022-01-01",
"intervalPeriodType": "MONTH",
"intervalPeriodCount": 6,
"intervalCount": 4
}
}

Loading