10 Commits
1.0.3 ... 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
3 changed files with 121 additions and 46 deletions

View File

@@ -4,16 +4,25 @@ on: [push]
jobs:
Build:
runs-on: windows
runs-on: ubuntu-latest
container:
image: python
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
uses: actions/checkout@main
- name: Setting up python modules to build package
run: |
python -m pip install build twine
- name: Building the package
run: |
powershell mv ${{ gitea.workspace }}\GLPIAPI.py ${{ gitea.workspace }}\build\src\GLPIAPI\
cd ${{ gitea.workspace }}\build
mkdir ${{ gitea.workspace }}/build/src/GLPIAPI/
mv ${{ gitea.workspace }}/GLPIAPI.py ${{ gitea.workspace }}/build/src/GLPIAPI/GLPIAPI.py
cd ${{ gitea.workspace }}/build
python -m build
- name: Publish package
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'

View File

@@ -32,8 +32,17 @@ class GLPIAPI:
"Session-Token": self.SessionToken,
"App-Token": self.AppToken
}
else:
raise Exception(f"{result.status_code} - {result.json()[0]}")
def GetComputers(self, deviceName=None, serialNumber=None, user=None, imei=None, airwatchDevice=None):
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 GetComputers(self, deviceName=None, serialNumber=None, user=None, imei=None, airwatchDevice=None, fieldsToDisplay=[], trashbin=0):
'''
Search for computer items in GLPI based on one of the possibles parameters :
- deviceName : name of the item
@@ -41,50 +50,58 @@ class GLPIAPI:
- 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
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=0&criteria[0][field]=1&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{deviceName}$'
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=0&criteria[0][field]=70&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{user}$'
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 != ''):
# 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)
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[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}$'
else:
# 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}$'
elif(imei != None):
# 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)
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[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}$'
elif(serialNumber != None):
# 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]=^{serialNumber}$'
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
search_parameter = f'is_deleted=0&criteria[0][link]=AND&criteria[0][field]=view&criteria[0][searchtype]=contains&criteria[0][value]='
if(searchAll):
searchUri = f"{self.Server}/apirest.php/search/Computer/?range=0-9999999&is_deleted=0"
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()
@@ -100,10 +117,61 @@ class GLPIAPI:
else:
itemID = list(search["data"].keys())
return itemID, search["data"], search["totalcount"]
else:
return None, None, 0
return None, None, 0
return None, search, 0
def GetUsers(self, username=None, email=None):
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
@@ -112,37 +180,31 @@ class GLPIAPI:
Returns a tuple with user id, user data and user count
'''
searchAll = False
fieldName = None
fieldValue = None
if(username != None):
search_parameter = f'is_deleted=0&criteria[0][field]=1&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{username}$'
fieldName = 'User.name'
fieldValue = username
elif(email != None):
search_parameter = f'is_deleted=0&criteria[0][field]=5&withindexes=true&criteria[0][searchtype]=contains&criteria[0][value]=^{email}$'
else:
searchAll = True
fieldName = 'User.UserEmail.email'
fieldValue = email
return self.GetItems("User", fieldName, fieldValue, fieldsToDisplay, trashbin)
if(searchAll):
searchUri = f"{self.Server}/apirest.php/search/User/?range=0-9999999&is_deleted=0"
else:
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):
if(searchAll):
# requires id to be in the display preferences of the api user
userID = [i["2"] for i in search["data"]]
else:
userID = list(search["data"].keys())
return userID, search["data"], search["totalcount"]
return None, None, 0
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):
self.CheckConnection()
manifest = {
"input": {
"name": file,
@@ -161,6 +223,7 @@ class GLPIAPI:
return requests.post(self.Server+"/apirest.php/Document/", headers=headers, data=data, files=files)
def SetDocumentToItem(self, itemID, itemType, documentID):
self.CheckConnection()
body = {
"input": {
"documents_id": documentID,
@@ -171,6 +234,7 @@ class GLPIAPI:
return requests.post(self.Server+"/apirest.php/Document/"+str(documentID)+"/Document_Item", headers=self.Headers, json=body)
def UpdateInventory(self, inventory):
self.CheckConnection()
headers = {
"Content-Type":"Application/x-compress",
"user-agent":self.UserAgent
@@ -186,6 +250,7 @@ class GLPIAPI:
...
}
'''
self.CheckConnection()
body = {
"input" : {
"id" : itemId,
@@ -197,7 +262,7 @@ class GLPIAPI:
return requests.put(uri, headers=self.Headers, json=body)
def UpdateSerialNumber(self, deviceid, serialnumber):
self.CheckConnection()
body = {
"input" : {
"id" : deviceid,
@@ -208,7 +273,7 @@ class GLPIAPI:
return requests.put(uri, headers=self.Headers, json=body)
def UpdateItemUser(self, itemID, itemType, username):
self.CheckConnection()
body = {
"input" : {
"id" : itemID,
@@ -223,6 +288,7 @@ class GLPIAPI:
- containerName is block label name
- containerID is block id
'''
self.CheckConnection()
uri = f"{self.Server}/apirest.php/PluginFields{itemType}{containerName}"
searchURI = f"{self.Server}/apirest.php/PluginFields{itemType}{containerName}?range=0-999999999"
result = requests.get(searchURI, headers=self.Headers)

View File

@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "GLPIAPI"
version = "1.0.3"
version = "1.0.4"
description = "A module python to make it easier to use GLPI API"
readme = "README.md"
dependencies = [