Erreur à l'appel d'une API Simplicité

Request description

Bonjour,

Nous rencontrons récemment une erreur à l’appel d’une API. L’appel fonctionne via l’interface /api de Simplicité mais en ligne de commande j’ai l’erreur suivante :

Cannot read field “m_objects” because “this.m_data” is null

Steps to reproduce

This request concerns an up-to-date Simplicité instance
and those are the steps to reproduce it:

  1. Ouvrir un CMD
  2. Obtenir un token curl -s -k -u : “/login?_output=json”
  3. Appeler l’API curl -X GET “” -H "Authorization: Bearer "

Technical information

Instance /health
[Platform]
Status=OK
Version=4.0.P25
BuiltOn=2022-02-22 00:16 (revision 39f36d20ad611794a99815948a8f09f3bd7c4e19)
Simplicité logs
Pas d'erreur dans les logs.

Je ne reproduis pas ce que vous décrivez sur une 4.0 à jour:

$ curl -u designer:simplicite https://testapi40.demo.simplicite.io/api/login
lXLjlrheXHZYhmXLT7fIMzJfn6y711Psg3aY0Mv4d8AlrcfLQc

$ curl -H 'Authorization: Bearer lXLjlrheXHZYhmXLT7fIMzJfn6y711Psg3aY0Mv4d8AlrcfLQc' https://testapi40.demo.simplicite.io/api/rest/SystemParam
[{"row_id":"453","sys_code":"ACE_OPTIONS","sys_value":"{\n\t\"invisibles\": false,\n\t\"theme\": \"e(...)

Pouvez vous décrire votre cas de test de manière plus précise ?

Est-ce que le code de l’API peut être utile ?

package com.simplicite.extobjects.RCIB;

import java.util.List;

import org.json.JSONArray;
import org.json.JSONObject;

import com.simplicite.util.AppLog;
import com.simplicite.util.ObjectDB;
import com.simplicite.util.Tool;
import com.simplicite.util.exceptions.HTTPException;
import com.simplicite.util.tools.Parameters;


/**
 * External object RciApiList
 */
public class RciApiList extends com.simplicite.webapp.services.RESTServiceExternalObject  {
	private static final long serialVersionUID = 1L;

	
	/**
	 * GET method
	 * @param params Request parameters
	 */
	@Override
	public Object get(Parameters params) throws HTTPException {
		JSONArray res = new JSONArray();
		JSONObject req = params.getJSONObject();
		String appFilter = req!=null?req.optString("applicativeDomain", ""):"";
		
		String sql = "SELECT rci_api_name, STRING_AGG(rci_tag_name, ','), rci_api_scope, rci_api_techno, rci_api_deployment, cty_name, rci_api_version_num, STRING_AGG(rci_data_name, ',')," 
			+ "app_domain.rci_dom_name, rci_app_name, fct_domain.rci_dom_name, fol_domain.rci_dom_name, ptrn.rci_prtn_name  "
			+ "FROM rci_api api  "
			+ "LEFT JOIN rci_api_version version on (api.row_id = version.rci_api_version_api_id)  "
			+ "LEFT JOIN rci_api_tag api_tag on (api.row_id = api_tag.rci_api_tag_api_id)"
			+ "LEFT JOIN rci_tag tag on (tag.row_id = api_tag.rci_api_tag_tag_id)"
			+ "LEFT JOIN rci_api_data api_data on (api_data.rci_api_data_api_id = api.row_id)  "
			+ "LEFT JOIN rci_data data on (data.row_id = api_data.rci_api_data_data_id )  "
			+ "LEFT JOIN rci_api_dom_app api_dom_app on (api_dom_app.rci_api_dom_app_api_id = api.row_id)  "
			+ "LEFT JOIN rci_domain app_domain on (api_dom_app.rci_api_dom_app_dom_id = app_domain.row_id )  "
			+ "LEFT JOIN rci_application app on (api_dom_app.rci_api_dom_app_app_id = app.row_id)  "
			+ "LEFT JOIN rci_api_dom_fct api_dom_fct on (api_dom_fct.rci_api_dom_fct_api_id = api.row_id)  "
			+ "LEFT JOIN rci_domain fct_domain on (api_dom_fct.rci_api_dom_fct_dom_id = fct_domain.row_id )  "
			+ "LEFT JOIN rci_api_dom_fol api_dom_fol on (api_dom_fol.rci_api_dom_fol_api_id = api.row_id)  "
			+ "LEFT JOIN rci_domain fol_domain on (api_dom_fol.rci_api_dom_fol_dom_id = fol_domain.row_id )"
			+ "LEFT JOIN rci_partner ptrn on (ptrn.row_id = api.rci_api_partner_id)"
			+ "LEFT JOIN iso_country on (iso_country.row_id = api.rci_api_country_id)"
			+ "GROUP BY rci_api_name, rci_api_scope, rci_api_techno, rci_api_deployment, cty_name, rci_api_version_num," 
			+ "app_domain.rci_dom_name, rci_app_name, fct_domain.rci_dom_name, fol_domain.rci_dom_name, rci_prtn_name";
			
			if (!appFilter.isEmpty())
				sql += "WHERE rci_app_name = '" + appFilter + "' OR app_domain.rci_dom_name = '" + appFilter + "'";
				
			for (String[] api : getGrant().query(sql))
			{
				res.put(new JSONObject()
				.put("apiName", api[0])
				.put("tag", api[1].isEmpty()?JSONObject.NULL:"[" + api[1] + "]")
				.put("scope", api[2].isEmpty()?JSONObject.NULL:api[2])
				.put("techno", api[3].isEmpty()?JSONObject.NULL:api[3])
				//.put("deployment", new JSONArray(api[4].split(";")).toString())
				.put("deployment", "[" + api[4].replace(";",",") + "]")
				.put("country", api[5].isEmpty()?JSONObject.NULL:api[5])
				.put("apiVersion", api[6].isEmpty()?JSONObject.NULL:api[6])	
				.put("dataType", api[7].isEmpty()?JSONObject.NULL:"[" + api[7] + "]")
				.put("applicativeDomainName", api[8].isEmpty()?JSONObject.NULL:api[8])
				.put("applicationName", api[9].isEmpty()?JSONObject.NULL:api[9])
				.put("functionalDomainName", api[10].isEmpty()?JSONObject.NULL:api[10])
				.put("fullOnlineProcess", api[11].isEmpty()?JSONObject.NULL:api[11])
				.put("domain", api[12].isEmpty()?JSONObject.NULL:api[12])
				);
			}
		
		return res;

	}
}

OK on ne parle donc pas d’une API standard Simplicité mais d’une API custom (du coup je ne comprend pas ce que vous voulez dire par le “L’appel fonctionne via l’interface /api de Simplicité” de votre premier post).

Quand je demande de décrire votre cas de test je vous demande de me donner les commandes curl exacte que vous utilisez (comme je l’ai fait dans ma réponse précédente) et qui posent pb. Vous pouvez bien sûr masquer la valeur du token.

NB: Si besoin (re)faites vos appels avec le flag -v pour avoir accès aux headers HTTP.

PS: dans votre code, dans le contexte d’un appel GET cette ligne de code JSONObject req = params.getJSONObject(); n’a à priori pas de sens, de mémoire ce n’est valorisé qu’à partir du body d’une requête POST ou PUT

J’ai testé avec une API custom similaire à la votre:

$ curl -u designer:simplicite https://testdaz40.demo.simplicite.io/api/login
zHYaUibcEU6Z5n2WoyYmZOeFdtfK4XpD8qdwbT4GBhPuJZjLUY

$ curl -H 'Authorization: Bearer zHYaUibcEU6Z5n2WoyYmZOeFdtfK4XpD8qdwbT4GBhPuJZjLUY' https://testdaz40.demo.simplicite.io/api/ext/Test
["hello","world"]

Le code de l’API custom:

package com.simplicite.extobjects.Application;

import org.json.JSONArray;

import com.simplicite.util.exceptions.HTTPException;
import com.simplicite.util.tools.Parameters;

public class Test extends com.simplicite.webapp.services.RESTServiceExternalObject  {
	private static final long serialVersionUID = 1L;
	
	public Object get(Parameters params) throws HTTPException {
		return new JSONArray().put("hello").put("world");
	}
}

Mon instance est une 4.0 à jour out of the box où j’ai uniquement ajouté cet objet externe.

Si vous avez une config particulière (sur la gestion des tokens, sur le mapping d’URI, sur le pooling d’objets, sur l’ident/authent, etc.) il faudrait me l’indiquer car, avec les infos fournies à ce stade, je ne reproduis pas le pb indiqué, ni avec une API standard ni avec cette API custom.

Où puis-je trouver ces informations ? Je n’ai pas conscience d’avoir configuré quelque chose de plus que ce que j’ai envoyé.

En activant les logs SQL je vois que ma requête n’est pas appelée donc ça doit planter avant.
Par contre en requêtant un objet standard avec REST (curl -X GET “/api/rest/RciApplication” -H "Authorization: Bearer ") je n’ai pas l’erreur.

J’avoue ne pas bien comprendre ce que vous faites… et donc je ne peux pas reproduire.

Comme demandé dans mes réponse précédentes, indiquez moi exactement les appels curl que vous faites = les commandes curl completes + les résultats de ces appels (idéalement avec le flag -v de curl pour avoir les headers HTTP) + si c’est pertinent les logs associés à chacun des appels en question.

Pour ce qui est de votre configuration, je parle des params système relatifs aux APIs et aux tokens mais la priorité est déjà que je comprenne ce que vous faites exactement.

D’accord, j’envoie les informations complètes par mail.

Non, masquez juste le hostname de l’URL et le token dans vos copier/coller

Genre

curl -H 'Authorization: Bearer zHY(..)LUY' https://(...)/api/ext/Test

J’ai vérifié => utiliser getGrant().query() ne pose pas de pb non plus:

package com.simplicite.extobjects.Application;

import org.json.JSONArray;

import com.simplicite.util.exceptions.HTTPException;
import com.simplicite.util.tools.Parameters;

public class Test extends com.simplicite.webapp.services.RESTServiceExternalObject  {
	private static final long serialVersionUID = 1L;
	
	public Object get(Parameters params) throws HTTPException {
		String version = getGrant().query("select sys_value from m_system where sys_code = 'VERSION'").get(0)[0];
		return new JSONArray().put("version").put(version);
	}
}
$ curl -H 'Authorization: Bearer zHYaUibcEU6Z5n2WoyYmZOeFdtfK4XpD8qdwbT4GBhPuJZjLUY' https://testdaz40.demo.simplicite.io/api/ext/Test
["version","4.0"]

Désolée je viens de voir qu’il manquait des morceaux dans les Steps de reproduction :neutral_face:
J’ai l’impression que quand je mets des <> ça disparaît à la publication.

Voici en masquant les infos confidentielles entre crochets à la place des <>

curl -s -k -u [LOGIN]:[MDP] “[BASE URL]/api/login?_output=json”

→ là j’ai un token en retour

curl -X GET “[BASE URL]/api/ext/RciApiList” -H “Authorization: Bearer [TOKEN]”

→ erreur
Cannot read field “m_objects” because “this.m_data” is null

Et si j’essaie une API classique
curl -X GET “[BASE URL]/api/rest/RciApplication” -H “Authorization: Bearer [TOKEN]”

→ retour correct sans erreur

Ce forum utilise une syntaxe Markdown cf. Markdown Cheat Sheet | Markdown Guide. Pour préserver les textes “techniques” qu’on copie colle il faut les mettre entre backticks simples - ex: ma commande - ou dans un bloc entre backticks triples - ex:

ma commande

Sinon sur le fond, je ne reproduis pas votre pb, cf. mes tests dans mes réponses précédentes. D’où ma question sur l’éventuelle customisation de certains params système (*TOKEN*, *WEBSERVICES*, etc.) car dans mon cas je suis sur une 4.0 à jour et out of the box = aucun param système customisé ni autres choses specifiques.

Sinon il y a surement plus d’information dans les logs au moment de l’appel de votre objet externe.

Ok c’est noté pour le Markdown.

Pour le reste, ben, mince alors. Je ne crois pas avoir touché aux paramètres systèmes. Y a-t-il une nouvelle version depuis le 22/02 ? Je peux tenter une mise à jour peut-être.

image

image

Ok ça semble la config par défaut sur ces params système (à moins qu’il n’y ait des params de disposition ou utilisateur qui les overrident, les copies d’écran fournies ne permettent pas de le savoir).

Quid des logs sur appel de votre objet externe ?

Dans tous les cas je vous conseille de debugger votre code pour voir où l’erreur se déclenche. Comme dit plus haut cette ligne est douteuse JSONObject req = params.getJSONObject();

NB: Au passage, votre code est vulnérable à de l’injection SQL => il faut utiliser (...)' + Tool.toSQL(maVariable) + '(...) pour se prémunir de ce genre d’attaques basiques.

1 Like

J’ai activé tous les logs possible je ne rentre pas dans la méthode : mes logs de DEBUG ne s’affichent pas en CURL mais ils s’affichent quand j’utilise l’UI de test /api de Simplicité.

Je parlais plutôt de debugger votre code via un IDE en remote debug (ex: sur Eclipse Simplicité® documentation/99-misc/remote-debug ou sur VSCode via notre extension)

Sinon je ne vois pas de raison que des traces serveur (AppLog) s’affichent dans un cas d’appel et pas dans un autre.

Cela dit il y a des mécanisme subtils qui influent sur là où finissent les traces de niveau debug donc le mieux quand on debugge manuellement avec des traces dans le code, c’est d’utiliser des AppLog.info("xxxx", getGrant());

J’utilise bien des AppLog mais clairement je n’entre pas dans ma méthode avec CURL. Est-ce que ça peut être un problème d’infrastructure chez nous ?
Où puis-je débugger avant l’entrée dans ma méthode GET ?

Commencez par un clear cache global et/ou un stop/start histoire d’être sûr que ce n’est pas un effet de cache. Dans mes tests sur mon API custom de test je n’ai pas constaté de pb de cet ordre mais on ne sait jamais d’autant que vous n’êtes à priori pas parfaitement à jour.

Ensuite je ne vois pas de raison qu’un appel depuis la page API tester ou depuis un curl se comportent différemment (en tout cas si les deux sont bien effectués avec le même user et depuis la même machine cliente et sur la même URL). Pendant mes tests j’ai fait les deux sans pb sur mon API custom de test (ici avec le même token):


Vous pouvez tester sur l’instance en question si besoin, elle est encore active: https://testdaz40.demo.simplicite.io designer/simplicite

Pour ce qui est de débugger au niveau où l’appel à la méthode get est effectué, ce n’est pas possible car on est dans les classes système et il n’y a pas de log events pour augmenter le niveau de trace.

Merci David et désolée pour le dérangement, en effet après un redémarrage des serveurs le problème a disparu !

Bonne semaine
Emmanuelle

This topic was automatically closed 60 minutes after the last reply. New replies are no longer allowed.