Skip to content

Commit

Permalink
Merge pull request #87 from skyflowapi/SK-865-development-delete-by-s…
Browse files Browse the repository at this point in the history
…kyflow-id-in-python-sdk

SK-865 development delete by skyflow id in python sdk
  • Loading branch information
skyflow-lipsa authored Jul 21, 2023
2 parents 1f4972f + 8e28e8a commit 4d0a5e4
Show file tree
Hide file tree
Showing 7 changed files with 398 additions and 2 deletions.
40 changes: 40 additions & 0 deletions samples/delete_by_id_sample.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
'''
Copyright (c) 2022 Skyflow, Inc.
'''
from skyflow.errors import SkyflowError
from skyflow.service_account import generate_bearer_token, is_expired
from skyflow.vault import Client, Configuration,DeleteOptions


# cache token for reuse
bearerToken = ''


def token_provider():
global bearerToken
if is_expired(bearerToken):
bearerToken, _ = generate_bearer_token('<YOUR_CREDENTIALS_FILE_PATH>')
return bearerToken


try:
config = Configuration(
'<YOUR_VAULT_ID>', '<YOUR_VAULT_URL>', token_provider)
client = Client(config)
options = DeleteOptions(False)

data = {"records": [
{
"id": "<SKYFLOW_ID1>",
"table": "<TABLE_NAME>",
},
{
"id": "<SKYFLOW_ID2>",
"table": "<TABLE_NAME>",
}
]}

response = client.delete_by_id(data,options=options)
print('Response:', response)
except SkyflowError as e:
print('Error Occurred:', e)
3 changes: 3 additions & 0 deletions skyflow/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ class InfoMessages(Enum):
UPDATE_DATA_SUCCESS = "Data has been updated successfully"
GET_TRIGGERED = "Get triggered."
GET_SUCCESS = "Data fetched successfully."
DELETE_BY_ID_TRIGGERED = "Delete by ID triggered."
DELETE_DATA_SUCCESS = "Data has been deleted successfully."


class InterfaceName(Enum):
Expand All @@ -89,6 +91,7 @@ class InterfaceName(Enum):

IS_TOKEN_VALID = "service_account.isTokenValid"
IS_EXPIRED = "service_account.is_expired"
DELETE_BY_ID = "client.delete_by_id"


def http_build_query(data):
Expand Down
8 changes: 8 additions & 0 deletions skyflow/errors/_skyflow_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

class SkyflowErrorCodes(Enum):
INVALID_INPUT = 400
INVALID_INDEX = 404
SERVER_ERROR = 500
PARTIAL_SUCCESS = 500

Expand Down Expand Up @@ -43,11 +44,18 @@ class SkyflowErrorMessages(Enum):
INVALID_RECORDS_TYPE = "Records key has value of type %s, expected list"
INVALID_FIELDS_TYPE = "Fields key has value of type %s, expected dict"
INVALID_TABLE_TYPE = "Table key has value of type %s, expected string"
INVALID_TABLE_TYPE_DELETE = "Table of type string is required at index %s in records array"
INVALID_IDS_TYPE = "Ids key has value of type %s, expected list"
INVALID_ID_TYPE = "Id key has value of type %s, expected string"
INVALID_ID_TYPE_DELETE = "Id of type string is required at index %s in records array"
INVALID_REDACTION_TYPE = "Redaction key has value of type %s, expected Skyflow.Redaction"
INVALID_COLUMN_NAME = "Column name has value of type %s, expected string"
INVALID_COLUMN_VALUE = "Column values has value of type %s, expected list"
INVALID_RECORDS_IN_DELETE = "Invalid records. records object should be an array"
EMPTY_RECORDS_IN_DELETE = "records array cannot be empty"
EMPTY_ID_IN_DELETE = "Id cannot be empty in records array at index %s"
EMPTY_TABLE_IN_DELETE = "Table cannot be empty in records array at index %s"
RECORDS_KEY_NOT_FOUND_DELETE = "records object is required"

INVALID_REQUEST_BODY = "Given request body is not valid"
INVALID_RESPONSE_BODY = "Given response body is not valid"
Expand Down
87 changes: 85 additions & 2 deletions skyflow/vault/_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
import json
import types
import requests

from ._delete_by_id import deleteProcessResponse
from ._insert import getInsertRequestBody, processResponse, convertResponse
from ._update import sendUpdateRequests, createUpdateResponseBody
from ._config import Configuration
from ._config import Configuration, DeleteOptions
from ._config import InsertOptions, ConnectionConfig, UpdateOptions
from ._connection import createRequest
from ._detokenize import sendDetokenizeRequests, createDetokenizeResponseBody
Expand Down Expand Up @@ -172,4 +174,85 @@ def update(self, updateInput, options: UpdateOptions = UpdateOptions()):
SkyflowErrorMessages.PARTIAL_SUCCESS, result, interface=interface)
else:
log_info(InfoMessages.UPDATE_DATA_SUCCESS.value, interface)
return result
return result

def delete_by_id(self, records: dict,options: DeleteOptions = DeleteOptions()):
interface = InterfaceName.DELETE_BY_ID.value
log_info(InfoMessages.DELETE_BY_ID_TRIGGERED.value, interface=interface)

