Google Data Studio – Gitlab connector

Chez AXOPEN, nous souhaitions se brancher sur GitLab pour récupérer la liste des issues pour un certain projet. On s’est donc lancé dans la création d’un connector Gitlab pour Google Data Studio.
Pierre LISERONMis à jour le 23 Mai 2018

Google Data Studio est un super outil de reporting de plus en plus utilisé qui possède de nombreux atouts. En particulier, il permet de créer des plugins / connectors pour se connecter à différentes sources de données.

Gitlab pour Google Data Studio Le besoin

En interne, nous souhaitions se brancher sur GitLabGitLab, c’est une plateforme permettant d’héberger et de gérer des projets web de A à Z. pour récupérer la liste des issues pour un certain projet. On s’est donc lancé dans la création d’un connector Gitlab pour Google Data Studio.

Bon on va pas se mentir la documentation de Google est plutôt faible et peu détaillée. Donc on a pas mal miséré pour trouver des exemples fiables. Mais on y est quand même arrivé et avons réussi à faire ce petit script qui permet de récupérer les issues d’un projet choisi au démarrage.

C’est pas hyper propre, surtout au niveau de la gestion des types de données mais ça marche et c’est déjà pas mal. Et concernant notre besoin c’était largement suffisant. Pour l’utiliser, il suffit de remplir l’APIUne API est un programme permettant à deux applications distinctes de communiquer entre elles et d’échanger des données. KEY (générée depuis l’interface de GitLab) et de modifier les url des endpoints.

En espérant que ça puisse aider des personnes qui ont le même besoin. On pourrait le publier en open source mais nous avons trop de souci avec la documentation pour en faire quelque chose de présentable !

Le script de Gitlab Data Studio Connector pour GitLab

{{< highlight javascript "linenos=table">}}

var API\_KEY = " API\_KEY ";

function isAdminUser(){
  
return true;
  
}
  
function getConfig(request) {

var url = [
  
’https://git.axopen.com/api/v4/projects?private_token=’,
  
API_KEY];
  
var response = JSON.parse(UrlFetchApp.fetch(url.join( ")));
  
var projects = [];
  
response.forEach(function(value) {
  
console.log(value.id);
  
projects.push({
  
" label ": value.path\_with\_namespace.toUpperCase(),
  
" value ": ’AB’+value.id
  
});
  
});

var config = {
  
configParams: [
  
{
  
" type ": " SELECT_SINGLE ",
  
" name ": " SELECT_SINGLE ",
  
" displayName ": " Select one project ",
  
" helpText ": " Pick only one project ",
  
" options ": projects
  
}
  
]
  
};
  
return config;
  
};

var fontDataSchema = [
  
{
  
name: ’id_issue’,
  
label: ’Issue ID’,
  
dataType: ’NUMBER’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
{
  
name: ’title’,
  
label: ’Issue title’,
  
dataType: ’STRING’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
{
  
name: ’created_at’,
  
label: ’Created at’,
  
dataType: ’STRING’
  
},
  
{
  
name: ’updated_at’,
  
label: ’Updated at’,
  
dataType: ’STRING’
  
},
  
{
  
name: ’closed_at’,
  
label: ’Closed at’,
  
dataType: ’STRING’
  
},
  
{
  
name: ’state’,
  
label: ’State’,
  
dataType: ’STRING’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
{
  
name: ’user\_notes\_count’,
  
label: ’User notes count’,
  
dataType: ’NUMBER’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
{
  
name: ’description’,
  
label: ’Description’,
  
dataType: ’STRING’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
{
  
name: ’time_estimate’,
  
label: ’Time estimate’,
  
dataType: ’NUMBER’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
{
  
name: ’total\_time\_spent’,
  
label: ’Total time spent’,
  
dataType: ’NUMBER’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
{
  
name: ’downvotes’,
  
label: ’Downvotes’,
  
dataType: ’NUMBER’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
{
  
name: ’upvotes’,
  
label: ’Upvotes’,
  
dataType: ’STRING’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
{
  
name: ’due_date’,
  
label: ’Due date’,
  
dataType: ’STRING’,
  
semantics: {
  
conceptType: ’DIMENSION’
  
}
  
},
  
];

function getSchema(request) {
  
return {schema: fontDataSchema};
  
};

function getData(request) {

var dataSchema = [];
  
request.fields.forEach(function(field) {
  
for (var i = 0; i < fontDataSchema.length; i++) {
  
if (fontDataSchema[i].name === field.name) {
  
dataSchema.push(fontDataSchema[i]);
  
break;
  
}
  
}
  
});

var url = [
  
’https://git.axopen.com/api/v4/projects/’+request.configParams.SELECT\_SINGLE.substring(2)+’/issues?private\_token=’,
  
API_KEY];
  
var response = JSON.parse(UrlFetchApp.fetch(url.join( ")));

var data = [];
  
response.forEach(function(value) {
  
var values = [];
  
dataSchema.forEach(function(field) {
  
switch(field.name) {
  
case ’id_issue’:
  
values.push(value.id);
  
break;
  
case ’title’:
  
values.push(value.title);
  
break;
  
case ’created_at’:
  
values.push(value.created_at.substring(0,10));
  
break;
  
case ’updated_at’:
  
values.push(value.updated\_at.created\_at.substring(0,10));
  
break;
  
case ’closed_at’:
  
values.push(value.closed\_at.created\_at.substring(0,10));
  
break;
  
case ’state’:
  
values.push(value.state);
  
break;
  
case ’upvotes’:
  
values.push(value.upvotes);
  
break;
  
case ’downvotes’:
  
values.push(value.downvotes);
  
break;
  
case ’due_date’:
  
values.push(value.due\_date.created\_at.substring(0,10));
  
break;
  
case ’time_estimate’:
  
values.push(value.time_estimate);
  
break;
  
case ’total\_time\_spent’:
  
values.push(value.total\_time\_spent);
  
break;
  
case ’user\_notes\_count’:
  
values.push(value.user\_notes\_count);
  
break;
  
case ’description’:
  
values.push(value.description);
  
break;
  
default:
  
values.push( ");
  
}
  
});
  
data.push({
  
values: values
  
});
  
});
  
return {
  
schema: dataSchema,
  
rows: data
  
};
  
};

function getAuthType() {
  
var response = {
  
" type ": " NONE "
  
};
  
return response;
  
}

{{< / highlight >}}