
Nous avons vu les fondements de la manipulation des données de Sciforma à l’aide de Postman. Ce n’est pas tout à fait fini ; nous continuerons cette découverte plus tard. Ce chapitre-ci sera axé sur la modification d’un volume de données beaucoup plus important à l’aide de scripts.
L’introduction a expliqué pourquoi nous allons utiliser Python comme langage de script. Cependant, mon exemple pourrait facilement être traduit dans n’importe quel autre langage de programmation un peu moderne.
Je ne donnerai pas de conseils sur la façon d’utiliser les outils de script ou même la programmation en Python. Néanmoins, je vais partager avec vous des exemples fonctionnels. Ils ne sont pas exactement construits de cette manière dans mes implémentations professionnelles, mais ils fonctionnent et c’est ce qu’on leur demande.
Le scénario.
Dans ce premier exemple, nous allons nous en tenir au traitement des données de ressources. Le cas d’utilisation est le suivant.
Dans la vraie vie, vous utilisez probablement plusieurs instances de votre base de données Sciforma. Une version est destinée à la production, une autre à la qualification ou aux essais et encore autre peut-être au développement.
Vous remarquerez sûrement qu’aucun moyen n’est intégré pour distinguer visuellement ces instances (je parle autant du Designer que du client HTML). Vous ne pouvez pas changer le schéma de couleurs. Vous ne pouvez pas mettre un grand panneau d’en-tête rouge pour vous informer sur quelle version vous travaillez.
Vous avez cependant à coup sûr expérimenté que, en travaillant avec plusieurs fenêtres de Designer ou HTML simultanément, l’erreur de manipulation n’est jamais très loin. Ma meilleure idée pour différentier les environnements a été d’avoir différentes icônes d’avatar dans chaque version, visuellement différentes. Cela, par ailleurs, ne fonctionne qu’avec le client HTML.
La seconde idée est d’utiliser le Middle Name. En France ; ce Middle Name est systématiquement laissé vide. Si je mets « DEV » ou « TEST » comme deuxième prénom pour toutes les ressources (vous voyez le cas d’utilisation arriver, n’est-ce pas ?), cela donnera des indices sur presque toutes les pages, sur la version sur laquelle je me trouve et diminuera réellement le risque d’erreur.
Changer le deuxième prénom de toutes les ressources avec copier-coller est assez lourd, nous allons donc utiliser un script de base.
Écrivons donc un script qui se connectera à, disons, l’instance TEST et définira ‘TEST’ comme deuxième nom pour toutes les ressources.
La structure du Script
Nous savons comment modifier les données de la ressource. Pour cela, nous avons besoin de son ID. La structure de notre script sera la suivante:
get the required data for authentification
for each resource in the resource list:
if the resource is active:
update its middle name to 'TEST'
Les données requises pour l’authentification
Dans Postman, nous avons mis en place le mécanisme pour obtenir le token d’autorisation du serveur 0auth et le transmettre à toutes les requêtes que nous avons écrites. Ici, nous devons le faire nous-mêmes.
J’ai aussi mentionné dans le chapitre précédent que nous devions spécifier le Content-Type pour certaines requêtes. Cela sera nécessaire autant pour les demandes PATCH que vous connaissez, que pour PUT, DELETE et POST, que nous verrons plus tard.
Nous devons gérer un en-tête pour GET et un pour PATCH dans notre cas d’utilisation. En effet, nous allons lire les ressources (GET) puis apporter des modifications (PATCH). Comme nous l’avons fait avec Postman, nous allons également définir toutes les variables dont nous avons besoin.
import json, requests
token_url = 'https://YOUR_DOMAIN_HERE.sciforma.net/oauth2/token'
rest_url = 'https://YOUR_DOMAIN_HERE.sciforma.net/sciforma/rest/'
header_token = {
'Content-type': 'application/x-www-form-urlencoded',
}
# get the token
body = {
'client_id': YOUR_CLIENT_ID,
'client_secret': YOUR_CLIENT_SECRET,
'grant_type': 'client_credentials',
'scope': 'resources:read resources:write',
}
response = requests.post(url=token_url,
headers=header_token,
verify=False,
data=body)
token = response.json()['access_token']
# define the GET header
header_get = {
'Content-type': 'application/json',
'Authorization': f'Bearer {token}',
'Cache-Control': 'no-cache'
}
# define the PATCH header
header_patch = {
'Content-type': 'application/vnd.sciforma.v1+merge-patch+json',
'Authorization': f'Bearer {token}',
'Cache-Control': 'no-cache'
}Passons en revue quelques commentaires :
- Au début, nous importons (cela se fait généralement tout en haut du script) :
- json est le module requis pour convertir ce que la REST API répond en une variable JSON, assez facile à manipuler
- requests est le module que nous utilisons pour appeler l’API Rest de Sciforma
- Dans ce premier exemple, le jeton et les en-têtes sont obtenus et produits à la tête de notre code pour plus de clarté. En pratique, nous allons mettre tout cela dans des fonctions dédiées ou des packages, qui seront beaucoup plus faciles à intégrer, de façon transparente, dans les différents scripts que vous allez créer.
- La durée de vie du jeton est d’environ une heure. Dans les scripts plus importants, vous devriez prendre soin de cela et générer de nouveaux jetons et en-têtes avant leur expiration.
- Notez que les Content-Types ont des valeurs différentes dans les deux en-têtes. Celui pour le header_get pourrait être omis, car c’est la valeur par défaut.
Récupérons la liste des ressources.
Nous ne pouvons pas accéder à la liste complète des ressources simultanément, nous devons donc la paginer. Nous utiliserons des pages avec 100 éléments.
Voici la deuxième partie du code :
LIMIT = 100
offset = 0
# prepare the data to write to the resources
data = {
'Middle Name': 'TEST',
}
while True:
# reads the 'page' of resources
response = requests.get(url=f'{rest_url}resources?offset={offset}&limit=100',
headers=header_get,
verify=False)
# convert the response into a JSON structure
json_data = response.json()
# test if the end of the list is reached
if not json_data:
# exit the loop
break
# fetch all the items of the reply
for resource in json_data:
# get the ID of the resource
sci_id = resource['id']
status = resource['status']
if status == 'ACTIVE':
# patch the resource
# todo
# next page of 100 resources
offset += LIMIT
print('end of process. bye.')Il y a plusieurs choses essentielles à comprendre
- Nous faisons une boucle infinie pour récupérer toute la liste des ressources par pages de 100
- À chaque page, nous testons si nous devons sortir de cette boucle (une page vide est retournée) ou continuer.
- Sur chaque page, nous transformons la réponse en JSON. C’est en fait une liste de JSONs. Alors, nous itérons à travers cet ensemble de ressources fourni et obtenons l’ID pour chacune d’elles. Cet ID est nécessaire pour passer la commande de Patch de la ressource.
- Nous récupérons également l’attribut status. Ainsi, nous l’utilisons uniquement pour tester et modifier uniquement les ressources actives.
- Le code pour la commande de Patch n’a pas encore été écrit et a été remplacé par ‘todo. ‘
- À la fin, lorsque nous quittons la boucle, nous imprimons un message de confirmation concernant la fin du script.
- Dans un script plus grand, l’incrément de l’offset (
offset += LIMIT, permettant à la page suivante de charger) serait un bon endroit pour envisager de renouveler le jeton, si nécessaire. a += best une expression standard de Python qui signifiea = a + bf'{python_code}'est un moyen pratique d’afficher la valeur d’une valeur calculée avec du code Python. Reportez-vous à la documentation des chaînes F, qui est facilement accessible sur Internet.
Il est temps de mettre à jour les ressources.
La dernière chose à faire est d’écrire la partie patch à insérer là où nous avons écrit ‘todo. ‘ L’ID de la ressource à patcher a été stocké dans une variable appelée sci_id, les instructions ont déjà été définies dans la variable appelée data.
# patches the resource
response = requests.patch(
url=f'{rest_url}resources/{sci_id}',
headers = header_patch,
json = data,
verify = False)
if response.status_code != 204:
# inform in case of errors
print(f'{json.loads(response.text)["message"]}')
else:
# sucess confirmation
print(f'{resource["last_name"]} updated')Encore une fois, voici quelques explications.
- Nous lançons la requête PATCH avec les paramètres requis.
- L’URL est construite comme nous l’avons vu dans Postman, avec l’identifiant de la ressource à mettre à jour.
- L’en-tête dédié pour PATCH qui transporte le token et informe sur le type de données échangées.
- Les données d’instruction sont utilisées pour modifier les ressources. C’est, en pratique, la nouvelle valeur du deuxième prénom. Cette information est passée dans la variable json data=
- Le paramètre Verify doit être False pour éviter les erreurs SSL (!) Note: la mise en place d’un certificat SSL pour pouvoir activer la vérification n’est pas couverte dans ce tutoriel.
- Ensuite, la réponse variable reçoit et stocke la réponse du serveur API Rest de Sciforma. Nous testons la requête qui contient un status_code.
- 204 signifie que le correctif a été accepté
- Les autres valeurs indiquent un problème, récupéré et affiché grâce à la formule barbare :
loads(response.text)["message"]
- Vous pouvez exécuter ce script en mode débogage avec des points d’arrêt pour visualiser et comprendre la structure de la variable ‘
response‘, qui pourrait être assez complexe. Il s’agit d’une chaîne que nous convertissons en JSON à partir de laquelle nous obtenons la propriété message.
Conclusion et résumé
Voici le script complet de bout en bout proposé. C’est assez simple, mais c’est votre première étape dans l’utilisation de l’API Rest de Sciforma.
Nous avons encore beaucoup de choses à découvrir ! J’ai déjà mentionné les autres types de requêtes : UPDATE, POST et DELETE. Saviez-vous que le traitement des données du projet nécessite l’obtention d’un ID caché qui ne peut pas être affiché avec l’interface utilisateur HTML ? Nous verrons comment obtenir et utiliser cet ID.
Ensuite, nous allons travailler avec des cas d’utilisation encore plus utiles : nous allons parler de
- mises à jour en masse du portefeuille de projets (dans le cas d’une migration de portefeuille, par exemple)
- création massive de personnes (ressources + utilisateurs) à partir d’un simple fichier Excel pour dynamiser vos processus d’onboarding
- Télécharger les données de suivi du temps depuis une source CSV externe pour les injecter dans les feuilles de temps
- et bien plus encore…
Alors, restez à l’écoute!
import json, requests
token_url = 'https://YOUR_DOMAIN.sciforma.net/oauth2/token'
rest_url = 'https://YOUR_DOMAIN.sciforma.net/sciforma/rest/'
header_token = {
'Content-type': 'application/x-www-form-urlencoded',
}
# get the token
body = {
'client_id': YOUR_CLIENT_ID,
'client_secret': YOUR_CLIENT_SECRET,
'grant_type': 'client_credentials',
'scope': 'resources:read resources:write',
}
response = requests.post(url=token_url,
headers=header_token,
verify=False,
data=body)
token = response.json()['access_token']
# define the GET header
header_get = {
'Content-type': 'application/json',
'Authorization': f'Bearer {token}',
'Cache-Control': 'no-cache'
}
# define the PATCH header
header_patch = {
'Content-type': 'application/vnd.sciforma.v1+merge-patch+json',
'Authorization': f'Bearer {token}',
'Cache-Control': 'no-cache'
}
LIMIT = 100
offset = 0
# prepare the data to write to the resources
data = {
'Middle Name': 'TEST',
}
i= 0
while True:
# reads the 'page' of resources
response = requests.get(url=f'{rest_url}resources?offset={offset}&limit=100',
headers=header_get,
verify=False)
# convert the response into a JSON structure
json_data = response.json()
# test if the end of the list is reached
if not json_data:
# exit the loop
break
# fetch all the items of the reply
for resource in json_data:
# get the ID of the resource
sci_id = resource['id']
status = resource['status']
if status == 'ACTIVE':
# patches the resource
response = requests.patch(
url=f'{rest_url}resources/{sci_id}',
headers=header_patch,
json=data,
verify=False)
if response.status_code != 204:
# inform in case of errors
print(f'{json.loads(response.text)["message"]}')
else:
# success confirmation
print(f'{resource["last_name"]} updated')
# next page of 100 resources
offset += LIMIT
print('end of process. bye.')


