19 Commits
1.0.1 ... 1.0.4

Author SHA1 Message Date
f7ab676b0d Updated Search methods to look for uid instead of name for searchOptions and new parameters (fieldsToDisplay, trashbin)
All checks were successful
Build python package / Build (push) Successful in 38s
2026-03-30 17:12:14 +02:00
b78a9512d8 Changed GetSearchOptions to use uid instead of name (immuable when changing language) 2026-03-30 17:10:32 +02:00
Jason SECULA
1c28d0ac84 Fixed a mistake in checkConnection not checking the status_code
All checks were successful
Build python package / Build (push) Successful in 40s
2026-03-30 10:57:52 +02:00
906564a44a Added some verification to renew session if session token expires
All checks were successful
Build python package / Build (push) Successful in 36s
2026-03-28 21:26:07 +01:00
b9fd987c5e returning requests return instead of status_code only for GetItems and GetComputers
All checks were successful
Build python package / Build (push) Successful in 39s
2026-03-28 20:50:07 +01:00
a3a393088c Fixed issue when not returning anything in GetItems() and updated GetUsers to use GetItems
All checks were successful
Build python package / Build (push) Successful in 35s
2026-03-28 20:45:46 +01:00
6599b967ab Updated build number and gitea workflow
All checks were successful
Build python package / Build (push) Successful in 38s
2026-03-28 20:26:12 +01:00
0155644b65 Updated GetItems method to use GetSearchOptions method to find fields id 2026-03-28 19:15:43 +01:00
3d920fd5e5 added a method to get search options for items 2026-03-28 18:15:14 +01:00
660b8e8463 added GetItems method to get any item 2026-03-28 17:41:15 +01:00
Jason SECULA
acbd11369e fixed variable interpreted in UpdateItemUser
All checks were successful
Build python package / Build (push) Successful in 26s
2026-03-19 09:03:05 +01:00
Jason SECULA
de1284b896 removed build src folder
All checks were successful
Build python package / Build (push) Successful in 23s
2026-03-19 08:55:54 +01:00
535a5d6ccd added dependencies to pyproject
All checks were successful
Build python package / Build (push) Successful in 21s
2026-03-18 20:55:20 +01:00
c2bb86c138 Updated module version in build project file
All checks were successful
Build python package / Build (push) Successful in 23s
2026-03-18 20:26:00 +01:00
02bee714b5 Updated UpdateUser to UpdateItemUser and added a parameter to set itemType
Some checks failed
Build python package / Build (push) Has been cancelled
2026-03-18 20:22:56 +01:00
11b2b61aac Updated SetDocumentToDevice to SetDocumentToItem and added a parameter to set itemType 2026-03-18 20:20:36 +01:00
ebac45944b Updated GetUser and change name to GetUsers 2026-03-18 20:18:48 +01:00
eea32532aa Updated GetDevice and changed name to GetComputers 2026-03-18 20:02:53 +01:00
Jason SECULA
c780c30533 added SetField for normal fields and SetCustomField for custom fields
Some checks failed
Build python package / Build (push) Has been cancelled
2026-02-27 08:07:48 +01:00
4 changed files with 205 additions and 60 deletions

View File

