diff --git a/.gitignore b/.gitignore
index 35d328e6d61fa10cf76e954c16d272bb6dd0b0f5..b6c5e5d6cae8c12ec44d39ef7b9bc8cd18b98eb1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,7 +28,7 @@ desktop.ini
 .floo
 .flooignore
 .idea/
-
+.vscode/
 # Default
 # /!\ KEEP THIS SECTION THE LAST ONE
 !.gitkeep
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index fef13ef2d2249f6cb2e84092c31acb1d14f8da6a..1ecbeffe212bf59e5699fd0fd98116492ffda2fd 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -6,8 +6,57 @@
 # Note that environment variables can be set in several places
 # See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
 stages:
-- test
+  - test
+  - build
+  - publish
+
 sast:
   stage: test
 include:
-- template: Security/SAST.gitlab-ci.yml
+  - template: Security/SAST.gitlab-ci.yml
+
+build-dev:
+  image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:16.14.2-alpine3.14
+  stage: build
+  before_script:
+    - apk add git
+    - apk add bash
+  script:
+    - yarn
+    - yarn build
+    - git config --global user.name build-token
+    - git config --global user.email "$GIT_USER"
+    - git config --global user.password "$GIT_PWD"
+    - git config user.email "$GIT_USER"
+    - git remote set-url origin https://"$GIT_USER":"$GIT_PWD"@forge.grandlyon.com/web-et-numerique/llle_project/enedis-sge-konnector.git
+    - git config --global credential.helper store
+    - yarn deploy-dev
+
+build:
+  image: ${CI_DEPENDENCY_PROXY_DIRECT_GROUP_IMAGE_PREFIX}/node:16.14.2-alpine3.14
+  stage: build
+  before_script:
+    - apk add git
+    - apk add bash
+  script:
+    - yarn
+    - yarn build
+    - git config --global user.name build-token
+    - git config --global user.email "$GIT_USER"
+    - git config --global user.password "$GIT_PWD"
+    - git config user.email "$GIT_USER"
+    - git remote set-url origin https://"$GIT_USER":"$GIT_PWD"@forge.grandlyon.com/web-et-numerique/llle_project/enedis-sge-konnector.git
+    - git config --global credential.helper store
+    - yarn deploy
+  only:
+    - master
+
+publish:
+  stage: publish
+  before_script:
+    - apk add git
+  script:
+    - yarn cozyPublish
+  only:
+    - tags
+  when: manual
diff --git a/manifest.konnector b/manifest.konnector
index 4196b41f2d5a992ab20dd690dd8be1d5af580213..e26d1d5f5e99005e71c97dea70c3171a24e71b9e 100644
--- a/manifest.konnector
+++ b/manifest.konnector
@@ -1,24 +1,18 @@
 {
-  "version": "1.0.0",
+  "version": "1.0.1",
   "name": "Enedis SGE",
   "type": "konnector",
   "language": "node",
   "icon": "icon.png",
-  "slug": "enedissgegrandlyon",
+  "slug": "enedis-sge-grandlyon",
   "source": "https://forge.grandlyon.com/web-et-numerique/llle_project/enedis-sge-konnector.git",
   "editor": "Cozy",
   "vendor_link": "Link to the target website",
   "categories": ["energy"],
   "frequency": "daily",
   "fields": {
-    "pdl": {
+    "pointId": {
       "type": "text"
-    },
-    "nom": {
-      "type": "password"
-    },
-    "prenom": {
-      "type": "password"
     }
   },
   "data_types": [
@@ -26,7 +20,11 @@
   "screenshots": [],
   "permissions": {
     "accounts": {
-      "type": "io.cozy.accounts"
+      "type": "io.cozy.accounts",
+      "verbs": ["GET"]
+    },
+    "files": {
+      "type": "io.cozy.files"
     },
      "enedis data": {
       "type": "com.grandlyon.enedis.*"
@@ -45,6 +43,9 @@
         "enedis data": {
           "description": "Requises pour accéder et stocker les données collectées par le compteur Linky et exposées par les API Enedis (consommations d’électricité à la demi-heure, au jour, mois et année). "
         },
+        "files": {
+          "description": "Cozy files"
+        },
         "accounts": {
           "description": "Utilisé pour accéder à vos données de consommation."
         }
@@ -57,6 +58,9 @@
         "enedis data": {
           "description": "Required to access and store the data collected by the Linky meter and exposed by Enedis APIs (half-an-hour, daily, monthly and yearly consumption)."
         },
+        "files": {
+          "description": "Cozy files"
+        },
         "accounts": {
           "description": "Used to access your consumption data."
         }
diff --git a/package.json b/package.json
index 66f38956166c45037afe3a2470eec6c2098506ae..4fe35f1d23ffddd902dc14581a41098e311d9689 100644
--- a/package.json
+++ b/package.json
@@ -32,12 +32,17 @@
     "clean": "rm -rf ./data",
     "build": "webpack",
     "lint": "eslint --fix .",
-    "deploy": "git-directory-deploy --directory build/ --branch ${DEPLOY_BRANCH:-build} --repo=${DEPLOY_REPOSITORY:-$npm_package_repository_url}",
+    "deploy": "git-directory-deploy --directory build/ --branch ${DEPLOY_BRANCH:-build}",
+    "deploy-dev": "git-directory-deploy --directory build/ --branch ${DEPLOY_BRANCH:-build-dev}",
     "cozyPublish": "cozy-app-publish --token $REGISTRY_TOKEN --build-commit $(git rev-parse ${DEPLOY_BRANCH:-build})",
     "travisDeployKey": "./bin/generate_travis_deploy_key"
   },
   "dependencies": {
-    "cozy-konnector-libs": "4.52.1"
+    "cozy-konnector-libs": "4.52.1",
+    "easy-soap-request": "^4.7.0",
+    "moment": "^2.29.3",
+    "moment-timezone": "^0.5.34",
+    "xml2js": "^0.4.23"
   },
   "devDependencies": {
     "cozy-jobs-cli": "1.17.6",
diff --git a/src/aggregate.js b/src/aggregate.js
new file mode 100644
index 0000000000000000000000000000000000000000..808d34f0c36877aa7ab15856851e60a8a9168965
--- /dev/null
+++ b/src/aggregate.js
@@ -0,0 +1,106 @@
+// @ts-check
+const { log, cozyClient } = require('cozy-konnector-libs')
+
+/**
+ * Retrieve and remove old data for a specific doctype
+ * Return an Array of agregated data
+ */
+async function buildAgregatedData(data, doctype) {
+  let agregatedData = []
+  // eslint-disable-next-line no-unused-vars
+  for (let [key, value] of Object.entries(data)) {
+    const data = await buildDataFromKey(doctype, key, value)
+    const oldValue = await resetInProgressAggregatedData(data, doctype)
+    data.load += oldValue
+    agregatedData.push(data)
+  }
+  return agregatedData
+}
+
+/**
+ * Format an entry for DB storage
+ * using key and value
+ * For year doctype: key = "YYYY"
+ * For month doctype: key = "YYYY-MM"
+ */
+async function buildDataFromKey(doctype, key, value) {
+  let year, month, day, hour
+  if (doctype === 'com.grandlyon.enedis.year') {
+    year = key
+    month = 1
+    day = 0
+    hour = 0
+  } else if (doctype === 'com.grandlyon.enedis.month') {
+    const split = key.split('-')
+    year = split[0]
+    month = split[1]
+    day = 0
+    hour = 0
+  } else {
+    const split = key.split('-')
+    year = split[0]
+    month = split[1]
+    day = split[2]
+    hour = split[3]
+  }
+  return {
+    load: Math.round(value * 10000) / 10000,
+    year: parseInt(year),
+    month: parseInt(month),
+    day: parseInt(day),
+    hour: parseInt(hour),
+    minute: 0,
+  }
+}
+
+/**
+ * Function handling special case.
+ * The temporary aggregated data need to be remove in order for the most recent one te be saved.
+ * ex for com.grandlyon.enedis.year :
+ * { load: 76.712, year: 2020, ... } need to be replace by
+ * { load: 82.212, year: 2020, ... } after enedis data reprocess
+ */
+async function resetInProgressAggregatedData(data, doctype) {
+  // /!\ Warning: cannot use mongo queries because not supported for dev by cozy-konnectors-libs
+  log('debug', doctype, 'Remove aggregated data for')
+  const result = await cozyClient.data.findAll(doctype)
+  if (result && result.length > 0) {
+    // Filter data to remove
+    var filtered = []
+    if (doctype === 'com.grandlyon.enedis.year') {
+      // Yearly case
+      filtered = result.filter(function(el) {
+        return el.year == data.year
+      })
+    } else if (doctype === 'com.grandlyon.enedis.month') {
+      // Monthly case
+      filtered = result.filter(function(el) {
+        return el.year == data.year && el.month == data.month
+      })
+    } else {
+      // Hourly case
+      filtered = result.filter(function(el) {
+        return (
+          el.year == data.year &&
+          el.month == data.month &&
+          el.day == data.day &&
+          el.hour == data.hour
+        )
+      })
+    }
+    // Remove data
+    let sum = 0.0
+    // eslint-disable-next-line no-unused-vars
+    for (const doc of filtered) {
+      sum += doc.load
+      log('debug', doc, 'Removing this entry for ' + doctype)
+      await cozyClient.data.delete(doctype, doc)
+    }
+    return sum
+  }
+  return 0.0
+}
+
+module.exports = {
+  buildAgregatedData,
+}
diff --git a/src/index.js b/src/index.js
index be21704979cb340475979e699619d819bb0afd2a..197ad2aa3ce82ed988f99fe9b76d8768d9cfa0a9 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,26 +1,49 @@
 // @ts-check
 const {
   BaseKonnector,
-  requestFactory,
-  scrape,
   log,
-  utils,
+  hydrateAndFilter,
+  addData,
+  errors,
 } = require('cozy-konnector-libs')
-const request = requestFactory({
-  // The debug mode shows all the details about HTTP requests and responses. Very useful for
-  // debugging but very verbose. This is why it is commented out by default
-  // debug: true,
-  // Activates [cheerio](https://cheerio.js.org/) parsing on each page
-  cheerio: true,
-  // If cheerio is activated do not forget to deactivate json parsing (which is activated by
-  // default in cozy-konnector-libs
-  json: false,
-  // This allows request-promise to keep cookies between requests
-  jar: true,
-})
-
-const VENDOR = 'template'
-const baseUrl = 'http://books.toscrape.com'
+const soapRequest = require('easy-soap-request')
+const moment = require('moment')
+require('moment-timezone')
+const xml2js = require('xml2js')
+const { buildAgregatedData } = require('./aggregate')
+const {
+  parseSgeXmlData,
+  parseSgeXmlTechnicalData,
+  formateDataForDoctype,
+  parseTags,
+  parseValue,
+} = require('./parsing')
+const {
+  userTechnicalData,
+  userMaxPower,
+  userMesureDetailles,
+  rechercherPoint,
+  activateDataCollect,
+  stopDataCollect,
+  getInseeCode,
+  updateBoConsent,
+  createBoConsent,
+  getBoConsent,
+  deleteBoConsent,
+} = require('./request')
+moment.locale('fr') // set the language
+moment.tz.setDefault('Europe/Paris') // set the timezone
+
+/*** Connector Constants ***/
+const manualExecution =
+  process.env.COZY_JOB_MANUAL_EXECUTION === 'true' ? true : false
+let startDailyDate = manualExecution
+  ? moment().subtract(12, 'month')
+  : moment().subtract(6, 'month')
+let startDailyDateString = startDailyDate.format('YYYY-MM-DD')
+const startLoadDate = moment().subtract(7, 'day')
+const endDate = moment()
+const endDateString = endDate.format('YYYY-MM-DD')
 
 module.exports = new BaseKonnector(start)
 
@@ -30,96 +53,439 @@ module.exports = new BaseKonnector(start)
 // cozyParameters are static parameters, independents from the account. Most often, it can be a
 // secret api key.
 async function start(fields, cozyParameters) {
+  log('info', 'Gathering data ...')
+  let baseUrl = fields.wso2BaseUrl
+  let apiAuthKey = fields.apiToken
+  let loginUtilisateur = fields.loginUtilisateur
   log('info', 'Authenticating ...')
-  if (cozyParameters) log('debug', 'Found COZY_PARAMETERS')
-  await authenticate.bind(this)(fields.login, fields.password)
+  //TODO: Verify if condition is working in local and on build version
+  if (cozyParameters && Object.keys(cozyParameters).length !== 0) {
+    log('debug', 'Found COZY_PARAMETERS')
+    baseUrl = cozyParameters.secret.wso2BaseUrl
+    apiAuthKey = cozyParameters.secret.apiToken
+    loginUtilisateur = cozyParameters.secret.loginUtilisateur
+  }
+
+  /**
+   * If it's first start we have to do the following operations:
+   * - verify pdl are matching
+   * - BO: create backoffice consent
+   * - get contract start date and store it
+   * - activate half-hour
+   * - BO: update consent with service ID
+   */
+  log('info', 'User Logging...')
+
+  if (await isFirstStart()) {
+    if (!(await verifyUserIdentity(fields))) {
+      throw errors.LOGIN_FAILED
+    }
+    await createBoConsent()
+    //TODO: remove because useless ? Done later in code
+    // const startDate = await getDataStartDate(
+    //   baseUrl,
+    //   apiAuthKey,
+    //   loginUtilisateur,
+    //   fields.pointId
+    // )
+    await activateDataCollect()
+    await updateBoConsent()
+  } else {
+    await getBoConsent()
+    if (!(await verifyUserIdentity(fields))) {
+      await deleteBoConsent()
+      await stopDataCollect()
+      throw errors.TERMS_VERSION_MISMATCH
+    }
+  }
   log('info', 'Successfully logged in')
-  // The BaseKonnector instance expects a Promise as return of the function
-  log('info', 'Fetching the list of documents')
-  const $ = await request(`${baseUrl}/index.html`)
-  // cheerio (https://cheerio.js.org/) uses the same api as jQuery (http://jquery.com/)
-  log('info', 'Parsing list of documents')
-  const documents = await parseDocuments($)
-
-  // Here we use the saveBills function even if what we fetch are not bills,
-  // but this is the most common case in connectors
-  log('info', 'Saving data to Cozy')
-  await this.saveBills(documents, fields, {
-    // This is a bank identifier which will be used to link bills to bank operations. These
-    // identifiers should be at least a word found in the title of a bank operation related to this
-    // bill. It is not case sensitive.
-    identifiers: ['books'],
-  })
+
+  await gatherData(baseUrl, apiAuthKey, loginUtilisateur, fields.pointId)
+}
+
+/**
+ * Verify user identity
+ * @param {object} fields
+ */
+async function verifyUserIdentity(fields) {
+  const inseeCode = getInseeCode(fields.postalCode)
+  const user = await findUser(
+    fields.name,
+    fields.addresse,
+    fields.postalCode,
+    inseeCode
+  )
+  if (fields.pointId !== user.pointId) {
+    log('error', 'PointId does not match')
+    return false
+  }
+  return true
+}
+
+/**
+ * Main method for gathering data
+ * @param {string} baseUrl
+ * @param {string} apiAuthKey
+ * @param {string} loginUtilisateur
+ * @param {number} pointId
+ */
+async function gatherData(baseUrl, apiAuthKey, loginUtilisateur, pointId) {
+  log('info', 'Querying data...')
+  await getDataStartDate(
+    `${baseUrl}/enedis_SGE_ConsultationDonneesTechniquesContractuelles/1.0`,
+    apiAuthKey,
+    loginUtilisateur,
+    pointId
+  )
+  await getData(
+    `${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
+    apiAuthKey,
+    loginUtilisateur,
+    pointId
+  )
+  await getMaxPowerData(
+    `${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
+    apiAuthKey,
+    loginUtilisateur,
+    pointId
+  )
+  await getDataHalfHour(
+    `${baseUrl}/enedis_SGE_ConsultationMesuresDetaillees/1.0`,
+    apiAuthKey,
+    loginUtilisateur,
+    pointId
+  )
+  log('info', 'Querying data: done')
 }
 
-// This shows authentication using the [signin function](https://github.com/konnectors/libs/blob/master/packages/cozy-konnector-libs/docs/api.md#module_signin)
-// even if this in another domain here, but it works as an example
-function authenticate(username, password) {
-  return this.signin({
-    url: `http://quotes.toscrape.com/login`,
-    formSelector: 'form',
-    formData: { username, password },
-    // The validate function will check if the login request was a success. Every website has a
-    // different way to respond: HTTP status code, error message in HTML ($), HTTP redirection
-    // (fullResponse.request.uri.href)...
-    validate: (statusCode, $, fullResponse) => {
-      log(
-        'debug',
-        fullResponse.request.uri.href,
-        'not used here but should be useful for other connectors'
-      )
-      // The login in toscrape.com always works except when no password is set
-      if ($(`a[href='/logout']`).length === 1) {
-        return true
-      } else {
-        // cozy-konnector-libs has its own logging function which format these logs with colors in
-        // standalone and dev mode and as JSON in production mode
-        log('error', $('.error').text())
-        return false
-      }
+/**
+ *
+ * @param {string} url
+ * @param {string} apiAuthKey
+ * @param {string} userLogin
+ * @param {number} pointId
+ */
+async function getDataStartDate(url, apiAuthKey, userLogin, pointId) {
+  log('info', 'Fetching data start date')
+  const sampleHeaders = {
+    'Content-Type': 'text/xml;charset=UTF-8',
+    apikey: apiAuthKey,
+  }
+
+  const { response } = await soapRequest({
+    url: url,
+    headers: sampleHeaders,
+    xml: userTechnicalData(pointId, userLogin),
+  }).catch(err => {
+    log('error', 'technicalDataResponse')
+    log('error', err)
+    return err
+  })
+
+  xml2js.parseString(
+    response.body,
+    {
+      tagNameProcessors: [parseTags],
+      valueProcessors: [parseValue],
+      explicitArray: false,
     },
+    processStartDate()
+  )
+}
+
+/**
+ * Get hour data
+ * @param {string} url
+ * @param {string} apiAuthKey
+ * @param {string} userLogin
+ * @param {number} pointId
+ */
+async function getData(url, apiAuthKey, userLogin, pointId) {
+  log('info', 'Fetching data')
+  const sampleHeaders = {
+    'Content-Type': 'text/xml;charset=UTF-8',
+    apikey: apiAuthKey,
+  }
+
+  setStartDate()
+
+  const { response } = await soapRequest({
+    url: url,
+    headers: sampleHeaders,
+    xml: userMesureDetailles(
+      pointId,
+      userLogin,
+      startDailyDateString,
+      endDateString
+    ),
+  }).catch(err => {
+    log('error', 'userMesureDetailles')
+    log('error', err)
+    return err
   })
+
+  xml2js.parseString(
+    response.body,
+    {
+      tagNameProcessors: [parseTags],
+      valueProcessors: [parseValue],
+      explicitArray: false,
+    },
+    processData()
+  )
 }
 
-// The goal of this function is to parse a HTML page wrapped by a cheerio instance
-// and return an array of JS objects which will be saved to the cozy by saveBills
-// (https://github.com/konnectors/libs/blob/master/packages/cozy-konnector-libs/docs/api.md#savebills)
-function parseDocuments($) {
-  // You can find documentation about the scrape function here:
-  // https://github.com/konnectors/libs/blob/master/packages/cozy-konnector-libs/docs/api.md#scrape
-  const docs = scrape(
-    $,
+/**
+ * Get Max power data
+ * @param {string} url
+ * @param {string} apiAuthKey
+ * @param {string} userLogin
+ * @param {number} pointId
+ */
+async function getMaxPowerData(url, apiAuthKey, userLogin, pointId) {
+  log('info', 'Fetching Max Power data')
+  const sampleHeaders = {
+    'Content-Type': 'text/xml;charset=UTF-8',
+    apikey: apiAuthKey,
+  }
+
+  setStartDate()
+
+  const { response } = await soapRequest({
+    url: url,
+    headers: sampleHeaders,
+    xml: userMaxPower(pointId, userLogin, startDailyDateString, endDateString),
+  }).catch(err => {
+    log('error', 'getMaxPowerData')
+    log('error', err)
+    return err
+  })
+
+  xml2js.parseString(
+    response.body,
     {
-      title: {
-        sel: 'h3 a',
-        attr: 'title',
-      },
-      amount: {
-        sel: '.price_color',
-        parse: normalizePrice,
-      },
-      fileurl: {
-        sel: 'img',
-        attr: 'src',
-        parse: src => `${baseUrl}/${src}`,
-      },
+      tagNameProcessors: [parseTags],
+      valueProcessors: [parseValue],
+      explicitArray: false,
     },
-    'article'
+    processData('com.grandlyon.enedis.maxpower')
   )
-  return docs.map(doc => ({
-    ...doc,
-    // The saveBills function needs a date field
-    // even if it is a little artificial here (these are not real bills)
-    date: new Date(),
-    currency: 'EUR',
-    filename: `${utils.formatDate(new Date())}_${VENDOR}_${doc.amount.toFixed(
-      2
-    )}EUR${doc.vendorRef ? '_' + doc.vendorRef : ''}.jpg`,
-    vendor: VENDOR,
-  }))
 }
 
-// Convert a price string to a float
-function normalizePrice(price) {
-  return parseFloat(price.replace('£', '').trim())
+/**
+ * If start date exceed the maximum amount of data we can get with one query
+ * get only 36 month
+ */
+function setStartDate() {
+  if (moment(endDate).diff(startDailyDate, 'months', true) > 36) {
+    log(
+      'info',
+      'Start date exceed 36 month, setting start date to current date minus 36 month'
+    )
+    startDailyDate = moment(endDate).subtract(36, 'month')
+    startDailyDateString = startDailyDate.format('YYYY-MM-DD')
+  }
+}
+
+/**
+ * Get half-hour data
+ * @param {string} url
+ * @param {string} apiAuthKey
+ * @param {string} userLogin
+ * @param {number} pointId
+ */
+async function getDataHalfHour(url, apiAuthKey, userLogin, pointId) {
+  log('info', 'Fetching data')
+  const sampleHeaders = {
+    'Content-Type': 'text/xml;charset=UTF-8',
+    apikey: apiAuthKey,
+  }
+
+  let MAX_HISTO = 4
+  // If manual execution, retrieve only 1 week
+  if (manualExecution) {
+    MAX_HISTO = 1
+  }
+  for (var i = 0; i < MAX_HISTO; i++) {
+    log('info', 'launch process with history')
+    const increamentedStartDateString = moment(startLoadDate)
+      .subtract(7 * i, 'day')
+      .format('YYYY-MM-DD')
+    const incrementedEndDateString = moment(endDate)
+      .subtract(7 * i, 'day')
+      .format('YYYY-MM-DD')
+    const { response } = await soapRequest({
+      url: url,
+      headers: sampleHeaders,
+      xml: userMesureDetailles(
+        pointId,
+        userLogin,
+        increamentedStartDateString,
+        incrementedEndDateString,
+        'COURBE',
+        'PA'
+      ),
+    }).catch(err => {
+      log('error', 'userMesureDetailles half-hour')
+      log('error', err)
+      return err
+    })
+
+    xml2js.parseString(
+      response.body,
+      {
+        tagNameProcessors: [parseTags],
+        valueProcessors: [parseValue],
+        explicitArray: false,
+      },
+      processData('com.grandlyon.enedis.minute')
+    )
+  }
+}
+
+/**
+ * Parse data
+ * @param {string} doctype
+ * @returns
+ */
+function processData(doctype = 'com.grandlyon.enedis.day') {
+  return async (err, result) => {
+    if (err) {
+      log('error', err)
+      throw err
+    }
+    // Return only needed part of info
+    const data = parseSgeXmlData(result)
+    const processedDailyData = await storeData(
+      await formateDataForDoctype(data),
+      doctype,
+      ['year', 'month', 'day', 'hour', 'minute']
+    )
+
+    log('info', 'Agregate enedis daily data for month and year')
+    if (doctype === 'com.grandlyon.enedis.day') {
+      log('info', 'Agregating...')
+      await agregateMonthAndYearData(processedDailyData)
+    }
+  }
+}
+
+/**
+ * Store an accurate start date based on contrat start
+ */
+function processStartDate() {
+  return async (err, result) => {
+    if (err) {
+      log('error', err)
+      throw err
+    }
+    // update start Date with contract openning date
+    try {
+      startDailyDate = moment(parseSgeXmlTechnicalData(result), 'YYYY-MM-DD')
+      startDailyDateString = startDailyDate.format('YYYY-MM-DD')
+    } catch (err) {
+      log('error', err)
+      //TODO: custom error ?
+      throw err
+    }
+  }
+}
+
+/**
+ * Save data in the right doctype db and prevent duplicated keys
+ * @param {EnedisKonnectorData[]} data
+ * @param {string} doctype
+ * @param {string[]} filterKeys
+ * @returns {Promise<*>}
+ */
+async function storeData(data, doctype, filterKeys) {
+  log('debug', doctype, 'Store into')
+  const filteredDocuments = await hydrateAndFilter(data, doctype, {
+    keys: filterKeys,
+  })
+  await addData(filteredDocuments, doctype)
+  return filteredDocuments
+}
+
+/**
+ * Agregate data from daily data to monthly and yearly data
+ */
+async function agregateMonthAndYearData(data) {
+  // Sum year and month values into object with year or year-month as keys
+  if (data && data.length > 0) {
+    let monthData = {}
+    let yearData = {}
+    data.forEach(element => {
+      element.year + '-' + element.month in monthData
+        ? (monthData[element.year + '-' + element.month] += element.load)
+        : (monthData[element.year + '-' + element.month] = element.load)
+      element.year in yearData
+        ? (yearData[element.year] += element.load)
+        : (yearData[element.year] = element.load)
+    })
+    // Agregation for Month data
+    const agregatedMonthData = await buildAgregatedData(
+      monthData,
+      'com.grandlyon.enedis.month'
+    )
+    await storeData(agregatedMonthData, 'com.grandlyon.enedis.month', [
+      'year',
+      'month',
+    ])
+    // Agregation for Year data
+    const agregatedYearData = await buildAgregatedData(
+      yearData,
+      'com.grandlyon.enedis.year'
+    )
+    await storeData(agregatedYearData, 'com.grandlyon.enedis.year', ['year'])
+  }
+}
+
+/**
+ * @returns {boolean}
+ */
+function isFirstStart() {
+  //TODO: Implement
+  return false
+}
+
+/**
+ * @return {User}
+ */
+async function findUser(
+  url,
+  apiAuthKey,
+  appLogin,
+  name,
+  addresse,
+  postalCode,
+  inseeCode
+) {
+  log('info', 'Fetching user data')
+  const sampleHeaders = {
+    'Content-Type': 'text/xml;charset=UTF-8',
+    apikey: apiAuthKey,
+  }
+
+  const { response } = await soapRequest({
+    url: url,
+    headers: sampleHeaders,
+    xml: rechercherPoint(appLogin, name, addresse, postalCode, inseeCode),
+  }).catch(err => {
+    log('error', 'rechercherPointResponse')
+    log('error', err)
+    throw errors.LOGIN_FAILED
+    //TODO: handling code error SGT4F6 and SGT432 into USER_ACTIon_NEEDED
+  })
+
+  //TODO: handle reply
+  xml2js.parseString(
+    response.body,
+    {
+      tagNameProcessors: [parseTags],
+      valueProcessors: [parseValue],
+      explicitArray: false,
+    },
+    processStartDate()
+  )
 }
diff --git a/src/parsing.js b/src/parsing.js
new file mode 100644
index 0000000000000000000000000000000000000000..64e6e7e57bd7e034d79bc4aceac97bce4ec11081
--- /dev/null
+++ b/src/parsing.js
@@ -0,0 +1,85 @@
+// @ts-check
+const { log } = require('cozy-konnector-libs')
+const moment = require('moment')
+
+/**
+ * Return start date
+ * @param {string} result
+ * @returns {string}
+ */
+function parseSgeXmlTechnicalData(result) {
+  log('info', 'Parsing technical data')
+  let json = JSON.stringify(result)
+  return JSON.parse(json)['Envelope']['Body'][
+    'consulterDonneesTechniquesContractuellesResponse'
+  ]['point']['donneesGenerales'][
+    'dateDerniereModificationFormuleTarifaireAcheminement'
+  ]
+}
+
+/**
+ * Parsing SGE xml reply to get only mesure data
+ * @param {string} result
+ * @returns {SGEData[]}
+ */
+function parseSgeXmlData(result) {
+  log('info', 'Parsing list of documents')
+  let json = JSON.stringify(result)
+  return JSON.parse(json)['Envelope']['Body'][
+    'consulterMesuresDetailleesResponse'
+  ]['grandeur']['mesure']
+}
+
+/**
+ * Format data for DB storage
+ * @param {SGEData[]} data
+ * @returns {Promise<EnedisKonnectorData[]>} Parsed timestamp array
+ */
+async function formateDataForDoctype(data) {
+  log('info', 'Formating data')
+  return data.map(record => {
+    let date = moment(record.d, 'YYYY/MM/DD h:mm:ss')
+    return {
+      load: record.v,
+      year: parseInt(date.format('YYYY')),
+      month: parseInt(date.format('M')),
+      day: parseInt(date.format('D')),
+      hour: parseInt(date.format('H')),
+      minute: parseInt(date.format('m')),
+    }
+  })
+}
+
+/**
+ * Format tag in order to be manipulated easly
+ * @param {string} name
+ * @returns {string} name
+ */
+function parseTags(name) {
+  if (name.split(':')[1] !== undefined) {
+    return name.split(':')[1]
+  }
+  return name
+}
+
+/**
+ *
+ * @param {string} value
+ * @param {string} name
+ * @returns {string|number} value
+ */
+function parseValue(value, name) {
+  // Wh => KWh
+  if (name === 'v') {
+    return parseFloat((parseInt(value) / 1000).toFixed(2))
+  }
+  return value
+}
+
+module.exports = {
+  parseSgeXmlData,
+  parseSgeXmlTechnicalData,
+  formateDataForDoctype,
+  parseTags,
+  parseValue,
+}
diff --git a/src/request.js b/src/request.js
new file mode 100644
index 0000000000000000000000000000000000000000..c8bcc1bdac5876b79a5af2744bc0df94e2338aa3
--- /dev/null
+++ b/src/request.js
@@ -0,0 +1,314 @@
+// @ts-check
+const { log } = require('cozy-konnector-libs')
+
+/**
+ * Query SGE in order to get info
+ * @param {number} pointId
+ * @param {string} appLogin
+ * @param {string} startDt
+ * @param {string} endDt
+ * @returns {string}
+ */
+function userMesureDetailles(
+  pointId,
+  appLogin,
+  startDt,
+  endDt,
+  mesureType = 'ENERGIE',
+  unit = 'EA'
+) {
+  log(
+    'info',
+    `Query data ${mesureType}/${unit} between ${startDt} and ${endDt}`
+  )
+  return `<?xml version='1.0' encoding='utf-8'?>
+  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+     xmlns:v2="http://www.enedis.fr/sge/b2b/services/consultationmesuresdetaillees/v2.0"
+     xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
+     <soapenv:Header/>
+     <soapenv:Body>
+        <v2:consulterMesuresDetaillees>
+           <demande>
+              <initiateurLogin>${appLogin}</initiateurLogin>
+              <pointId>${pointId}</pointId>
+              <mesuresTypeCode>${mesureType}</mesuresTypeCode>
+              <grandeurPhysique>${unit}</grandeurPhysique>
+              <soutirage>true</soutirage>
+              <injection>false</injection>
+              <dateDebut>${startDt}</dateDebut>
+              <dateFin>${endDt}</dateFin>
+              <mesuresCorrigees>false</mesuresCorrigees>
+              <accordClient>true</accordClient>
+           </demande>
+        </v2:consulterMesuresDetaillees>
+     </soapenv:Body>
+  </soapenv:Envelope>
+  `
+}
+
+/**
+ * Get user technical data
+ * @param {number} pointId
+ * @param {string} appLogin
+ * @returns {string}
+ */
+function userTechnicalData(pointId, appLogin) {
+  log('info', `Query userMesureDetailles`)
+  return `<?xml version='1.0' encoding='utf-8'?>
+  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+     xmlns:v2="http://www.enedis.fr/sge/b2b/services/consulterdonneestechniquescontractuelles/v1.0"
+     xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
+     <soapenv:Header/>
+     <soapenv:Body>
+        <v2:consulterDonneesTechniquesContractuelles>
+           <pointId>${pointId}</pointId>
+           <loginUtilisateur>${appLogin}</loginUtilisateur>
+           <autorisationClient>true</autorisationClient>
+        </v2:consulterDonneesTechniquesContractuelles>
+     </soapenv:Body>
+  </soapenv:Envelope>
+  `
+}
+
+/**
+ * Get user max power
+ * @param {number} pointId
+ * @param {string} appLogin
+ * @param {string} startDt
+ * @param {string} endDt
+ * @returns {string}
+ */
+function userMaxPower(pointId, appLogin, startDt, endDt) {
+  log('info', `Query userMesureDetailles`)
+  return `<?xml version='1.0' encoding='utf-8'?>
+  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+     xmlns:v2="http://www.enedis.fr/sge/b2b/services/consultationmesuresdetaillees/v2.0"
+     xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
+      <soapenv:Header/>
+      <soapenv:Body>
+          <v2:consulterMesuresDetaillees>
+              <demande>
+                  <initiateurLogin>${appLogin}</initiateurLogin>
+                  <pointId>${pointId}</pointId>
+                  <mesuresTypeCode>PMAX</mesuresTypeCode>
+                  <grandeurPhysique>PMA</grandeurPhysique>
+                  <soutirage>true</soutirage>
+                  <injection>false</injection>
+                  <dateDebut>${startDt}</dateDebut>
+                  <dateFin>${endDt}</dateFin>
+                  <mesuresPas>P1D</mesuresPas>
+                  <mesuresCorrigees>false</mesuresCorrigees>
+                  <accordClient>true</accordClient>
+              </demande>
+          </v2:consulterMesuresDetaillees>
+      </soapenv:Body>
+  </soapenv:Envelope>
+  `
+}
+
+/**
+ * Use rechercherPoint to find user PDL if exist
+ * @param {string} name
+ * @param {string} postalCode
+ * @param {string} address
+ * @returns {string} PDL
+ */
+function rechercherPoint(appLogin, name, postalCode, inseeCode, address) {
+  log('info', `Query rechercherPoint`)
+  return `<?xml version='1.0' encoding='utf-8'?>
+  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+     xmlns:v2="http://www.enedis.fr/sge/b2b/services/rechercherpoint/v2.0"
+     xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
+     <soapenv:Header/>
+     <soapenv:Body>
+        <v2:rechercherPoint>
+           <criteres>
+              <adresseInstallation>
+                 <numeroEtNomVoie>${address}</numeroEtNomVoie>
+                 <codePostal>${postalCode}</codePostal>
+                 <codeInseeCommune>${inseeCode}</codeInseeCommune>
+              </adresseInstallation>
+              <nomClientFinalOuDenominationSociale>${name}</nomClientFinalOuDenominationSociale>
+              <rechercheHorsPerimetre>true</rechercheHorsPerimetre>
+           </criteres>
+           <loginUtilisateur>${appLogin}</loginUtilisateur>
+        </v2:rechercherPoint>
+     </soapenv:Body>
+  </soapenv:Envelope>`
+}
+
+/**
+ * Search if user as a service
+ * @param {string} appLogin
+ * @param {string} contractId
+ * @param {string} pointId
+ * @returns {*}
+ */
+function searchServiceSouscrit(appLogin, contractId, pointId) {
+  log('info', `Query activateDataCollect`)
+  return `<?xml version='1.0' encoding='utf-8'?>
+  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+     xmlns:v2="http://www.enedis.fr/sge/b2b/rechercherservicessouscritsmesures/v1.0"
+     xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
+      <soapenv:Header/>
+      <soapenv:Body>
+          <v2:rechercherServicesSouscritsMesures>
+            <criteres>
+              <pointId>${pointId}</pointId>
+              <contratId>${contractId}</contratId>
+            </criteres>
+            <loginUtilisateur>${appLogin}</loginUtilisateur>
+          </v2:rechercherServicesSouscritsMesures>
+      </soapenv:Body>
+  </soapenv:Envelope>`
+}
+
+/**
+ * Activate half hour data collect for user
+ * @param {string} appLogin
+ * @param {string} contractId
+ * @param {string} pointId
+ * @param {string} name
+ * @param {string} startDate
+ * @param {string} endDate
+ * @returns {*}
+ */
+function activateDataCollect(
+  appLogin,
+  contractId,
+  pointId,
+  name,
+  startDate,
+  endDate
+) {
+  log('info', `Query activateDataCollect`)
+  return `<?xml version='1.0' encoding='utf-8'?>
+  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+     xmlns:v2="http://www.enedis.fr/sge/b2b/commandercollectepublicationmesures/v3.0"
+     xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
+      <soapenv:Header/>
+      <soapenv:Body>
+          <v2:commanderCollectePublicationMesures>
+              <demande>
+                  <donneesGenerales>
+                      <objetCode>AME</objetCode>
+                      <pointId>${pointId}</pointId>
+                      <initiateurLogin>${appLogin}</initiateurLogin>
+                      <contratId>${contractId}</contratId>
+                  </donneesGenerales>
+                  <accesMesures>
+                      <dateDebut>${startDate}</dateDebut>
+                      <dateFin>${endDate}</dateFin>
+                      <declarationAccordClient>
+                          <accord>true</accord>
+                          <personnePhysique>
+                              <nom>${name}</nom>
+                          </personnePhysique>
+                      </declarationAccordClient>
+                      <mesuresTypeCode>CDC</mesuresTypeCode>
+                      <soutirage>true</soutirage>
+                      <injection>false</injection>
+                      <mesuresPas>PT30M</mesuresPas>
+                      <mesuresCorrigees>false</mesuresCorrigees>
+                      <transmissionRecurrente>true</transmissionRecurrente>
+                      <periodiciteTransmission>P1D</periodiciteTransmission>
+                  </accesMesures>
+              </demande>
+          </v2:commanderCollectePublicationMesures>
+      </soapenv:Body>
+  </soapenv:Envelope>`
+}
+
+/**
+ *
+ * @param {string} appLogin
+ * @param {string} contractId
+ * @param {string} pointId
+ * @param {string} serviceSouscritId
+ * @returns {*}
+ */
+function stopDataCollect(appLogin, contractId, pointId, serviceSouscritId) {
+  log('info', `Query stopDataCollect`)
+  return `<?xml version='1.0' encoding='utf-8'?>
+  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
+     xmlns:v2="http://www.enedis.fr/sge/b2b/commanderarretservicesouscritmesures/v1.0"
+     xmlns:v1="http://www.enedis.fr/sge/b2b/technique/v1.0">
+      <soapenv:Header/>
+      <soapenv:Body>
+          <v2:commanderArretServiceSouscritMesures>
+              <demande>
+                  <donneesGenerales>
+                      <objetCode>ASS</objetCode>
+                      <pointId>${pointId}</pointId>
+                      <initiateurLogin>${appLogin}</initiateurLogin>
+                      <contratId>${contractId}</contratId>
+                  </donneesGenerales>
+                  <arretServiceSouscrit>
+                  <serviceSouscritId>${serviceSouscritId}</serviceSouscritId>
+                  </arretServiceSouscrit>
+              </demande>
+          </v2:commanderArretServiceSouscritMesures>
+      </soapenv:Body>
+  </soapenv:Envelope>`
+}
+
+/**
+ * Return inseeCode given a postalCode
+ * @param {string} postalCode
+ * @return {string} inseeCode
+ */
+function getInseeCode(postalCode) {
+  //TODO: Implement
+  log('info', `Query getInseeCode for postalCode ${postalCode}`)
+  throw new Error('Function not implemented.')
+}
+
+/**
+ *
+ */
+function createBoConsent() {
+  //TODO: Implement
+  log('info', `Query createBoConsent`)
+  throw new Error('Function not implemented.')
+}
+
+/**
+ *
+ */
+function updateBoConsent() {
+  //TODO: Implement
+  log('info', `Query updateBoConsent`)
+  throw new Error('Function not implemented.')
+}
+
+/**
+ *
+ */
+function getBoConsent() {
+  //TODO: Implement
+  log('info', `Query getBoConsent`)
+  throw new Error('Function not implemented.')
+}
+/**
+ *
+ */
+function deleteBoConsent() {
+  //TODO: deleteBoConsent
+  log('info', `Query createBoConsent`)
+  throw new Error('Function not implemented.')
+}
+
+module.exports = {
+  userTechnicalData,
+  userMaxPower,
+  userMesureDetailles,
+  rechercherPoint,
+  searchServiceSouscrit,
+  activateDataCollect,
+  stopDataCollect,
+  getInseeCode,
+  createBoConsent,
+  updateBoConsent,
+  getBoConsent,
+  deleteBoConsent,
+}
diff --git a/src/types.js b/src/types.js
new file mode 100644
index 0000000000000000000000000000000000000000..b2d1f4016528c80d44ce27bc9b6c461fd1c170c6
--- /dev/null
+++ b/src/types.js
@@ -0,0 +1,26 @@
+/**
+ * EnedisKonnectorData definition
+ * @typedef {object} EnedisKonnectorData
+ * @property {number} year
+ * @property {number} month
+ * @property {number} day
+ * @property {number} hour
+ * @property {number} minute
+ */
+
+/**
+ * SGEData definition
+ * @typedef {object} SGEData
+ * @property {number} v
+ * @property {string} d
+ */
+
+/**
+ * User definition
+ * @typedef {object} User
+ * @property {string} name
+ * @property {string} address
+ * @property {string} postalCode
+ * @property {string} pointId
+ * @property {string} [inseeCode]
+ */
diff --git a/yarn.lock b/yarn.lock
index 2985990f679a5ca343b58634ee8056f5132b5f2a..b132129a871c880fbf6d6d2b97ea53ce06183817 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1797,6 +1797,13 @@ aws4@^1.8.0:
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59"
   integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==
 
+axios@^0.26.1:
+  version "0.26.1"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9"
+  integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==
+  dependencies:
+    follow-redirects "^1.14.8"
+
 babel-eslint@10.0.1:
   version "10.0.1"
   resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.0.1.tgz#919681dc099614cd7d31d45c8908695092a1faed"
@@ -2809,6 +2816,13 @@ domutils@^2.5.2, domutils@^2.6.0, domutils@^2.7.0:
     domelementtype "^2.2.0"
     domhandler "^4.2.0"
 
+easy-soap-request@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/easy-soap-request/-/easy-soap-request-4.7.0.tgz#d57af87fc91a3c8ff606e3ca1102a2c37a095ff8"
+  integrity sha512-rFvXxk65ROqUkSSgVbxS4X9skRkdBPU4eoETCzgWaKh5seVibLD8feY5F0YV6UTnY7ErcM9DWMG3RNaYm02xHA==
+  dependencies:
+    axios "^0.26.1"
+
 ecc-jsbn@~0.1.1:
   version "0.1.2"
   resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9"
@@ -3568,6 +3582,11 @@ flatted@^3.1.0:
   resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
   integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
 
+follow-redirects@^1.14.8:
+  version "1.14.9"
+  resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7"
+  integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==
+
 forever-agent@~0.6.1:
   version "0.6.1"
   resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
@@ -4612,6 +4631,18 @@ mkdirp@^0.5.1:
   dependencies:
     minimist "^1.2.6"
 
+moment-timezone@^0.5.34:
+  version "0.5.34"
+  resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.34.tgz#a75938f7476b88f155d3504a9343f7519d9a405c"
+  integrity sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==
+  dependencies:
+    moment ">= 2.9.0"
+
+"moment@>= 2.9.0", moment@^2.29.3:
+  version "2.29.3"
+  resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.3.tgz#edd47411c322413999f7a5940d526de183c031f3"
+  integrity sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==
+
 morgan@^1.9.1:
   version "1.10.0"
   resolved "https://registry.yarnpkg.com/morgan/-/morgan-1.10.0.tgz#091778abc1fc47cd3509824653dae1faab6b17d7"
@@ -5686,7 +5717,7 @@ safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.2, safe-buffer@^5.2.1,
   resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
   integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
 
-sax@~1.2.4:
+sax@>=0.6.0, sax@~1.2.4:
   version "1.2.4"
   resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
   integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
@@ -6622,6 +6653,19 @@ write@1.0.3:
   dependencies:
     mkdirp "^0.5.1"
 
+xml2js@^0.4.23:
+  version "0.4.23"
+  resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66"
+  integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==
+  dependencies:
+    sax ">=0.6.0"
+    xmlbuilder "~11.0.0"
+
+xmlbuilder@~11.0.0:
+  version "11.0.1"
+  resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3"
+  integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==
+
 y18n@^5.0.5:
   version "5.0.8"
   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"