# Accepter un dossier et y joindre un justificatif (une PJ)

Il est possible d'accepter un dossier et d'y joindre le justificatif via les API GraphQL. Mais avant cela, voici un petit tour d'horizon du fonctionnement :&#x20;

## 3 étapes :

### 1. Vous demandez a notre **API une authorisation pour uploader un fichier sur notre object storage**.&#x20;

Cette requete implique de décrire le fichier que vous allez envoyer : le filename, byteSize, checksum (un digest md5, base64digesté) et son contenu. Ceci pour nous permettre de valider que nous echangeons le meme fichier, qu'il n'a pas été altéré etc.. Nous vous renvoyons :&#x20;

1. les crédentials pour communiquer avec notre object storage
2. l'identifiant du fichier (blob\_signed\_id) a utiliser dans une autre requete pour le lier à une autre mutation&#x20;

### 2. Vous uploadez le fichier sur notre object storage, en réutilisant les crédentials de la 1ere requete&#x20;

<figure><img src="https://463284772-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-L7_aKvpAJdAIEfxHudA%2Fuploads%2FJsMChBFIxUlUDnYw4BKR%2Fevil-martions-direct-upload-architecture.webp?alt=media&#x26;token=24a5405c-4a1b-4ee5-9eaf-4913b648e9c1" alt=""><figcaption><p>résumé des étapes 1 et 2,source: <a href="https://evilmartians.com/chronicles/active-storage-meets-graphql-direct-uploads">https://evilmartians.com/chronicles/active-storage-meets-graphql-direct-uploads</a></p></figcaption></figure>

### 3. Vous, client, faites une requete pour lier ce fichier (maintenant sur nos serveurs, identifié par le signed\_blob\_id) a un justificatif

## 1ere étape : demander les crédentials

Utiliser la mutation `createDirectUpload`. Voici un exemple complet de script que vous pouvez executer :

{% code overflow="wrap" %}

```bash
FILE=./file.txt \
FILE_TYPE="text/plain" \
API_TOKEN="VOTRE_TOKEN" \
DOSSIER_ID="un identifiant de dossier, cf: dossiers.id (ce n'est pas le numero de dossier)" \
ruby ./get_credentials.rb 

```

{% endcode %}

{% code title="get\_credentials.rb" %}

```ruby
require 'net/http'
require 'uri'
require 'json'
require 'byebug'
require 'digest'
ENDPOINT = URI('https://demarche.numerique.gouv.fr/api/v2/graphql')

QUERY_UPLOAD_REQUEST = "
mutation createDirectUpload($input: CreateDirectUploadInput!) {
  createDirectUpload(input: $input) {
    directUpload {
      url
      headers
      blobId
      signedBlobId
    }
  }
}
"

### that's the HTTP part
# open an http connexion to our GraphQL endpoint
def open_http_connection
  http = Net::HTTP.new(ENDPOINT.host, ENDPOINT.port)
  if ENDPOINT.scheme == 'https'
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  end
  http
end

# the headers of our http query, include auth
def request_headers
  {
    "Content-Type" => "application/json",
    "Authorization" => "Bearer #{ENV.fetch('API_TOKEN') { raise 'missing env var API_TOKEN=xxx' }}"
  }
end

def compute_checksum_in_chunks(io)
  Digest::MD5.new.tap do |checksum|
    while (chunk = io.read(5242880))
      checksum << chunk
    end

    io.rewind
  end.base64digest
end

def request_direct_upload_credentials(http, file)
  data = {
    "query" => QUERY_UPLOAD_REQUEST,
    "operationName" => "createDirectUpload",
    "variables" => {
      "input" => {
        "dossierId" => ENV.fetch("DOSSIER_ID") { raise "missing env var DOSSIER_ID=xxx" },
        "filename" => file.path,
        "byteSize" => file.size,
        "checksum" => compute_checksum_in_chunks(file),
        "contentType" => ENV.fetch("FILE_TYPE") { raise 'missing file type' }
      }
    }
  }
  pp "SENT DATA : #{data.inspect}"

  req = Net::HTTP::Post.new(ENDPOINT, request_headers)
  req.body = data.to_json
  http.request(req)
end

fp = ENV.fetch('FILE') { raise 'missing env var FILE' }
File.open(fp, 'r') do |file|
  http = open_http_connection
  response = request_direct_upload_credentials(http, file)
  body = JSON.parse(response.body)
  puts body.inspect

  credentials = body['data']['createDirectUpload']['directUpload']
  credentials_headers = JSON.parse(credentials['headers'])

  puts <<~CURL
    curl -vvv \
    --data-binary @#{file.path} \
    -X PUT \
    -H "Content-Type: #{credentials_headers['Content-Type']}" \
    -H "ETag: #{credentials_headers['ETag']}" \
    "#{credentials['url']}"
  CURL
end


```

