{"id":4945,"date":"2022-04-05T21:51:28","date_gmt":"2022-04-05T19:51:28","guid":{"rendered":"https:\/\/gpmfactory.com\/?p=4945"},"modified":"2022-04-05T21:51:28","modified_gmt":"2022-04-05T19:51:28","slug":"integration-oracle-apex-et-google-sheets-et-google-drive","status":"publish","type":"post","link":"https:\/\/gpmfactory.com\/index.php\/2022\/04\/05\/integration-oracle-apex-et-google-sheets-et-google-drive\/","title":{"rendered":"Int\u00e9gration Oracle APEX et Google Sheets et Google Drive"},"content":{"rendered":"\n\n\n<h2 class=\"wp-block-heading\">Objet<\/h2>\n\n\n\n<p>Il est possible de manipuler des feuilles de calcul Google avec des APIs REST mais cela requiert un peu de codage pour la parsing du payload json. <br>&#8212; Ce POST n&rsquo;est pas encore finalis\u00e9 &#8212;<\/p>\n\n\n\n<p>Normalement il est conseill\u00e9 d&rsquo;utiliser une API cliente selon le langage h\u00f4te (ph, python, nodejs&#8230;). Dans notre cas , PL\/SQL, nous allons utiliser les appels REST natifs car il n&rsquo;existe pas encore de client PL\/SQL..<\/p>\n\n\n\n<p>Pour lire une feuille Google Sheets, consulter la documentation <em>Basic Reading<\/em>: <a rel=\"noreferrer noopener\" href=\"https:\/\/developers.google.com\/sheets\/api\/samples\/reading\" target=\"_blank\">https:\/\/developers.google.com\/sheets\/api\/samples\/reading<\/a><\/p>\n\n\n\n<p>Dans Oracle APEX, on pourrait utiliser une d\u00e9finition de REST Data Source mais le probl\u00e8me est que le <em>parser <\/em>ne reconnait pas les tableaux imbriqu\u00e9s dont voici un \u00e9chantillon:<br>Exemple de <em>payload <\/em>renvoy\u00e9 par l&rsquo;API <\/p>\n\n\n\n<pre id=\"block-f3b5e721-5e1e-4b9c-a8c9-fe9304d45e0a\" class=\"wp-block-preformatted\">{\n\"range\": \"'F2'!A2:D5\",\n\"majorDimension\": \"ROWS\",\n\"values\": [\n[\n\"test\",\n\"4\"\n],\n[\n\"test\",\n\"3\"\n],\n[\n\"test\",\n\"2\"\n],\n[\n\"zert\",\n\"8\"\n]\n]\n}\n\n1 error has occurred\nError during REST Data Source storage or discovery: ORA-40597: JSON path expression syntax error ('$.$.values[*]') JZN-00217: Key step contains unexpected characters at position 3\nFound no data while parsing the service response.<\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"991\" height=\"518\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/04\/image.png\" alt=\"\" class=\"wp-image-4960\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/04\/image.png 991w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/04\/image-300x157.png 300w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/04\/image-768x401.png 768w\" sizes=\"auto, (max-width: 991px) 100vw, 991px\" \/><\/figure>\n\n\n\n<p>Il faut donc se rabattre sur une approche manuelle. Celle qui est expliqu\u00e9e dans ce post consiste \u00e0 effectuer un appel \u00e0 la m\u00e9thode GET dans un fonction PLSQL puis \u00e0 retourner le contenu dans un pipe. Cela permet, au final, de manipuler la feuille \u00e0 partir d&rsquo;une requ\u00eate SQL classique ou bien d&rsquo;une vue.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">l_clob := apex_web_service.make_rest_request(\np_url => 'https:\/\/sheets.googleapis.com\/v4\/spreadsheets\/' || tsheet || '\/values\/' || trange,\np_http_method => 'GET',\np_token_url => 'https:\/\/oauth2.googleapis.com\/token',\np_credential_static_id => '<strong>GoogleSAV<\/strong>'\n);<\/pre>\n\n\n\n<p>Pour le range, il faut remplacer les espaces \u00e9ventuels dans le nom de la feuille par un signe +<\/p>\n\n\n\n<p>ex:      trange varchar2(200) := &lsquo;R\u00e9ponses+au+formulaire+4!A1:D5&rsquo;;<\/p>\n\n\n\n<p>Cr\u00e9ation optionnelle d&rsquo;une vue<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">CREATE OR REPLACE FORCE EDITIONABLE VIEW \"GOOGLE_SHEET1_V\" (\"C1\", \"C2\", \"C3\", \"C4\", \"C5\", \"C7\", \"C8\", \"C9\", \"C10\") AS\nselect \"C1\",\"C2\",\"C3\",\"C4\",\"C5\",\"C7\",\"C8\",\"C9\",\"C10\" \nfrom google_pkg.get_rows('R\u00e9ponses+au+formulaire+4')\n<\/pre>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"344\" src=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/04\/image-1-1024x344.png\" alt=\"\" class=\"wp-image-4962\" srcset=\"https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/04\/image-1-1024x344.png 1024w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/04\/image-1-300x101.png 300w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/04\/image-1-768x258.png 768w, https:\/\/gpmfactory.com\/wp-content\/uploads\/2022\/04\/image-1.png 1240w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Contr\u00f4le d&rsquo;acc\u00e8s.<\/h2>\n\n\n\n<p>Comme pour la plupart des acc\u00e8s \u00e0 des donn\u00e9es priv\u00e9es de Google, il faut utiliser le protocole OAUTH2. <\/p>\n\n\n\n<p>Celui-ci est bien expliqu\u00e9 dans le <a href=\"https:\/\/developers.google.com\/identity\/protocols\/oauth2\" data-type=\"URL\" data-id=\"https:\/\/developers.google.com\/identity\/protocols\/oauth2\" target=\"_blank\" rel=\"noreferrer noopener\">document de Google<\/a>.<\/p>\n\n\n\n<p>M\u00eame si on ne peut pas utiliser les sourec REST, on peut n\u00e9anmoins s&rsquo;appuyer sur les <em>credentials<\/em> pour all\u00e9ger la partie authentification. Sans cela, il faut r\u00e9cup\u00e9rer un code d&rsquo;acc\u00e8s et le convertir en <em>token<\/em>.<\/p>\n\n\n\n<p>Pour Gogle Drive, l&rsquo;int\u00e9gration est plus simple car les REST data source sont op\u00e9rationnels. <br>&#8230; mais, comme il &lsquo;agit d&rsquo;une donn\u00e9e priv\u00e9e, il faut une authentification lors de la phase de design &lsquo;redirection vers la page Google) qui n&rsquo;est pas support\u00e9e par l&rsquo;assistant. Donc, il faut auparavant g\u00e9n\u00e9rer manuellement un token valide qu&rsquo;on fournira en param\u00e8tre de type Header Authorization Bearer &lt;TOKEN>. Le code pour g\u00e9n\u00e9rer ce token et decrit en annexe.<\/p>\n\n\n\n<p>Une fois la d\u00e9couverte du <em>data profile<\/em> r\u00e9alis\u00e9e, on retire le param\u00e8tre <em>Authorization <\/em>qui devient superflu et on sp\u00e9cifie une autorisation bas\u00e9e sur un des <em>credentials <\/em>enregistr\u00e9s (<em>GoogleSAV<\/em> dans notre exemple). L&#8217;emploi du param\u00e8tre p_credential_static_id dans l&rsquo;appel de apex_web_service.make_rest_request est tr\u00e8s pratique car il soulage la t\u00e2che du d\u00e9veloppeur de toute la gestion du cycle de vie du token<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">ANNEXES<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Obtention d&rsquo;un token pour tester une REST data source<\/h3>\n\n\n\n<p>Le principe consiste \u00e0 :<\/p>\n\n\n\n<p>Constituer une URL contenant les parametres :<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>response_code<\/li><li>access_type<\/li><li>redirect_url (https:\/\/xxxxxxxxxx.yyyyy.oraclecloudapps.com\/ords\/demo\/mygoogle\/token) . Cette url correspond \u00e0 un module ORDS custom contenant un handler GET sur le <em>template <\/em>nomm\u00e9e <em>token<\/em>. Pour info, si on veut fourni un id de session qui sera retourn\u00e9e par Google, on ajoute le param\u00e8tre <em>state <\/em>qui contiendra ce qu&rsquo;on veut bien y mettre.<\/li><li>client_id<\/li><li>Demande d&rsquo;un code d&rsquo;acc\u00e8s (dans une branch before Header d&rsquo;une page afin de provoquer une redirection<\/li><\/ul>\n\n\n\n<p>Affichage d&rsquo;un page d&rsquo;authentification Google<\/p>\n\n\n\n<p>Affichage optionnelle d&rsquo;une page de consentement (envoy\u00e9e par Google)<\/p>\n\n\n\n<p>Redirection vers l&rsquo;URL envoy\u00e9e en param\u00e8tre<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">https:\/\/accounts.google.com\/o\/oauth2\/auth?<br>scope=https:\/\/www.googleapis.com\/auth\/drive<br>&amp;response_type=code<br>&amp;access_type=offline<br>&amp;redirect_uri=<br>&amp;P0_REDIRECT.<br>&amp;client_id=xxxxxxxxxxxxxx.apps.googleusercontent.com<\/pre>\n\n\n\n<p>Comme la string ne peut pas depasser 255 caract\u00e8res, on pr\u00e9enregistre l&rsquo;url de redirection dans une variable globale P0_REDIRECT que l&rsquo;on mentionne avec la syntaxe &amp;P0_REDIRECT.<\/p>\n\n\n\n<p>Conversion du code d&rsquo;acc\u00e8s en <em>token <\/em>(cf code source du package GOOGLE_PKG en annexe)<\/p>\n\n\n\n<p>Affichage du token pour utilisation ult\u00e9rieure. La dur\u00e9e de vie de vie est d&rsquo;une heure environ. Ce token  devra \u00eatre fourni dans la variable de header nomm\u00e9e AUTHORIZATION et devra \u00eatre pr\u00e9c\u00e9d\u00e9 par le mot clef <em>Bearer <\/em>suivi d&rsquo;un espace.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">declare\nl_token varchar2(500);\nl_code varchar2(500);\nbegin\nl_code := utl_url.unescape(GOOGLE_PKG.at_get_env_parameter('code')\n);\nl_token := GOOGLE_PKG.get_token(l_code);\nhtp.p('token=' || l_token);\nhtp.p('scope=');\nhtp.p(utl_url.unescape(GOOGLE_PKG.at_get_env_parameter('scope'))\n);\nend;\n<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Objet Il est possible de manipuler des feuilles de calcul Google avec des APIs REST mais cela requiert un peu de codage pour la&#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"ppma_author":[150],"class_list":["post-4945","post","type-post","status-publish","format-standard","hentry","category-non-classe"],"authors":[{"term_id":150,"user_id":1,"is_guest":0,"slug":"admin8700","display_name":"Patrick","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/209d5ed69b74d288390621ab4c1d3773?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/4945","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/comments?post=4945"}],"version-history":[{"count":16,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/4945\/revisions"}],"predecessor-version":[{"id":4963,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/posts\/4945\/revisions\/4963"}],"wp:attachment":[{"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/media?parent=4945"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/categories?post=4945"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/tags?post=4945"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/gpmfactory.com\/index.php\/wp-json\/wp\/v2\/ppma_author?post=4945"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}