@@ -4,16 +4,25 @@ on: [push]
jobs: jobs:
Build: Build:
runs-on: windows runs-on: ubuntu-latest
container:
image: python
steps: steps:
- name: Act Workaround # https://github.com/nektos/act/issues/973
if: ${{ env.ACT }}
run: curl -fsSL https://deb.nodesource.com/setup_22.x | bash && apt install -y nodejs
- name: Check out repository code - name: Check out repository code
uses: actions/checkout@main uses: actions/checkout@main
- name: Setting up python modules to build package
run: |
python -m pip install build twine
- name: Building the package - name: Building the package
run: | run: |
powershell mv ${{ gitea.workspace }}\GLPIAPI.py ${{ gitea.workspace }}\build\src\GLPIAPI\ mkdir ${{ gitea.workspace }}/build/src/GLPIAPI/
cd ${{ gitea.workspace }}\build mv ${{ gitea.workspace }}/GLPIAPI.py ${{ gitea.workspace }}/build/src/GLPIAPI/GLPIAPI.py
cd ${{ gitea.workspace }}/build
python -m build python -m build
- name: Publish package - name: Publish package
run: | run: |
python -m twine upload -u ${{ secrets.repo_user }} -p ${{ secrets.repo_pass }} --repository-url ${{ secrets.repo_url }} ${{ gitea.workspace }}\build\dist\* python -m twine upload -u ${{ secrets.repo_user }} -p ${{ secrets.repo_pass }} --repository-url ${{ secrets.repo_url }} ${{ gitea.workspace }}/build/dist/*
if: github.ref_type == 'tag' if: github.ref_type == 'tag'

View File

@@ -32,68 +32,179 @@ class GLPIAPI:
"Session-Token": self.SessionToken, "Session-Token": self.SessionToken,
"App-Token": self.AppToken "App-Token": self.AppToken
} }
else:
raise Exception(f"{result.status_code} - {result.json()[0]}")
def CheckConnection(self):
sessionUri = f"{self.Server}/apirest.php/getFullSession/"
result = requests.get(sessionUri, headers=self.Headers)
if(result.status_code != 200 and result.json()[0] == 'ERROR_SESSION_TOKEN_INVALID'):
self.InitConnection()
return
def GetDevice(self, airwatchDevice=None, serialNumber=None, imei=None): def GetComputers(self, deviceName=None, serialNumber=None, user=None, imei=None, airwatchDevice=None, fieldsToDisplay=[], trashbin=0):
if(airwatchDevice != None): '''
Search for computer items in GLPI based on one of the possibles parameters :
- deviceName : name of the item
- serialNumber : serial number of the item
- user : user of the item
- imei : custom field using field plugin (only for internal use)
- airwatchDevice : an airwatchDevice object from airwatchAPI module
If no parameters are set, it will search for all items.
fieldsToDisplay: set fields to forcefully add to the results of the search (must be a list [])
trashbin: set if the search is in the trashbin or not (0 not in the trash or 1 in the trash)
Return a tuple with item id, item data and item count.
'''
self.CheckConnection()
searchAll = False
if(deviceName != None):
# Recherche en fonction du nom de l'appareil
search_parameter = f'is_deleted={trashbin}&criteria[0][field]=1&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{deviceName}$'
elif(user != None):
# Recherche en fonction de l'utilisateur de l'appareil
search_parameter = f'is_deleted={trashbin}&criteria[0][field]=70&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{user}$'
elif(airwatchDevice != None):
if(airwatchDevice.Imei != ''): if(airwatchDevice.Imei != ''):
# Recherche des appareils en fonction du numéro de série ou de l'imei # Recherche des appareils en fonction du numéro de série ou de l'imei
# l'imei pouvant être dans le champ numéro de série ou les champs imei custom (plugin field) # l'imei pouvant être dans le champ numéro de série ou les champs imei custom (plugin field)
search_parameter = f'is_deleted=0&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{airwatchDevice.SerialNumber}$'\ search_parameter = f'is_deleted={trashbin}&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{airwatchDevice.SerialNumber}$'\
f'&criteria[1][link]=OR&criteria[1][field]=5&criteria[1][searchtype]=contains&criteria[1][value]=^{airwatchDevice.Imei}$'\ f'&criteria[1][link]=OR&criteria[1][field]=5&criteria[1][searchtype]=contains&criteria[1][value]=^{airwatchDevice.Imei}$'\
f'&criteria[2][link]=OR&criteria[2][field]=76667&criteria[2][searchtype]=contains&criteria[2][value]=^{airwatchDevice.Imei}$'\ f'&criteria[2][link]=OR&criteria[2][field]=76667&criteria[2][searchtype]=contains&criteria[2][value]=^{airwatchDevice.Imei}$'\
f'&criteria[3][link]=OR&criteria[3][field]=76670&criteria[3][searchtype]=contains&criteria[3][value]=^{airwatchDevice.Imei}$' f'&criteria[3][link]=OR&criteria[3][field]=76670&criteria[3][searchtype]=contains&criteria[3][value]=^{airwatchDevice.Imei}$'
else: else:
# Recherche des appareils en fonction du numéro de série seulement # Recherche des appareils en fonction du numéro de série seulement
search_parameter = f'is_deleted=0&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{airwatchDevice.SerialNumber}$' search_parameter = f'is_deleted={trashbin}&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{airwatchDevice.SerialNumber}$'
else:
if(imei != None): elif(imei != None):
# Recherche des appareils en fonction du numéro de série ou de l'imei # Recherche des appareils en fonction du numéro de série ou de l'imei
# l'imei pouvant être dans le champ numéro de série ou les champs imei custom (plugin field) # l'imei pouvant être dans le champ numéro de série ou les champs imei custom (plugin field)
search_parameter = f'is_deleted=0&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{serialNumber}$'\ search_parameter = f'is_deleted={trashbin}&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{serialNumber}$'\
f'&criteria[1][link]=OR&criteria[1][field]=5&criteria[1][searchtype]=contains&criteria[1][value]=^{imei}$'\ f'&criteria[1][link]=OR&criteria[1][field]=5&criteria[1][searchtype]=contains&criteria[1][value]=^{imei}$'\
f'&criteria[2][link]=OR&criteria[2][field]=76667&criteria[2][searchtype]=contains&criteria[2][value]=^{imei}$'\ f'&criteria[2][link]=OR&criteria[2][field]=76667&criteria[2][searchtype]=contains&criteria[2][value]=^{imei}$'\
f'&criteria[3][link]=OR&criteria[3][field]=76670&criteria[3][searchtype]=contains&criteria[3][value]=^{imei}$' f'&criteria[3][link]=OR&criteria[3][field]=76670&criteria[3][searchtype]=contains&criteria[3][value]=^{imei}$'
elif(serialNumber != None):
# Recherche des appareils en fonction du numéro de série seulement
search_parameter = f'is_deleted={trashbin}&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{serialNumber}$'
else:
# Recherche de l'ensemble des appareils si rien n'est renseigné
searchAll = True
if(searchAll):
searchUri = f"{self.Server}/apirest.php/search/Computer/?range=0-9999999&is_deleted={trashbin}"
else:
searchUri = f"{self.Server}/apirest.php/search/Computer?{search_parameter}"
if(fieldsToDisplay != []):
for i in range(len(fieldsToDisplay)):
searchUri += f"&forcedisplay[{i}]={fieldsToDisplay[i]}"
search = requests.get(searchUri, headers=self.Headers)
if(search.status_code == 200):
search = search.json()
if(search["totalcount"] == 1):
itemID = list(search["data"].keys())[0]
data = search["data"][itemID]
return itemID, data, search["totalcount"]
elif(search["totalcount"] > 1):
if(searchAll):
# requires id to be in the display preferences of the api user
itemID = [i["2"] for i in search["data"]]
else:
itemID = list(search["data"].keys())
return itemID, search["data"], search["totalcount"]
else: else:
# Recherche des appareils en fonction du numéro de série seulement return None, None, 0
search_parameter = f'is_deleted=0&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{serialNumber}$'
searchUri = f"{self.Server}/apirest.php/search/Computer?{search_parameter}"
search = requests.get(searchUri, headers=self.Headers)
if(search.status_code == 200):
search = search.json()
if(search["totalcount"] == 1):
deviceID = list(search["data"].keys())[0]
data = search["data"][deviceID]
return deviceID, data, search["totalcount"] return None, search, 0
elif(search["totalcount"] > 1):
deviceID = list(search["data"].keys())
return deviceID, search["data"], search["totalcount"]
return None, None, 0
def GetUser(self, username=None, email=None):
if(username != None):
search_parameter = f'is_deleted=0&criteria[0][field]=1&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{username}$'
elif(email != None):
search_parameter = f'is_deleted=0&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{email}$'
searchUri = f"{self.Server}/apirest.php/search/user?{search_parameter}"
search = requests.get(searchUri, headers=self.Headers)
if(search.status_code == 200):
search = search.json()
if(search["totalcount"] == 1):
userID = list(search["data"].keys())[0]
data = search["data"][userID]
return userID, data, search["totalcount"]
elif(search["totalcount"] > 1):
userID = list(search["data"].keys())
return userID, search["data"], search["totalcount"]
return None, None, 0
def GetItems(self, itemType, fieldName=None, fieldValue=None, fieldsToDisplay=[], trashbin=0):
'''
Search for items of a specific item type in GLPI. A filter can be set using fieldName and fieldValue :
fieldName: must be the name of the field as visible in GLPI
fieldValue: a string
If only itemType is set, it will search for all items
Return a tuple with item id (in a list if there are more than 1), item data (in a list of dict items) and item count (int).
If the query does not succeed it will return a tuple with the http status code instead of item data (e.g. None, 400, 0).
'''
self.CheckConnection()
searchAll = False
if(fieldName != None and fieldValue != None):
fieldId = list(self.GetSearchOptions(itemType, fieldName))[0]
# Recherche en fonction de l'utilisateur de l'appareil
search_parameter = f'is_deleted={trashbin}&criteria[0][field]={fieldId}&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{fieldValue}$'
else:
searchAll = True
if(searchAll):
searchUri = f"{self.Server}/apirest.php/search/{itemType}/?range=0-9999999&is_deleted={trashbin}"
else:
searchUri = f"{self.Server}/apirest.php/search/{itemType}?{search_parameter}"
if(fieldsToDisplay != []):
for i in range(len(fieldsToDisplay)):
searchUri += f"&forcedisplay[{i}]={fieldsToDisplay[i]}"
search = requests.get(searchUri, headers=self.Headers)
if(search.status_code == 200):
search = search.json()
if(search["totalcount"] == 1):
itemID = list(search["data"].keys())[0]
data = search["data"][itemID]
return itemID, data, search["totalcount"]
elif(search["totalcount"] > 1):
if(searchAll):
idFieldNumber = list(self.GetSearchOptions(itemType, f'{itemType}.id'))[0]
itemID = [i[idFieldNumber] for i in search["data"]]
else:
itemID = list(search["data"].keys())
return itemID, search["data"], search["totalcount"]
else:
return None, None, 0
return None, search, 0
def GetUsers(self, username=None, email=None, fieldsToDisplay=[], trashbin=0):
'''
Search for users in GLPI based on one of the possibles parameters :
- username : username of the glpi user
- email : email of the glpi user
If no parameters are set, it will search for all users
Returns a tuple with user id, user data and user count
'''
fieldName = None
fieldValue = None
if(username != None):
fieldName = 'User.name'
fieldValue = username
elif(email != None):
fieldName = 'User.UserEmail.email'
fieldValue = email
return self.GetItems("User", fieldName, fieldValue, fieldsToDisplay, trashbin)
def GetSearchOptions(self, itemType, fieldName=None):
self.CheckConnection()
queryUri = f"{self.Server}/apirest.php/listSearchOptions/{itemType}"
searchOptions = requests.get(queryUri, headers=self.Headers)
if(searchOptions.status_code == 200):
searchOptions = searchOptions.json()
if(fieldName != None):
for k,v in searchOptions.items():
if('uid' in v.keys() and v['uid'].lower() == fieldName.lower()):
return {k : searchOptions[k]}
return searchOptions
return searchOptions.status_code
def UploadFile(self, file, path): def UploadFile(self, file, path):
self.CheckConnection()
manifest = { manifest = {
"input": { "input": {
"name": file, "name": file,
@@ -111,25 +222,47 @@ class GLPIAPI:
files = {file: fileData} files = {file: fileData}
return requests.post(self.Server+"/apirest.php/Document/", headers=headers, data=data, files=files) return requests.post(self.Server+"/apirest.php/Document/", headers=headers, data=data, files=files)
def SetDocumentToDevice(self, deviceID, documentID): def SetDocumentToItem(self, itemID, itemType, documentID):
self.CheckConnection()
body = { body = {
"input": { "input": {
"documents_id": documentID, "documents_id": documentID,
"items_id": deviceID, "items_id": itemID,
"itemtype": "Computer" "itemtype": itemType
} }
} }
return requests.post(self.Server+"/apirest.php/Document/"+str(documentID)+"/Document_Item", headers=self.Headers, json=body) return requests.post(self.Server+"/apirest.php/Document/"+str(documentID)+"/Document_Item", headers=self.Headers, json=body)
def UpdateInventory(self, inventory): def UpdateInventory(self, inventory):
self.CheckConnection()
headers = { headers = {
"Content-Type":"Application/x-compress", "Content-Type":"Application/x-compress",
"user-agent":self.UserAgent "user-agent":self.UserAgent
} }
return requests.post(self.Server, headers=headers, data=inventory) return requests.post(self.Server, headers=headers, data=inventory)
def UpdateSerialNumber(self, deviceid, serialnumber):
def SetField(self, itemType, itemId, data):
'''Modify fields of a GLPI item.
Input must be a dict formatted like this :
{
"fieldName1": "fieldValue1",
"fieldName2": "fieldValue2",
...
}
'''
self.CheckConnection()
body = {
"input" : {
"id" : itemId,
}
}
body["input"] = {**body["input"], **data}
uri = f"{self.Server}/apirest.php/{itemType}/"
return requests.put(uri, headers=self.Headers, json=body)
def UpdateSerialNumber(self, deviceid, serialnumber):
self.CheckConnection()
body = { body = {
"input" : { "input" : {
"id" : deviceid, "id" : deviceid,
@@ -139,22 +272,23 @@ class GLPIAPI:
uri = f"{self.Server}/apirest.php/Computer/" uri = f"{self.Server}/apirest.php/Computer/"
return requests.put(uri, headers=self.Headers, json=body) return requests.put(uri, headers=self.Headers, json=body)
def UpdateUser(self, deviceid, username): def UpdateItemUser(self, itemID, itemType, username):
self.CheckConnection()
body = { body = {
"input" : { "input" : {
"id" : deviceid, "id" : itemID,
"users_id" : username "users_id" : username
} }
} }
uri = f"{self.Server}/apirest.php/Computer/" uri = f"{self.Server}/apirest.php/{itemType}/"
return requests.put(uri, headers=self.Headers, json=body) return requests.put(uri, headers=self.Headers, json=body)
def SetField(self, itemType, containerName, containerID, itemId, fieldName, data): def SetCustomField(self, itemType, containerName, containerID, itemId, fieldName, data):
'''Requires fields plugin on GLPI server '''Requires fields plugin on GLPI server
- containerName is block label name - containerName is block label name
- containerID is block id - containerID is block id
''' '''
self.CheckConnection()
uri = f"{self.Server}/apirest.php/PluginFields{itemType}{containerName}" uri = f"{self.Server}/apirest.php/PluginFields{itemType}{containerName}"
searchURI = f"{self.Server}/apirest.php/PluginFields{itemType}{containerName}?range=0-999999999" searchURI = f"{self.Server}/apirest.php/PluginFields{itemType}{containerName}?range=0-999999999"
result = requests.get(searchURI, headers=self.Headers) result = requests.get(searchURI, headers=self.Headers)

View File

@@ -7,9 +7,12 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "GLPIAPI" name = "GLPIAPI"
version = "1.0.1" version = "1.0.4"
description = "A module python to make it easier to use GLPI API" description = "A module python to make it easier to use GLPI API"
readme = "README.md" readme = "README.md"
dependencies = [
"requests >= 2.32.5"
]
requires-python = ">=3.7" requires-python = ">=3.7"
classifiers = [ classifiers = [
"Programming Language :: Python :: 3" "Programming Language :: Python :: 3"

View File

@@ -1 +0,0 @@
__all__ = ["GLPIAPI"]