{% endcode %}

{% hint style="info" %}
Vous pouvez voir la documentation des parametres de la mutation graphQL ici : <https://demarche.numerique.gouv.fr/graphql/schema/mutations/createDirectUpload>
{% endhint %}

La réponse HTTP de notre API sera de la forme suivante

```json
{
   "data":{
      "createDirectUpload":{
         "directUpload":{
            "url":"https://static.demarches-simplifiees.fr:443/v1/AUTH_XXX/ds_activestorage_backup/2023/12/21/We/We9FmNxKuJfzEr6QSmKWSrxP263Z?temp_url_sig=XXX\u0026temp_url_expires=XXX",
            "headers":"{\"Content-Type\":\"image/png\",\"ETag\":\"d5d122f320e9d34faf716390a33429a7\"}",
            "blobId":"a number in a string",
            "signedBlobId":"a string"
         }
      }
   }
}
```

Aussi notre script echo un exemple pour uploader le fichier via `curl`, il vous suffit de le copier/coller pour envoyer le même fichier sur notre object storage

### 2eme étape : Uploader le fichier en utilisant les crédentials

```
curl -vvv --data-binary @./file.txt -X PUT -H "Content-Type: LE type de votre fichier" -H "ETag: LAVALEUR PRESENTE DANS LA REPONSE, au niveau de headers.etag [attention, c'est un json a parser]" "data[createDirectUpload][directUpload][url]"
```

### 3eme étape : Associer ce fichier lors de l'acceptation du dossier.

En préambule, il vous faut envoyer ce message au nom d'un instructeur, nous vous renvoyons à la documentation pour [lister les id des instructeurs](https://doc.demarches-simplifiees.fr/api-graphql/cas-dusages-exemple-dimplementation/lister-les-id-des-instructeurs).

Utiliser la mutation `dossierAccepter`. Voici un exemple complet de script que vous pouvez executer :

```bash
SIGNED_BLOB_ID="Le signed blob id de la 1ere requete" API_TOKEN="votre token d'api" DOSSIER_ID="l'id de votre dossier" INSTRUCTEUR_ID="l'id de l'instructeur" ruby ./accept_with_pj.rb
```

{% code title="accept\_with\_pj.rb" %}

```ruby
require 'net/http'
require 'uri'
require 'json'
ENDPOINT = URI('https://demarche.numerique.gouv.fr/api/v2/graphql')

QUERY = "
mutation dossierAccepter($input: DossierAccepterInput!) {
  dossierAccepter(input: $input) {
    dossier {
      id
    }
    errors {
      message
    }
  }
}
"

### that's the HTTP part
# open an http connexion to our GraphQL endpoint
def open_http_connection
  http = Net::HTTP.new(ENDPOINT.host, ENDPOINT.port)
  if ENDPOINT.scheme == 'https'
    http.use_ssl = true
    http.verify_mode = OpenSSL::SSL::VERIFY_NONE
  end
  http
end

# the headers of our http query, include auth
def request_headers
  {
    "Content-Type" => "application/json",
    "Authorization" => "Bearer #{ENV.fetch('API_TOKEN') { raise 'missing env var API_TOKEN=xxx' }}"
  }
end

def accept_dossier_and_link_justificatif(http)
  data = {
    "query" => QUERY,
    "operationName" => "dossierAccepter",
    "variables" => {
      "input" => {
        "dossierId" => ENV.fetch('DOSSIER_ID') { raise 'missing env var DOSSIER_ID' },
        "instructeurId" => ENV.fetch('INSTRUCTEUR_ID') { raise 'missing env var INSTRUCTEUR_ID' },
        "justificatif" => ENV.fetch('SIGNED_BLOB_ID') { raise 'missing SIGNED_BLOB_ID env var' }
      }
    }
  }

  req = Net::HTTP::Post.new(ENDPOINT, request_headers)
  req.body = data.to_json
  response = http.request(req)
  pp JSON.parse(response.body)
end

http = open_http_connection
puts accept_dossier_and_link_justificatif(http)

```

{% endcode %}