self._checkConfig(interface)

self.storedToken = tokenProviderWrapper(
self.storedToken, self.tokenProvider, interface)
headers = {
"Authorization": "Bearer " + self.storedToken,
"sky-metadata": json.dumps(getMetrics())
}
error_list = []
result_list = []
errors = {}
result = {}
error = {}
try:
if not isinstance(records, dict) or "records" not in records:
error = {"error": {"code": SkyflowErrorCodes.INVALID_INPUT.value,
"description": SkyflowErrorMessages.RECORDS_KEY_NOT_FOUND_DELETE.value}}
return error
records_list = records["records"]
if not isinstance(records_list, list):
error.update({"error": {"code": SkyflowErrorCodes.INVALID_INPUT.value,
"description": SkyflowErrorMessages.INVALID_RECORDS_IN_DELETE.value}})
return error
elif len(records_list) == 0:
error = {"error": {"code": SkyflowErrorCodes.INVALID_INPUT.value,
"description": SkyflowErrorMessages.EMPTY_RECORDS_IN_DELETE.value}}
return error
except KeyError:
raise SkyflowError(SkyflowErrorCodes.INVALID_INPUT,
SkyflowErrorMessages.RECORDS_KEY_ERROR, interface=interface)
try:
for index,record in enumerate(records["records"]):
record_list = record["id"]
if not isinstance(record_list, str):
error.update({"error": {"code": SkyflowErrorCodes.INVALID_INDEX.value,
"description": SkyflowErrorMessages.INVALID_ID_TYPE_DELETE.value % (index)}})
return error
elif record_list == "":
error.update({"error": {"code": SkyflowErrorCodes.INVALID_INPUT.value,
"description": SkyflowErrorMessages.EMPTY_ID_IN_DELETE.value % (index)}})
return error
except KeyError:
error.update({"error": {"code": SkyflowErrorCodes.INVALID_INDEX.value,
"description": SkyflowErrorMessages.IDS_KEY_ERROR.value}})
return error
try:
for index,record in enumerate(records["records"]):
record_table = record["table"]
if not isinstance(record_table, str):
error.update({"error": {"code": SkyflowErrorCodes.INVALID_INPUT.value,
"description": SkyflowErrorMessages.INVALID_TABLE_TYPE_DELETE.value % (index)}})
return error
elif record_table == "":
error.update({"error": {"code": SkyflowErrorCodes.INVALID_INPUT.value,
"description": SkyflowErrorMessages.EMPTY_TABLE_IN_DELETE.value % (index)}})
return error
except KeyError:
error.update({"error": {"code": SkyflowErrorCodes.INVALID_INDEX.value,
"description": SkyflowErrorMessages.TABLE_KEY_ERROR.value}})
return error
for record in records["records"]:
request_url = self._get_complete_vault_url() + "/" + record["table"] + "/" + record["id"]
response = requests.delete(request_url, headers=headers)
processed_response = deleteProcessResponse(response, records)
if processed_response is not None and processed_response.get('code') == 404:
errors.update({'id': record["id"], 'error': processed_response})
error_list.append(errors)
else:
result_list.append(processed_response)
if result_list:
result.update({'records': result_list})
if errors:
result.update({'errors': error_list})

log_info(InfoMessages.DELETE_DATA_SUCCESS.value, interface)
return result
4 changes: 4 additions & 0 deletions skyflow/vault/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ class UpdateOptions:
def __init__(self, tokens: bool=True):
self.tokens = tokens

class DeleteOptions:
def __init__(self, tokens: bool=False):
self.tokens = tokens

class RequestMethod(Enum):
GET = 'GET'
POST = 'POST'
Expand Down
41 changes: 41 additions & 0 deletions skyflow/vault/_delete_by_id.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'''
Copyright (c) 2022 Skyflow, Inc.
'''
import json

import requests
from requests.models import HTTPError
from skyflow.errors._skyflow_errors import SkyflowError, SkyflowErrorCodes, SkyflowErrorMessages
from skyflow._utils import InterfaceName

interface = InterfaceName.DELETE_BY_ID.value


def deleteProcessResponse(response: requests.Response, interface=interface):
statusCode = response.status_code
content = response.content
try:
response.raise_for_status()
if statusCode == 204:
return None
try:
return json.loads(content)
except:
raise SkyflowError(
statusCode, SkyflowErrorMessages.RESPONSE_NOT_JSON.value % content, interface=interface)
except HTTPError:
message = SkyflowErrorMessages.API_ERROR.value % statusCode
if response is not None and response.content is not None:
try:
errorResponse = json.loads(content)
if 'error' in errorResponse and type(errorResponse['error']) == dict and 'message' in errorResponse[
'error']:
message = errorResponse['error']['message']
except:
message = SkyflowErrorMessages.RESPONSE_NOT_JSON.value % content
error = {}
if 'x-request-id' in response.headers:
message += ' - request id: ' + response.headers['x-request-id']
error.update({"code": statusCode, "description": message})
return error

Loading

0 comments on commit 4d0a5e4

Please sign in to comment.