The List Data API enables the user to publish and query tabular data sets (lists) in a low-latency, key-value data store. The API includes the ability to create, read, update, and delete a list and control access through authorization rules.
SAS products that integrate the capabilities of the List Data API refer to the functionality as Advanced Lists. These tabular data sets, called lists, can have multiple keys and values that can be shared among other SAS applications and workflows.
IMPORTANT: In order to use the List Data API, you must provide, configure, and maintain your own Redis instance for the List Data service.
For information about configuring the List Data service for Redis, see sas.listData.redis.
See also:
This use case shows how to use the List Data API endpoints to create and manage a list for employee information, and it covers the following scenarios:
- Creating a new list definition for employee data
- Retrieving all lists or a single list by listId
- Updating the list's description and label
- Importing data from a CSV file
- Getting the status of an import job
- Changing the list's state
- Adding, updating, or deleting list contents
- Submitting a job to purge a list's contents
- Getting the status of a purge job
- Delete the list definition
This example shows how to send a POST request to the /lists
endpoint.
In this example, you specify the following metadata for the new list:
- 'name' (must be unique across all list definitions)
- 'state' (must be 'deployed' or 'developing')
- 'description' (optional)
- 'isImmutable' ('true' or 'false' to indicate whether the contents can be modified after you create the list. The default is 'false'.)
- 'label' (optional)
You initially create the list definition by specifying values for the minimally required properties. Your list definition looks like this:
{
"name": "ACME Corp Employees",
"state": "developing",
}
Next, create a collection of column information objects that describe the list contents. At a minimum, you need the following properties for each column:
- 'name' (must match the column name in the CSV data)
- 'dataType' (must be either 'number' or 'string')
- 'position' (must begin with '1')
In addition, identify at least one column as a unique key. You can create a composite key by combining multiple columns. In this case, set 'isKey' to 'true' for each column that is part of the key and set the 'keyPosition' value to indicate the order in which the columns should be combined. In this example, 'employeeId' is your key, so the 'ColumnInfo' object for 'employeeId' includes the 'isKey' and 'keyPosition' properties.
Here is a small sample of your data:
employeeId,firstName,lastName,email,phoneNumber,hireDate,jobId,salary,commissionPct,managerId,departmentId
100,Steven,King,SKING,515-123-4567,17-JUN-03,AD_PRES,24000,0,0,90
101,Neena,Kochhar,NKOCHHAR,515-123-4568,21-SEP-05,AD_VP,17000,0,100,90
102,Lex,De Haan,LDEHAAN,515-123-4569,13-JAN-01,AD_VP,17000,0,100,90
Your list definition looks like this after you have added the 'ColumnInfo' objects:
{
"name": "ACME Corp Employees",
"state": "developing",
"columns": [
{
"name": "employeeId",
"dataType": "number",
"position": 1,
"isKey": true,
"keyPosition": 1
},
{
"name": "firstName",
"dataType": "string",
"position": 2
},
{
"name": "lastName",
"dataType": "string",
"position": 3
},
{
"name": "email",
"dataType": "string",
"position": 4
},
{
"name": "phoneNumber",
"dataType": "string",
"position": 5
},
{
"name": "hireDate",
"dataType": "string",
"position": 6
},
{
"name": "jobId",
"dataType": "string",
"position": 7
},
{
"name": "salary",
"dataType": "number",
"position": 8
},
{
"name": "commissionPct",
"dataType": "number",
"position": 9
},
{
"name": "managerId",
"dataType": "number",
"position": 10
},
{
"name": "departmentId",
"dataType": "number",
"position": 11
}
]
}
Here is an example of how to send a POST request to the /lists
endpoint in Python. The list definition that you created above has been saved to employeesListDefinition.json.
import requests
import json
from requests.structures import CaseInsensitiveDict
# Set the headers. The actual authorization token is not shown here.
headers = CaseInsensitiveDict()
headers["Accept"] = "application/vnd.sas.listdata.list+json"
headers["Content-Type"] = "application/vnd.sas.listdata.list+json"
headers["Authorization"] = f"Bearer {sasdemo_token}"
# Get the list definition from the JSON file.
f = open("employeesListDefinition.json")
employeeList = json.dumps(json.load(f), indent=2)
f.close()
url = "https://myserver:443/listData/lists"
response = requests.post(url, headers=headers, data=employeeList)
# A "create" should return HTTP 201 (Created).
assert response.status_code == 201, f"\n{json.dumps(response.json(), indent=2)}"
# Save the 'listId' for later.
response_body = response.json()
createdListId = response_body['id']
Notice that the JSON response body that is returned contains more information than what you provided in the request body.
-
Additional metadata:
- 'id'- the unique ID created by the ListData service that is used when referring to the list in GET, PUT, or DELETE requests
- 'version' - the version of the vnd.sas.listdata.list schema
- 'creationTimeStamp' - the timestamp for when the list was created
- 'createdBy' - the name of the user who created the list
- 'modifiedTimeStamp' - the timestamp for when the list was most recently modified
- 'modifiedBy' - the name of the user who last modified the list
-
Properties not included in the request body that used their default values:
- 'description'
- 'isImmutable'
- 'label'
-
Links:
- used for further interaction with the list that you created
{
"id": "6c757bb0-29d5-4118-8ad7-04e5cfda3992",
"version": 1,
"creationTimeStamp": "2022-12-11T12:40:06.794857653Z",
"modifiedTimeStamp": "2022-12-11T12:40:06.794857653Z",
"createdBy": "sasdemo",
"modifiedBy": "sasdemo",
"name": "ACME Corp Employees",
"description": "",
"state": "developing",
"isImmutable": false,
"label": "",
"columns": [
{
"name": "employeeId",
"dataType": "number",
"position": 1,
"isKey": true,
"keyPosition": 1
},
{
"name": "firstName",
"dataType": "string",
"position": 2,
"isKey": false,
"keyPosition": 0
},
{
"name": "lastName",
"dataType": "string",
"position": 3,
"isKey": false,
"keyPosition": 0
},
{
"name": "email",
"dataType": "string",
"position": 4,
"isKey": false,
"keyPosition": 0
},
{
"name": "phoneNumber",
"dataType": "string",
"position": 5,
"isKey": false,
"keyPosition": 0
},
{
"name": "hireDate",
"dataType": "string",
"position": 6,
"isKey": false,
"keyPosition": 0
},
{
"name": "jobId",
"dataType": "string",
"position": 7,
"isKey": false,
"keyPosition": 0
},
{
"name": "salary",
"dataType": "number",
"position": 8,
"isKey": false,
"keyPosition": 0
},
{
"name": "commissionPct",
"dataType": "number",
"position": 9,
"isKey": false,
"keyPosition": 0
},
{
"name": "managerId",
"dataType": "number",
"position": 10,
"isKey": false,
"keyPosition": 0
},
{
"name": "departmentId",
"dataType": "number",
"position": 11,
"isKey": false,
"keyPosition": 0
}
],
"links": [
{
"method": "GET",
"rel": "up",
"href": "/listData/lists",
"uri": "/listData/lists",
"type": "application/vnd.sas.collection",
"itemType": "application/vnd.sas.listdata.list"
},
{
"method": "GET",
"rel": "self",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992",
"type": "application/vnd.sas.listdata.list"
},
{
"method": "PUT",
"rel": "update",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992",
"type": "application/vnd.sas.listdata.list",
"responseType": "application/vnd.sas.listdata.list"
},
{
"method": "GET",
"rel": "state",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/state",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/state",
"type": "text/plain"
},
{
"method": "GET",
"rel": "contents",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/contents",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/contents",
"type": "application/vnd.sas.collection",
"itemType": "application/json"
},
{
"method": "GET",
"rel": "contentsDataset",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/contents",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/contents",
"type": "application/vnd.sas.listdata.dataset"
},
{
"method": "PUT",
"rel": "updateContents",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/contents",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/contents",
"type": "application/vnd.sas.collection",
"responseType": "application/vnd.sas.listdata.list",
"itemType": "application/json"
},
{
"method": "POST",
"rel": "importContents",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/importJobs",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/importJobs",
"type": "multipart/form-data",
"responseType": "application/vnd.sas.listdata.importjob"
},
{
"method": "POST",
"rel": "purgeContents",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/purgeJobs",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/purgeJobs",
"responseType": "application/vnd.sas.listdata.purgejob"
},
{
"method": "DELETE",
"rel": "delete",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992"
},
{
"method": "GET",
"rel": "alternate",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992",
"type": "application/vnd.sas.summary"
},
{
"method": "GET",
"rel": "privilegedContents",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/privilegedContents",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/privilegedContents",
"type": "application/vnd.sas.listdata.list"
},
{
"method": "PUT",
"rel": "privilegedEdit",
"href": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/privilegedEdit",
"uri": "/listData/lists/6c757bb0-29d5-4118-8ad7-04e5cfda3992/privilegedEdit",
"type": "application/vnd.sas.listdata.list",
"responseType": "application/vnd.sas.listdata.list"
}
]
}
To see all lists, you can send a GET request to the /lists
endpoint. A collection of list resources is returned that includes all lists that you are authorized to see. To see a single list, you can send a GET request to the /lists/{listId}
endpoint.
Here is an example:
https://myserver:443/listData/lists
or
https://myserver:443/listData/lists/{listId}
https://myserver:443/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2
Note: Links have been removed for brevity.
{
"createdBy": "sasdemo",
"creationTimeStamp": "2022-12-12T16:39:42.818187Z",
"description": "",
"id": "b3c93e05-2f57-49a9-9780-df55ae0510f2",
"isImmutable": false,
"label": "",
"modifiedBy": "sasdemo",
"modifiedTimeStamp": "2022-12-12T17:49:29.562215Z",
"name": "ACME Corp Employees",
"state": "developing",
"version": 1,
"columns": [
{
"name": "employeeId",
"dataType": "number",
"position": 1,
"isKey": true,
"keyPosition": 1
},
{
"name": "firstName",
"dataType": "string",
"position": 2,
"isKey": false,
"keyPosition": 0
},
{
"name": "lastName",
"dataType": "string",
"position": 3,
"isKey": false,
"keyPosition": 0
},
{
"name": "email",
"dataType": "string",
"position": 4,
"isKey": false,
"keyPosition": 0
},
{
"name": "phoneNumber",
"dataType": "string",
"position": 5,
"isKey": false,
"keyPosition": 0
},
{
"name": "hireDate",
"dataType": "string",
"position": 6,
"isKey": false,
"keyPosition": 0
},
{
"name": "jobId",
"dataType": "string",
"position": 7,
"isKey": false,
"keyPosition": 0
},
{
"name": "salary",
"dataType": "number",
"position": 8,
"isKey": false,
"keyPosition": 0
},
{
"name": "commissionPct",
"dataType": "number",
"position": 9,
"isKey": false,
"keyPosition": 0
},
{
"name": "managerId",
"dataType": "number",
"position": 10,
"isKey": false,
"keyPosition": 0
},
{
"name": "departmentId",
"dataType": "number",
"position": 11,
"isKey": false,
"keyPosition": 0
}
]
}
In this example, use a PUT request to update the properties of a list by sending a JSON object in the body of the request to the /lists/{listId}
endpoint.
You cannot modify the following properties if the list definition has contents:
- 'name'
- 'isImmutable'
- 'columns'
- You cannot add or remove columns.
- 'dataMask' is the only 'ColumnInfo' property that you can modify and you must use the
privilegedEdit
endpoint.
You can update these properties:
- 'description'
- 'label'
- 'state'
- The value can be either 'deployed' or 'developing'.
- You can also use the
/lists/{listId}/state
endpoint.
When you update a list's properties, you need to send only the properties that you are changing in your request body. All other properties, except for the life cycle metadata ('createdBy' and 'modifiedBy'), do not change. In this example, you change the label and add a description to the existing list definition.
Notes:
-
You are using a different user identity than what was used for the create request.
-
You are using the 'listId' value ('createdListId') that was returned in the response from the create request. If you do not know the 'listId' value, you can send a GET to the
/listData/lists
endpoint to retrieve all lists. You can also filter by the list name, as shown in this example:https://myserver/listData/lists?filter=eq(name,'ACME Corp Employees')
-
The HTTP response includes the entire list record, not just the values that were changed.
#!/usr/bin/python3
import argparse
import requests
import json
from requests.structures import CaseInsensitiveDict
parser = argparse.ArgumentParser()
parser.add_argument(
"listId", help="The listId of the list to be deleted.")
args = parser.parse_args()
listId = args.listId
# Set the headers. The actual authorization token is not shown here.
headers = CaseInsensitiveDict()
headers["Accept"] = "application/vnd.sas.listdata.list+json"
headers["Content-Type"] = "application/vnd.sas.listdata.list+json"
headers["Authorization"] = f"Bearer {user1_token}"
url = f"https://myserver:443/listData/lists/{listId}"
# Update values for 'label' and 'description'.
employeeList = {
"label": "Internal Use Only",
"description": "Employee Information"
}
response = requests.put(url, headers=headers, data=json.dumps(employeeList))
response_body = response.json()
if response.status_code != 200:
print("\nRESPONSE:\n", json.dumps(response_body, indent=2))
exit(1)
# Show the response without links or columns.
del response_body['links']
del response_body['columns']
print(f"RESPONSE:\n{json.dumps(response_body, indent=2)}")
- The 'modifiedTimeStamp' and 'modifiedBy' values have been updated.
- The 'description' and 'label' values have been updated.
- The columns and links have been omitted for brevity.
{
"id": "27f48794-25b8-473e-b7f4-15c836f6eca7",
"version": 1,
"creationTimeStamp": "2022-12-11T13:52:03.507741Z",
"modifiedTimeStamp": "2022-12-11T15:10:26.528894209Z",
"createdBy": "sasdemo",
"modifiedBy": "sastest1",
"name": "ACME Corp Employees",
"description": "Employee Information",
"state": "developing",
"isImmutable": false,
"label": "Internal Use Only"
}
Loading data from a CSV file for your list requires that you create an inport job by submitting a POST request to the /lists/{listId}/ImportJobs
endpoint. The POST request must include the CSV file that contains the data and a parameter that specifies the delimiter that is used in the file, which is typically a comma.
This example shows how to send a POST request to the /lists/{listId}/ImportJobs
endpoint by using Python. It accepts both the 'listId' value and the CSV file name as command-line parameters.
#!/usr/bin/python3
import argparse
import requests
import json
from requests.structures import CaseInsensitiveDict
from os.path import exists
# Get the list ID and CSV file from the command-line.
parser = argparse.ArgumentParser()
parser.add_argument(
"listId", help="The listId of the list definition.")
parser.add_argument(
"csvFile", help="The CSV file that contains the data.")
args = parser.parse_args()
listId = args.listId
csvFile = args.csvFile
# Check whether the CSV file exists.
if not exists(csvFile):
raise SystemExit(f"CSV File {csvFile} does not exist.")
# Set headers. The actual authorization token is not shown here.
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Authorization"] = f"Bearer {sasdemo_token}"
# Open the CSV file.
try:
files = {
'dataFile': (csvFile, open(csvFile, 'rb'), 'text/csv')
}
except:
raise SystemExit(f"Unable to open {csvFile}.")
# Set a comma as the delimeter.
data = {
'delimeter': ','
}
# Send a POST to '/lists/{listId}/ImportJobs'.
url = f"https://myserver:443/listData/lists/{listId}/importJobs"
response = requests.post(url, headers=headers, files=files, data=data)
# Show the response.
response_body = response.json()
print(json.dumps(response_body, indent=2))
The job runs asynchronously, and the response returns immediately. If the import job is successfully accepted, HTTP Status 202 (Accepted) is returned with a JSON response that provides the following information about the import job:
- 'id' - the import job ID
- 'state' - the state of the job (running, completed, and so on)
- 'filename' - the name of the file that you submitted with the request
- 'listId' - the ID of the list resource definition that is associated with this job
- 'results' - initially empty
{
"id": "c86bfb89-8f89-403f-a91f-907eead07559",
"version": 1,
"state": "running",
"fileName": "employees.csv",
"sha256Sum": "e8ef753212e4ca360a99f68ff9f6335c72faf5fc7a174920d9e940880690e3d4",
"creationTimeStamp": "2022-12-12T17:06:31.693948Z",
"createdBy": "sasdemo",
"listId": "b3c93e05-2f57-49a9-9780-df55ae0510f2",
"results": {},
"totalErrors": 0,
"errors": [],
"links": [
{
"method": "GET",
"rel": "self",
"href": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/importJobs/c86bfb89-8f89-403f-a91f-907eead07559",
"uri": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/importJobs/c86bfb89-8f89-403f-a91f-907eead07559",
"type": "application/vnd.sas.listdata.importjob"
},
{
"method": "GET",
"rel": "up",
"href": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/importJobs",
"uri": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/importJobs",
"type": "application/vnd.sas.collection",
"itemType": "application/vnd.sas.listdata.importjob"
}
]
}
After you submit the import job, you can retrieve the job's status by sending a GET request to the "self" link that is returned when you submitted the import job. Here is example output:
https://myserver:443/listData/lists/{listId}/importJobs/{importJobId}
Here is the link for your example based on the example output above:
The following example response shows that the import job completed successfully without errors and loaded 50 records.
{
"id":"c86bfb89-8f89-403f-a91f-907eead07559",
"version":1,
"state":"completed",
"fileName":"employees.csv",
"sha256Sum":"e8ef753212e4ca360a99f68ff9f6335c72faf5fc7a174920d9e940880690e3d4",
"creationTimeStamp":"2022-12-12T17:06:31.693948Z",
"completedTimeStamp":"2022-12-12T17:06:31.757776Z",
"createdBy":"sasdemo",
"listId":"b3c93e05-2f57-49a9-9780-df55ae0510f2",
"results":{
"recordCount":50
},
"totalErrors":0,
"errors":[],
"links":[
{
"method":"GET",
"rel":"self",
"href":"/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/importJobs/c86bfb89-8f89-403f-a91f-907eead07559",
"uri":"/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/importJobs/c86bfb89-8f89-403f-a91f-907eead07559",
"type":"application/vnd.sas.listdata.importjob"
},
{
"method":"GET",
"rel":"up",
"href":"/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/importJobs",
"uri":"/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/importJobs",
"type":"application/vnd.sas.collection",
"itemType":"application/vnd.sas.listdata.importjob"
}
]
}
You set the state for a list by sending a PUT request to the /lists/{listId}/state
endpoint and specifying either 'deployed' or 'developing' for the value parameter. Here is an example using the 'listId' value from the previous examples:
PUT https://myserver:443/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/state?value="deployed"
or
PUT https://myserver:443/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/state?value="developing"
The following example shows how to do this in Python. This program takes a 'listId' and uses either '-i' or '-a' as arguments to set the state to either "developing" or "deployed", respectively.
#!/usr/bin/python3
import argparse
import requests
import json
from requests.structures import CaseInsensitiveDict
from os.path import exists
# Get the 'listId' value by command-line.
parser = argparse.ArgumentParser()
parser.add_argument(
"listId", help="The listId of the list to be retrieved.")
argGroup2 = parser.add_mutually_exclusive_group(required=True)
argGroup2.add_argument("-a", "--deployed", action="store_true",
help="Set state to deployed")
argGroup2.add_argument("-i", "--developing",
action="store_true", help="Set state to developing")
args = parser.parse_args()
listId = args.listId
# Set headers. The actual authorization token is not shown here.
headers = CaseInsensitiveDict()
headers["Authorization"] = f"Bearer {sasdemo_token}"
# Set the state based on the '-i' or '-a' argument being present.
if args.deployed:
state = "deployed"
elif args.developing:
state = "developing"
webservice = "http://myserver"
url = f"{webservice}/listData/lists/{listId}/state?value={state}"
response = requests.put(url, headers=headers)
if not response.ok:
print(
f"ERROR! Return Status: {response.reason} ({response.status_code})\n")
try:
jbody = response.json()
print(json.dumps(jbody, indent=2))
except:
print(response.text)
exit(1)
print(f"Return Status: {response.reason} ({response.status_code})\n")
print(json.dumps(response.json(), indent=2))
The list is returned in the body of the response. Links and columns are removed for brevity.
{
"id": "b3c93e05-2f57-49a9-9780-df55ae0510f2",
"version": 1,
"creationTimeStamp": "2022-12-12T16:39:42.818187Z",
"modifiedTimeStamp": "2022-12-12T20:18:39.487773Z",
"createdBy": "sasdemo",
"modifiedBy": "sasdemo",
"name": "ACME Corp Employees",
"description": "",
"state": "developing",
"isImmutable": false,
"label": ""
}
Sending a PUT request to the /lists/{listId}/contents
endpoint enables you to either modify existing records in a list's contents, add new records to a list's contents, or delete records from a list's contents. The op (short for operation) parameter is used to specify the behavior to either be an upsert or a delete. The request response contains the list that was modified (the definition, not the contents). In addition, the 'modifiedTimeStamp' and 'modifiedBy' properties are updated to reflect that the list has been updated.
Desired Effect | OP Value | Column Values Required |
---|---|---|
Update records | "upsert" | All key columns + columns to be modified |
Add new records | "upsert" | All columns |
Delete records | "delete" | All key columns |
#!/usr/bin/python3
import argparse
import requests
import json
from requests.structures import CaseInsensitiveDict
from os.path import exists
# Get the 'listId' and CSV file from the command-line.
parser = argparse.ArgumentParser()
parser.add_argument(
"listId", help="The listId of the List definition.")
args = parser.parse_args()
listId = args.listId
# Set headers.
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Content-Type"] = "application/vnd.sas.collection+json"
# The actual authorization token is not shown here.
headers["Authorization"] = f"Bearer {sasdemo_token}"
# Specify the op(eration). You are doing an upsert operation.
operation = "upsert"
# Updated Employee Data.
# - Updated salary for Alexander Hunold's staff.
# - New Employee Tyler Tatman.
employeeData = {
"items": [
{
"employeeId": 104,
"salary": 6501,
},
{
"employeeId": 105,
"salary": 5301,
},
{
"employeeId": 106,
"salary": 5301,
},
{
"employeeId": 107,
"salary": 4701,
},
{
"employeeId": 207,
"firstName": "Tyler",
"lastName": "Tatman",
"email": "TTATMAN",
"phoneNumber": "850-467-0709",
"hireDate": "5-FEB-15",
"jobId": "PUBLICITY",
"salary": 12000,
"commissionPct": 0,
"managerId": 101,
"departmentId": 90
}
]
}
# Send a PUT to '/lists/{listId}/contents'.
webservice = "https://myserver:443"
url = f"{webservice}/listData/lists/{listId}/contents?op={operation}"
try:
response = requests.put(url, headers=headers,
json=employeeData, verify=False)
except requests.exceptions.ConnectionError as errc:
raise SystemExit(
f"Unable to connect to {webservice}. {type(errc)}")
# Show the response without links or columns.
response_body = response.json()
del response_body['links']
del response_body['columns']
print(json.dumps(response_body, indent=2))
The list is returned in the body of the response. Links and columns are removed for brevity.
{
"id": "b3c93e05-2f57-49a9-9780-df55ae0510f2",
"version": 1,
"creationTimeStamp": "2022-12-12T16:39:42.818187Z",
"modifiedTimeStamp": "2022-12-13T16:04:02.135119Z",
"createdBy": "sasdemo",
"modifiedBy": "sasdemo",
"name": "ACME Corp Employees",
"description": "",
"state": "developing",
"isImmutable": false,
"label": ""
}
Purging data from a list is a synchronous operation that is initiated by submitting a purge job. You submit the purge job by sending a PUT request to the /lists/{listId}/purgeJobs
endpoint. There are no additional parameters required.
#!/usr/bin/python3
import json
import sys
import requests
import argparse
from requests.structures import CaseInsensitiveDict
from urllib3.exceptions import InsecureRequestWarning
parser = argparse.ArgumentParser()
parser.add_argument(
"listId", help="The listId of the list to be purged.")
parser.add_argument("-v", "--verbose", action="store_true",
help="Enable verbose output")
args = parser.parse_args()
listId = args.listId
user = "sasdemo"
webservice = "https://myserver:443"
token = getToken(user)
headers = CaseInsensitiveDict()
headers["Accept"] = "application/json"
headers["Authorization"] = f"Bearer {token}"
url = f"{webservice}/listData/lists/{listId}/purgeJobs"
try:
response = requests.post(url, headers=headers, verify=False)
except requests.exceptions.ConnectionError as errc:
raise SystemExit(f"Unable to connect to {webservice}")
print(f"HTTP RESPONSE: {response.reason} ({response.status_code})")
if not response.ok:
print("\n**ERROR** Unable to purge contents")
try:
jbody = response.json()
print(json.dumps(jbody, indent=2))
except:
print(response.text)
try:
jbody = response.json()
print(json.dumps(jbody, indent=2))
except:
print("Unable to render JSON")
print(response.text)
Among other things, the response that is returned by the POST request contains the initial state of the purge job (running) and links that can be used to retrieve the current state of the purge job.
{
"id": "251f002a-c584-4ba5-8540-ff420805f9f6",
"version": 1,
"state": "running",
"creationTimeStamp": "2022-12-13T17:53:41.813024353Z",
"createdBy": "sasdemo",
"listId": "b3c93e05-2f57-49a9-9780-df55ae0510f2",
"results": {},
"errors": [],
"links": [
{
"method": "GET",
"rel": "self",
"href": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/purgeJobs/251f002a-c584-4ba5-8540-ff420805f9f6",
"uri": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/purgeJobs/251f002a-c584-4ba5-8540-ff420805f9f6",
"type": "application/vnd.sas.listdata.purgejob"
},
{
"method": "GET",
"rel": "up",
"href": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/purgeJobs",
"uri": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/purgeJobs",
"type": "application/vnd.sas.collection",
"itemType": "application/vnd.sas.listdata.purgejob"
}
]
}
Once the purge job completes, you can retrieve the job's status by sending a GET request to the "self" link that is returned when you submitted the purge job. Here is example output:
https://myserver:443/listData/lists/{listId}/purgeJobs/{purgeJobId}
Here is the link for your example based on the example output above:
Once completed, the response shows the state is completed and the timestamps and number of records are updated.
{
"id": "251f002a-c584-4ba5-8540-ff420805f9f6",
"version": 1,
"state": "completed",
"creationTimeStamp": "2022-12-13T17:53:41.813024Z",
"completedTimeStamp": "2022-12-13T17:53:41.840122Z",
"createdBy": "sasdemo",
"listId": "b3c93e05-2f57-49a9-9780-df55ae0510f2",
"results": {
"recordCount": 51
},
"errors": [],
"links": [
{
"method": "GET",
"rel": "self",
"href": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/purgeJobs/251f002a-c584-4ba5-8540-ff420805f9f6",
"uri": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/purgeJobs/251f002a-c584-4ba5-8540-ff420805f9f6",
"type": "application/vnd.sas.listdata.purgejob"
},
{
"method": "GET",
"rel": "up",
"href": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/purgeJobs",
"uri": "/listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2/purgeJobs",
"type": "application/vnd.sas.collection",
"itemType": "application/vnd.sas.listdata.purgejob"
}
]
}
To delete a list, you submit a DELETE request to the /lists/{listId}
endpoint. There are no additional parameters required; however, the list state must not be 'deployed'. Attempting to delete a deployed list results in a 409 HTTP Status (Conflict) and returns the following response body:
{
"httpStatusCode": 409,
"errorCode": 124775,
"message": "The list is deployed.",
"details": [
"path: /listData/lists/b3c93e05-2f57-49a9-9780-df55ae0510f2",
"correlator: c1d96cf0-dda5-4618-a15f-66f7ea11dd30"
]
}
If the delete request succeeds, HTTP 204 (No Content) is returned. Deleting a list that does not exist is not an error and also returns an HTTP 204 (No Content).
#!/usr/bin/python3
import argparse
import requests
import json
from requests.structures import CaseInsensitiveDict
# Get the 'listId' value from the command-line.
parser = argparse.ArgumentParser()
parser.add_argument(
"listId", help="The listId of the List definition to be deleted.")
args = parser.parse_args()
listId = args.listId
# Set headers.
headers = CaseInsensitiveDict()
# The actual authorization token is not shown here.
headers["Authorization"] = f"Bearer {sasdemo_token}"
# Send a DELETE request to '/lists/{listId}'.
webservice = "https://myserver:443"
url = f"{webservice}/listData/lists/{listId}"
try:
response = requests.delete(url, headers=headers, verify=False)
except requests.exceptions.ConnectionError as errc:
raise SystemExit(f"Unable to connect to {webservice}. {type(errc)}")
assert response.status_code == 204, f"\n{json.dumps(response.json(), indent=2)}"
print(f"Return Status: {response.reason} ({response.status_code})\n")
version 4, last updated on 14 December 2023