diff --git a/biome.json b/biome.json index 0ea08ab8..df3e6552 100644 --- a/biome.json +++ b/biome.json @@ -5,7 +5,8 @@ "**/build/**", "node_modules/**", "package.json", - "pnpm-lock.yaml" + "pnpm-lock.yaml", + "docs/**" ] }, "organizeImports": { diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 00000000..9263a1ed --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +_site +.DS_Store diff --git a/docs/Gemfile b/docs/Gemfile new file mode 100644 index 00000000..7d2fbc58 --- /dev/null +++ b/docs/Gemfile @@ -0,0 +1,9 @@ +source "https://rubygems.org" + +gem 'jekyll' +group :jekyll_plugins do + gem 'jekyll-gfm-adminitions' + gem 'jekyll-optional-front-matter' + gem 'github-pages' +end +gem 'jekyll-remote-theme' diff --git a/docs/Gemfile.lock b/docs/Gemfile.lock new file mode 100644 index 00000000..0c0338f4 --- /dev/null +++ b/docs/Gemfile.lock @@ -0,0 +1,248 @@ +GEM + remote: https://rubygems.org/ + specs: + activesupport (~>7.0.7.1) + i18n (~> 0.7) + minitest (~> 5.1) + thread_safe (~> 0.3.4) + tzinfo (~> 1.2.10) + addressable (~> 2.5.2) + public_suffix (~> 4.0) + coffee-script (~> 2.4.1) + coffee-script-source + execjs + coffee-script-source (~> 1.11.1) + colorator (~> 1.1.0) + commonmarker (~> 0.23.10) + ruby-enum (~> 0.5) + concurrent-ruby (~> 1.1.5) + dnsruby (~> 1.61.2) + addressable (~> 2.8.0) + em-websocket (~> 0.5.1) + eventmachine (>= 0.12.9) + http_parser.rb (~> 0.6.0) + ethon (~> 0.11.0) + ffi (~> 1.3.0) + eventmachine (~> 1.2.7) + execjs (~> 2.7.0) + faraday (~> 0.15.2) + multipart-post (~> 3) + ffi (~> 1.11.1) + forwardable-extended (~> 2.6.0) + gemoji (~> 3.0.0) + github-pages (191) + activesupport (~> 7.0.7.1) + github-pages-health-check (~> 1.8.1) + jekyll (~> 3.7.3) + jekyll-avatar (~> 0.6.0) + jekyll-coffeescript (~> 1.1.1) + jekyll-commonmark-ghpages (~> 0.1.5) + jekyll-default-layout (~> 0.1.4) + jekyll-feed (~> 0.10.0) + jekyll-gist (~> 1.5.0) + jekyll-github-metadata (~> 2.9.4) + jekyll-mentions (~> 1.4.1) + jekyll-optional-front-matter (~> 0.3.0) + jekyll-paginate (~> 1.1.0) + jekyll-readme-index (~> 0.2.0) + jekyll-redirect-from (~> 0.14.0) + jekyll-relative-links (~> 0.5.3) + jekyll-remote-theme (~> 0.3.1) + jekyll-sass-converter (~> 1.5.2) + jekyll-seo-tag (~> 2.5.0) + jekyll-sitemap (~> 1.2.0) + jekyll-swiss (~> 0.4.0) + jekyll-theme-architect (~> 0.1.1) + jekyll-theme-cayman (~> 0.1.1) + jekyll-theme-dinky (~> 0.1.1) + jekyll-theme-hacker (~> 0.1.1) + jekyll-theme-leap-day (~> 0.1.1) + jekyll-theme-merlot (~> 0.1.1) + jekyll-theme-midnight (~> 0.1.1) + jekyll-theme-minimal (~> 0.1.1) + jekyll-theme-modernist (~> 0.1.1) + jekyll-theme-primer (~> 0.5.3) + jekyll-theme-slate (~> 0.1.1) + jekyll-theme-tactile (~> 0.1.1) + jekyll-theme-time-machine (~> 0.1.1) + jekyll-titles-from-headings (~> 0.5.1) + jemoji (~> 0.10.1) + kramdown (~> 2.3.0) + liquid (~> 4.0.0) + listen (~> 3.1.5) + mercenary (~> 0.3) + minima (~> 2.5.0) + nokogiri (~> 1.16.5) + rouge (~> 2.2.1) + terminal-table (~> 1.4) + github-pages-health-check (~> 1.8.1) + addressable (~> 2.8.0) + dnsruby (~> 1.60) + octokit (~> 4.0) + public_suffix (~> 2.0) + typhoeus (~> 1.3) + html-pipeline (~> 2.8.4) + activesupport (~> 7.0.7.1) + nokogiri (~> 1.16.5) + http_parser.rb (~> 0.6.0) + i18n (~> 0.9.5) + concurrent-ruby (~> 1.0) + jekyll (~> 3.8.4) + addressable (~> 2.8.0) + colorator (~> 1.0) + em-websocket (~> 0.5) + i18n (~> 0.7) + jekyll-sass-converter (~> 1.0) + jekyll-watch (~> 2.0) + kramdown (~> 2.3.0) + liquid (~> 4.0) + mercenary (~> 0.3.3) + pathutil (~> 0.9) + rouge (~> 4.0) + safe_yaml (~> 1.0) + jekyll-avatar (~> 0.6.0) + jekyll (~> 3.0) + jekyll-coffeescript (~> 1.1.1) + coffee-script (~> 2.2) + coffee-script-source (~> 1.11.1) + jekyll-commonmark (~> 1.2.0) + commonmarker (~> 0.23.10) + jekyll (~> 4.0) + jekyll-commonmark-ghpages (~> 0.1.5) + commonmarker (~> 0.23.10) + jekyll-commonmark (~> 1) + rouge (~> 2) + jekyll-default-layout (~> 0.1.4) + jekyll (~> 3.0) + jekyll-feed (~> 0.10.0) + jekyll (~> 3.3) + jekyll-gist (~> 1.5.0) + octokit (~> 4.2) + jekyll-github-metadata (~> 2.9.4) + jekyll (~> 3.1) + octokit (~> 4.4.0) + jekyll-mentions (~> 1.4.1) + html-pipeline (~> 2.3) + jekyll (~> 3.0) + jekyll-optional-front-matter (~> 0.3.0) + jekyll (~> 3.0) + jekyll-paginate (~> 1.1.0) + jekyll-readme-index (~> 0.2.0) + jekyll (~> 3.0) + jekyll-redirect-from (~> 0.14.0) + jekyll (~> 3.3) + jekyll-relative-links (~> 0.5.3) + jekyll (~> 3.3) + jekyll-remote-theme (~> 0.3.1) + jekyll (~> 3.5) + rubyzip (~> 3.0) + jekyll-sass-converter (~> 1.5.2) + sass (~> 3.4) + jekyll-seo-tag (~> 2.5.0) + jekyll (~> 3.3) + jekyll-sitemap (~> 1.2.0) + jekyll (~> 3.3) + jekyll-swiss (~> 0.4.0) + jekyll-theme-architect (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-cayman (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-dinky (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-hacker (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-leap-day (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-merlot (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-midnight (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-minimal (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-modernist (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-primer (~> 0.5.3) + jekyll (~> 3.5) + jekyll-github-metadata (~> 2.9) + jekyll-seo-tag (~> 2.0) + jekyll-theme-slate (0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-tactile (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-theme-time-machine (~> 0.1.1) + jekyll (~> 3.5) + jekyll-seo-tag (~> 2.0) + jekyll-titles-from-headings (~> 0.5.1) + jekyll (~> 3.3) + jekyll-watch (~> 2.2.1) + listen (~> 3.0) + jemoji (~> 0.10.1) + gemoji (~> 3.0) + html-pipeline (~> 2.2) + jekyll (~> 3.0) + kramdown (~> 2.3.0) + liquid (~> 4.0.0) + listen (~> 3.1.5) + rb-fsevent (~> 0.9.4) + rb-inotify (~> 0.9.7) + ruby_dep (~> 1.2) + mercenary (~> 0.3.6) + mini_portile2 (~> 2.4.0) + minima (~> 2.5.0) + jekyll (~> 3.5) + jekyll-feed (~> 0.9) + jekyll-seo-tag (~> 2.1) + minitest (~> 5.11.3) + multipart-post (~> 2.0.0) + nokogiri (~> 1.16.5) + mini_portile2 (~> 2.4.0) + octokit (~> 4.11.0) + sawyer (~> 0.8.0) + pathutil (~> 0.16.2) + forwardable-extended (~> 2.6) + public_suffix (~> 2.0.5) + rb-fsevent (~> 0.10.3) + rb-inotify (~> 0.10.0) + ffi (~> 1.0) + rouge (~> 2.2.1) + ruby-enum (~> 0.7.2) + i18n + ruby_dep (~> 1.5.0) + rubyzip (~> 2.0.0) + safe_yaml (~> 1.0.5) + sass (~> 3.7.4) + sass-listen (~> 4.0.0) + sass-listen (~> 4.0.0) + rb-fsevent (~> 0.9.4) + rb-inotify (~> 0.9.7) + sawyer (~> 0.8.1) + addressable (~> 2.8.0) + faraday (~> 1.0) + terminal-table (~> 1.8.0) + unicode-display_width (~> 1.1.1) + thread_safe (~> 0.3.6) + typhoeus (~> 1.3.0) + ethon (~> 0.9.0) + tzinfo (~> 1.2.10) + thread_safe (~> 0.1) + unicode-display_width (~> 1.4.0) + +PLATFORMS + ruby + +DEPENDENCIES + github-pages + +BUNDLED WITH + 1.16.4 diff --git a/docs/_config.yml b/docs/_config.yml new file mode 100644 index 00000000..55399393 --- /dev/null +++ b/docs/_config.yml @@ -0,0 +1,81 @@ +# Site settings +title: GC Forms API +repository: cds-snc/forms-api +description: >- + This documentation site is for developers who want to use the GC Forms API to retrieve form submission data programmatically into their department’s web application or back-office system. + +# Base configuration +exclude: [".rvmrc", ".rbenv-version", "README.md", "Rakefile", "changelog.md"] +markdown: kramdown, GFM +lsi: false +safe: true +source: [your repo's top level directory] +incremental: false +highlighter: rouge +gist: + noscript: false +kramdown: + math_engine: mathjax + syntax_highlighter: rouge +collections: + pages: + output: true + permalink: /:path/ + +# Theme +remote_theme: pages-themes/minimal@v0.2.0 + +#Plugins +plugins: +- jekyll-coffeescript +- jekyll-commonmark-ghpages +- jekyll-default-layout +- jekyll-gfm-admonitions +- jekyll-gist +- jekyll-github-metadata +- jekyll-optional-front-matter +- jekyll-paginate +- jekyll-readme-index +- jekyll-remote-theme +- jekyll-relative-links +- jekyll-titles-from-headings +- jekyll-relative-links +- jekyll-seo-tag +- kramdown-parser-gfm + +# Title +name: GC Forms API +subtitle: This documentation is for developers who want to use the GC Forms API to retrieve form submission data programmatically into their department’s web application or back-office system. + +# When using this template with a project page set the baseurl to '/project-name' +# For user/organization pages set this to an empty string +# When working locally use jekyll serve --baseurl '' so that you can view everything at localhost:4000 +# See http://jekyllrb.com/docs/github-pages/ for more info +#baseurl: '' +baseurl: '/forms-api' + +# Author/Organization info to be displayed in the templates +author: + name: GC Forms + url: https://articles.alpha.canada.ca/forms-formulaires/ +author2: + name: Canadian Digital Service + url: https://digital.canada.ca + +# Point the logo URL at a file in your repo or hosted elsewhere by your organization +logourl: +logoalt: + +# Repo list +# List repos that you would like to appear on the homepage here +repos: +- name: + description: + url: + +# Style Variables +brand_color: "#9d8cf2ff" + +# Offline caching +offline_cache: false +cache_name: DOCter-v1.1 diff --git a/docs/_data/menu.yml b/docs/_data/menu.yml new file mode 100644 index 00000000..02b10ebb --- /dev/null +++ b/docs/_data/menu.yml @@ -0,0 +1,39 @@ +en: + home: + name: "Home" + url: "/home/" + getting-started: + name: "Getting started" + url: "/getting-started/" + authentication: + name: "Authentication" + url: "/authentication/" + making-requests: + name: "Making requests" + url: "/making-requests/" + monitoring: + name: "Monitoring" + url: "/monitoring/" + contact: + name: "Getting support" + url: "/getting-support/" + +fr: + accueil: + name: "Accueil" + url: "/accueil/" + getting-started: + name: "Pour commencer" + url: "/pour-commencer/" + authentication: + name: "Authentification" + url: "/authentification/" + making-requests: + name: "Effectuer des demandes" + url: "/effectuer-des-demandes/" + monitoring: + name: "Surveiller" + url: "/surveiller/" + contact: + name: "Obtenir du soutien" + url: "/obtenir-du-soutien/" diff --git a/docs/_data/repos.yml b/docs/_data/repos.yml new file mode 100644 index 00000000..1907e2b4 --- /dev/null +++ b/docs/_data/repos.yml @@ -0,0 +1,8 @@ +en: +- name: + description: + url: +fr: +- name: + description: + url: diff --git a/docs/_data/translations.yml b/docs/_data/translations.yml new file mode 100644 index 00000000..80dc4051 --- /dev/null +++ b/docs/_data/translations.yml @@ -0,0 +1,22 @@ +en: + homepage-tag-line: "Version 1.7.1" + cds-snc: "GC Forms" + cds-snc-url: "https://articles.alpha.canada.ca/forms-formulaires/" + hosting: "Hosted on" + built-by: "This documentation is for API developers retrieving form submissions from" + contribute: "Contribute to the project" + repo: "Repository" + language: "Français" + homepage-slug: "/home/" + site-name: "GC Forms — Data retrieval API" +fr: + homepage-tag-line: "Version 1.7.1" + cds-snc: "Formulaires GC" + cds-snc-url: "https://articles.alpha.canada.ca/forms-formulaires/fr/" + hosting: "Hébergé dans" + built-by: "Cette documentation s'adresse aux développeur·euse·s d'API qui récupèrent des soumissions de formulaire à partir de" + contribute: "Contribuer au projet" + repo: "Référentiel" + language: "English" + homepage-slug: "/accueil/" + site-name: "API pour la récupération de données — Formulaires GC" diff --git a/docs/_includes/accueil.md b/docs/_includes/accueil.md new file mode 100644 index 00000000..afb4f05f --- /dev/null +++ b/docs/_includes/accueil.md @@ -0,0 +1,6 @@ +### Bienvenue + +Voici la documentation technique de l'interface de programmation d'applications (API) de Formulaires GC. Ce site est destiné aux développeur·euse·s qui souhaitent utiliser l’API de Formulaires GC pour récupérer programmatiquement des données de soumission de formulaire dans l’application Web ou le système d’arrière-guichet de leur ministère. + +Vous pourrez vous orienter dans le processus de configuration d’une intégration initiale et récupérer de façon sécurisée des soumissions de formulaires. Nous apprécions votre rétroaction ouverte et honnête pour nous aider à améliorer Formulaires GC et à bâtir l’avenir de ce produit. + diff --git a/docs/_includes/home.md b/docs/_includes/home.md new file mode 100644 index 00000000..7b2fd08c --- /dev/null +++ b/docs/_includes/home.md @@ -0,0 +1,5 @@ +### Welcome + +Welcome to the technical documentation for the GC Forms application programming interface (API). This site is for developers who want to use the GC Forms API to retrieve form submission data programmatically into their department’s web application or back-office system. + +Here you'll find how to set up an initial integration to securely retrieve form submissions. We appreciate your open and honest feedback to help us improve GC Forms and shape the future of this product. diff --git a/docs/_includes/index.md b/docs/_includes/index.md new file mode 100644 index 00000000..a299cce5 --- /dev/null +++ b/docs/_includes/index.md @@ -0,0 +1 @@ +{% include home.md %} diff --git a/docs/_includes/repositories.html b/docs/_includes/repositories.html new file mode 100644 index 00000000..989c8163 --- /dev/null +++ b/docs/_includes/repositories.html @@ -0,0 +1,16 @@ +
+

{{site.data.translations[page.lang].repo}}

+ +
diff --git a/docs/_includes/sidebar.html b/docs/_includes/sidebar.html new file mode 100644 index 00000000..efacb8d8 --- /dev/null +++ b/docs/_includes/sidebar.html @@ -0,0 +1,19 @@ + diff --git a/docs/_includes/submenu.html b/docs/_includes/submenu.html new file mode 100644 index 00000000..0b5be88e --- /dev/null +++ b/docs/_includes/submenu.html @@ -0,0 +1,10 @@ + diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html new file mode 100644 index 00000000..4fd544f1 --- /dev/null +++ b/docs/_layouts/default.html @@ -0,0 +1,76 @@ + + + + + + + {% if page.title == null %} + {{ site.name }} + {% else %} + {{ page.title }} - {{ site.name }} + {% endif %} + + + + + + + + + + + + + + + +
+
+ +
+ +
+ + {% include sidebar.html %} + +
+ {{ content }} +
+ +
+ + +
+ {% if site.offline_cache == true %} + + {% endif %} + + diff --git a/docs/_layouts/page.html b/docs/_layouts/page.html new file mode 100644 index 00000000..635737dd --- /dev/null +++ b/docs/_layouts/page.html @@ -0,0 +1,6 @@ +--- +layout: default +--- +

{{ page.title }}

+ +{{ content }} diff --git a/docs/_layouts/post.html b/docs/_layouts/post.html new file mode 100644 index 00000000..f2e278b0 --- /dev/null +++ b/docs/_layouts/post.html @@ -0,0 +1,11 @@ +--- +layout: default +--- +
+

{{ page.title }}

+

{{ page.date | date_to_string }}

+ +
+ {{ content }} +
+
diff --git a/docs/_pages/en/authentication.md b/docs/_pages/en/authentication.md new file mode 100644 index 00000000..04ef0a8c --- /dev/null +++ b/docs/_pages/en/authentication.md @@ -0,0 +1,44 @@ +--- +layout: page +title: "Authentication" +lang: en +permalink: "/authentication/" +trans_url: "/authentification/" +--- + +### Base URL + + +https://api.forms-formulaires.alpha.canada.ca/v1/... + + + +> _Note: You may need to use a cloud provider such as Microsoft Azure or Amazon Web Services (AWS), to be able to access the endpoint._ + +### How authentication works + +The GC Forms API leverages an OAuth 2.0 Signed JSON Web Token ([JWT](https://jwt.io/)) flow with a self-hosted Identity provider (IDP), Zitadel, to establish machine-to-machine authentication in a safe and secure way for the API endpoint. + +Authentication is done through an opaque token, with basic authorization handled via the user profile and the requested form ID. The OAuth server provides a short-lived access token that can be used to make API requests by verifying the client’s JWT that has been signed with a private key and includes their client ID. This access token is valid for 30 minutes. + +The API requires authentication with a Government of Canada email, an associated **formID**, a JWT signed with a **Private key** and verified using a **Public key** with the IDP. This token contains a claim to specify the form ID from which it is associated. + +### Integration examples + +Once you’ve created a form, you can submit mock form submissions to simulate an active form and be able to test generating an access token. + +Use these examples as a reference to generate access tokens in your preferred programming language: +- [.NET / C#](https://github.com/cds-snc/forms-api/blob/main/examples/dotnet/AccessTokenGenerator.cs) +- [Node.JS / Typescript](https://github.com/cds-snc/forms-api/blob/main/examples/nodejs/accessTokenGenerator.ts) +- [Python](https://github.com/cds-snc/forms-api/blob/main/examples/python/access_token_generator.py) +- [Bash /Curl](https://github.com/cds-snc/forms-api/blob/main/examples/bash/get_access_token.sh) + +### Authorization header + +Use this header for each API request to verify it’s you: + +
+
curl \ +
--request GET "$FORMS_URL" \ +
--header "Authorization: Bearer $ACCESS_TOKEN" +
diff --git a/docs/_pages/en/getting-started.md b/docs/_pages/en/getting-started.md new file mode 100644 index 00000000..4a5c07da --- /dev/null +++ b/docs/_pages/en/getting-started.md @@ -0,0 +1,21 @@ +--- +layout: page +title: "Getting started" +lang: en +permalink: "/getting-started/" +trans_url: "/pour-commencer/" +--- + +### Overview + +The purpose of this API is to allow you to securely and reliably retrieve form submissions directly from an API endpoint. This should help alleviate the effort associated for very high volumes of form submissions. So, rather than having to manually download and sign off on the removal of responses to confirm their retrieval from the database, the API will automate the workflow with systems that talk to each other and exchange data. + +### What you will need + - A [GC Forms account](https://articles.alpha.canada.ca/forms-formulaires/) + - A draft form (Unclassified, Protected A, or Protected B) + - An API key (that you generate in Settings > API integration). + - A few “mock” responses submitted to the form + - A target system where you plan on receiving form submission data + - Infrastructure development resources who can craft HTTP requests to reach the API. _No infrastructure development capabilities available? This first version of the API might not be ready for you just yet._ + +> **IMPORTANT: You must keep this API key secure as it is used to authenticate API requests and could be used to access protected data. Use encrypted email if you must share it with a developer team member to set up the integration.** diff --git a/docs/_pages/en/getting-support.md b/docs/_pages/en/getting-support.md new file mode 100644 index 00000000..86e4c431 --- /dev/null +++ b/docs/_pages/en/getting-support.md @@ -0,0 +1,10 @@ +--- +layout: page +title: "Getting support" +lang: en +permalink: "/getting-support/" +trans_url: "/obtenir-du-soutien/" +--- +We’re open to all questions and welcome any feedback you might have to help us improve. You can get in touch with our team directly with any questions or needs for technical support, by filling out the [Support form](https://forms-formulaires.alpha.canada.ca/en/support). + +> Please include your name, email address, the form ID, response IDs (if relevant), and a description of the problem. We'll do our best to be in touch within 2 business days. diff --git a/docs/_pages/en/home.md b/docs/_pages/en/home.md new file mode 100644 index 00000000..bf3a2e45 --- /dev/null +++ b/docs/_pages/en/home.md @@ -0,0 +1,8 @@ +--- +layout: page +title: "Home" +lang: en +permalink: "/home/" +trans_url: "/accueil/" +--- +{% include home.md %} diff --git a/docs/_pages/en/making-requests.md b/docs/_pages/en/making-requests.md new file mode 100644 index 00000000..eb89c22e --- /dev/null +++ b/docs/_pages/en/making-requests.md @@ -0,0 +1,443 @@ +--- +layout: page +title: "Making requests" +lang: en +permalink: "/making-requests/" +trans_url: "/effectuer-des-demandes/" +childPages: + - Retrieving new form submissions + - Retrieving specific form submissions + - Confirming form submissions + - Reporting a problem with a form submission + - Getting form questions +--- + +The GC Forms API allows you to retrieve form submissions as files in JSON file format. You can get up to 100 form submissions in a batch, and these are available for up to 30 days after submission, given our current data retention periods. Form submissions are encrypted with a public key and can be decrypted by you locally. + +### Query parameters + +The API requests for GC Forms submission data are possible using query parameters such as: + - **{formID}** + 25-character alphanumeric string found at the end of the URL for the form or at the start of the + file name of the API key. + For example: ```clzvj8fzb00226o90r2b7l1gt```. + - **{SubmissionName}** + 11-character alphanumeric including dashes, sometimes also called the SubmissionID in the GC Forms + app. + For example: ```e02-08-d732```. + - **{ConfirmationCode}** + 36-character alphanumeric string that includes dashes, sometimes also called the Receipt code in the + GC Forms app. + For example: ```620b203c-9836-4000-bf30-1c3bcc26b834```. + +### Integration examples + +We’ve developed some examples that you can reference or run the program for, that we hope will help make integration easier. Here are the programming languages currently available: + +- [.NET / C#](https://github.com/cds-snc/forms-api/tree/main/examples/dotnet) +- [Node.JS / Typescript](https://github.com/cds-snc/forms-api/tree/main/examples/nodejs) +- [Python](https://github.com/cds-snc/forms-api/tree/main/examples/python) +- [Bash /Curl](https://github.com/cds-snc/forms-api/tree/main/examples/bash) + +Work with your development team to test out a draft form and ensure the API integration is receiving responses, able to decrypt, check the hash, and confirm the responses or report a problem. Once that process is complete, you are ready to publish your form with this data delivery method. + +### Retrieving **new** form submissions + +##### HTTP request + +This URL path returns a list of submissions that includes the 100 oldest form submissions marked with a “New” status: + + +GET /forms/{formID}/submission/new + + +> _Note: The status of these form submissions will not change to “Downloaded”._ + +##### Response status + + + + + + + + + + +
Status code Example message Meaning or how to fix
200 +[ + { + "name": "05-09-09f4", + "createdAt": 1725553403512 + }, + { + "name": "05-09-9620", + "createdAt": 1725553404965 + }, + { + "name": "05-09-75dc", + "createdAt": 1725553404972 + } +] + + List of new submission names successfully retrieved. +
+ +> _Note: The timestamp is in Coordinated Universal Time (UTC)._ + +### Retrieving **specific** form submissions + +#### Getting the form submission + +##### HTTP request + +This URL path returns one form submission per request based on the respective submission name: + + +GET /forms/{formID}/submission/{submissionName} + + +> _Note: Retrieval is only possible for 1 submission, with 1 download per request. The status of these form submissions will not change to “Downloaded”. To change the status of the form submissions, make a request to confirm form submissions once they are properly received._ + +##### Response status + + + + + + + + + + + + + + + +
Status code Example message Meaning or how to fix
200 +{ + "encryptedResponses": "IOWyM7bpo+wVCXpFkU13JeO0HcxFHTIwLX17ol+jUWdvhicIG+fJj", + "encryptedKey": "IOWyM7bpELZg4kPBOPVe7jeHcxFHTIwLX17ol+jUw6KGictIG+fJj", + "encryptedNonce": "GVyPXC/6UTteJ3uf8d6doBNbppHzKjEXDxwE2DXQbD30/vIxlsY", + "encryptedAuthTag": "VRMt87LgedVo+wVCXpFkU13JeO0guDGHb48XVpvWdvhs3bv/D" +} + + Encrypted data of the submission successfully retrieved. +
404 +{ + "error": "Form submission does not exist" +} + + "Form ID" and/or "Submission name" are incorrect or cannot be found. +
+ +#### Decrypting the data + +Security of the system is paramount and it is enhanced by encrypting form submissions. While encrypted in HTTPS, we’ve added another layer of security with AES-256-GSM encryption. When you get a form submission it will be encrypted and it will come with an encrypted key, an encrypted nonce, and an encrypted AuthTag. These can be decrypted using the Private key. + +### Getting files that were attached to form submissions securely + +The retrieval of files attached within submissions is only possible via the API data delivery method. The same storage conditions apply as for submission data, where GC Forms only temporarily keeps data and files until downloaded and confirmed. To learn more about adding a file upload element to your form, consult our [Guidance page](https://articles.alpha.canada.ca/forms-formulaires/files-attachments/). + +Files are linked directly within responses (retrieval from AWS) and are accompanied by an attribute that shows whether scanning for malicious files detected any potential harm, with basic antivirus technology. Be careful when opening any attachments: choose a secure location, follow security guidelines, and run host-level sensors if possible. The safety of files cannot be guaranteed, as no software is perfect at detecting threats. + +While there is a file scanning software implemented via Amazon Web Services (AWS), to mark files, it is your responsibility to check the isPotentiallyMalicious attribute before opening the linked files. You can decide how to use this attribute, for example: sending a warning in the logs, running a quarantine system, or other logic you develop based on how you and your security team want to safeguard against malicious files. + +#### To retrieve files uploaded to a form: + +You will receive each submission as a raw JSON file that includes direct links to files, if attached. These direct download links are only valid for 10 seconds for security reasons.  + +As files are scanned upon being submitted, they may also be marked as potentially being malicious, based on the file scanning software. You must decide what to do with flagged files — whether to download those files or not if they include a flagged with a “malicious” or “bad” metadata attribute. Add some code for how to handle the encountering of isPotentiallyMalicious:true. You'll then be able to access submission data and files attached in your system.  + +### **Confirming** form submissions + +The confirmation step helps ensure the form submissions are exploitable and render as expected before they are permanently removed from the GC Forms system’s database. + +Before confirming a response, ensure you’ve got access to all attached files, if any are present. These file download links are only temporarily available, as they are valid for up to 10 seconds. If you have not received all files, redownload the same form submission to get new download links. Report a problem if there is one with file or data. Once you are confident that you indeed have all submission data and files submitted, confirm that all data has been transferred successfully. Responses and files will then be removed from GC Forms after 30 days. + +#### Example of response data with files + +Data will look something like this when files are attached: + + +{ +   "createdAt":1749476854628, +   "status":"New", +   "confirmationCode":"714dfe46-6fa1-4281-8d15-a39bcebc3c4f", +   "answers":"{\"1\":\"Test1\",\"2\":\"form_attachments/2025-06-09/8b42aafd-09e9-44ad-9208-d3891a7858df/output.txt\",\"3\":\"form_attachments/2025-06-09/9064b3c7-eee5-4599-99c8-a257b2b5f37d/a0393b10-396c-4b8d-a97c-15394fddda86.jpg\",\"4\":\"form_attachments/2025-06-09/0c7c3414-05e2-4ae6-a825-683857e4c0c4/IMG_0441.jpeg\"}", +   "checksum":"cc33cb49f6c088bf98b7315794db216e", +   "attachments":[ +      { + "id": "04d8aff7-25d7-49e5-8f01-77a8b6fba214", + "name":"output.txt", + "downloadLink":"https://...", + "isPotentiallyMalicious":true, + "md5":"54b0c58c7ce9f2a8b551351102ee0938" +      } +   ] +} + + +##### HTTP request + +This URL path confirms form submissions were successfully retrieved from the system, thus removing them: + + +PUT /forms/{formID}/submission/{submissionName}/confirm/{confirmationCode} + + +> _Note: Confirmation is only possible for one submission at a time, with one confirmation code per request. The status of that submission will be modified from “New” to “Confirmed”. This is similar to the two-step “Download” and “Sign off on removal” process in the application._ + +##### Response status + + + + + + + + + + + + + + + + + + + + + + + + + +
Status code Example message Meaning or how to fix
200OK Submission has been successfully confirmed
200 +{ + "info": "Form submission is already confirmed" +} + + Submission has already been successfully confirmed. +
400 +{ + "error": "Confirmation code is incorrect" +} + + Provided confirmation code is not the one associated with the submission to be confirmed. +
404 +{ + "error": "Form submission does not exist" +} + + "Form ID" and/or "Submission name" are incorrect or cannot be found. +
+ +### Reporting a problem with a form submission + +##### HTTP request + +This URL path identifies a form submission as having a problem if something unexpected occurs: + + +POST /forms/{formID}/submission/{submissionName}/problem + + +> _Note: Reporting a problem is only possible for one submission at a time. You can report a problem with form submissions that are “New” or “Confirmed” as long as they have not been removed from the system. This will change the status to “Problem” and block the submission’s removal from the system until the problem is resolved._ + +##### Example payload for reporting a problem + +To report a problem include a message formatted like the one below in the HTTP POST request body: + + +{ + “contactEmail”: “something@somethingelse.com”, + “description”: “Here is my problem”, + “preferredLanguage”: “en” (either “en” or “fr”) +} + + +> _Note: This will be directed to our support team._ + + +##### Response status + + + + + + + + + + + + + + + + + + + + + + + + + +
Status code Example message Meaning or how to fix
200OK Submission has been successfully reported as having a problem
200 + +{ + "info": "Form submission is already confirmed" +} + + + Submission has already been successfully reported as having a problem. +
400 +{ + "error": "Invalid payload", + "details": [ + { + "type": "field", + "value": "test@cds-snc", + "msg": "Invalid value", + "path": "contactEmail", + "location": "body" + }, + { + "type": "field", + "value": "", + "msg": "Must be at least 10 characters long", + "path": "description", + "location": "body" + } + ] +} + + Details about why the provided payload is invalid. +
400 +{ + "error": "Form submission does not exist" +} + + “Form ID” and/or “Submission name” are incorrect or could not be found. +
+ +### Getting form questions + +##### HTTP request + +This URL path retrieves the questions that were asked in JSON format so they can more easily be associated with the answer data retrieved: + + +GET /forms/{formID}/template + + +> _Note: The questions will be in a data structure that is JSON format when retrieved. This helps if you’ll be transforming the data and need to match the answers to the questions._ + +You can configure question attributes with customizable unique **Question IDs** and **additional tags**. These two attributes can be used to help map form response data to target systems or destination fields in a database, making it easier to update API integrations with a consistent way of mapping response data. + +You might use these attributes to: +- Clarify a field's purpose +- Organize and sort data +- Support automation + +Learn more below. + +##### Response status + + + + + + + + + + + + + + + +
Status code Example message Meaning or how to fix
200 + + { + "layout": [ + 1 + ], + "titleEn": "Test Form", + "titleFr": "Formulaire de test", + "elements": [ + { + "id": 1, + "type": "textField", + "properties": { + "choices": [ + { + "en": "", + "fr": "" + } + ], + "titleEn": "This is a question", + "titleFr": "C'est une question", + "validation": { + "required": false + }, + "subElements": [], + "descriptionEn": "", + "descriptionFr": "", + "placeholderEn": "", + "placeholderFr": "" + } + } + ], + "confirmation": { + "descriptionEn": "Confirmed", + "descriptionFr": "Confirmation", + "referrerUrlEn": "", + "referrerUrlFr": "" + }, + "introduction": { + "descriptionEn": "Description", + "descriptionFr": "Description" + }, + "privacyPolicy": { + "descriptionEn": "Private", + "descriptionFr": "Privé" + } +} + Form template data successfully retrieved.
404 + +{ + "error": "Form template does not exist" +} + + + “Form ID” is incorrect or could not be found. +
+ +#### To set and customize unique Question IDs + +The Question ID is a unique value that allows you to consistently refer to a form element so that it can be matched across republished form versions, or other data structures and systems. This can provide a more scannable and useful way to identify and reference a question field. For example, a standard way to reference all first name questions or phone numbers across forms, so they land in the right place without having to be rewired individually. + +**GC Forms gives you the option to configure a single unique question ID:** + +1. Create a new form or navigate to an existing form in GC Forms. +2. In “Edit”, select a question and click “More”. +3. In the modal, scroll down to “Customize API data attributes”. +4. Modify the Question ID to a unique value of your choice. + +#### To label and organize data with Additional tags + +The additional tags are flexible labels that allow you to add metadata to form elements so that related questions can be marked, grouped, or categorized allowing data to be read, searched, sorted, and transformed by machines more meaningfully and easily. + +**GC Forms gives you the option to configure multiple tags:** + +1. Create a new form or navigate to an existing form in GC Forms. +2. In “Edit”, select a question and click “More”. +3. In the modal, scroll down to “Customize API data attributes”. +4. Add multiple tags to a question to mark data in a helpful way. diff --git a/docs/_pages/en/monitoring.md b/docs/_pages/en/monitoring.md new file mode 100644 index 00000000..f0ba8edc --- /dev/null +++ b/docs/_pages/en/monitoring.md @@ -0,0 +1,42 @@ +--- +layout: page +title: "Monitoring" +lang: en +permalink: "/monitoring/" +trans_url: "/monitoring/" +--- + +### Common errors + +These are common errors that apply globally and are the same for every URL path. + +| Status code | Example message | Meaning or how to fix | +| :---------------- | :------ | :---- | +| 401 | Unauthorized | No access token was provided. | +| 401 | Access token has expired | Access token has expired | +| 403 | Forbidden | Could not validate access token OR "Form ID" in request is not allowed to be accessed with the provided access token. | +| 404 | Not found | Invalid request URL. | +| 429 | Too many requests | Refer to API rate limit for the limit number. | +| 500 | Internal server error | Internal error while serving request. | + +#### Reporting errors and feedback +We expect there may be some additional features needed to get the data to target systems successfully. If you encounter an error, please submit a [technical support ticket](https://forms-formulaires.alpha.canada.ca/en/support) and we'll work with you to resolve the issue. Don't hesitate to include any feedback on the implementation so far — what works and what doesn't. Your feedback helps shape the product directly by informing our decisions on what issues are most important. + +### API rate limit + +API throttling allows us to manage high API traffic and helps maintain the system’s stability. This helps avoid overloading situations where too many requests are processed simultaneously. + +The limit for API requests from one form is **500 requests per minute** by default. Should you happen to exceed the limit, you will get a RateLimitError. You may either wait and try again, or request a higher limit for your form. If you require an API request rate increase, please [contact Support](https://forms-formulaires.alpha.canada.ca/en/support) and we'll be able to increase the limit to 1000 requests per minute for a period of time. + +You can see additional information in the headers included as part of the API response: +- X-RateLimit-Limit: the current limit of API requests for your form per 60 seconds +- X-RateLimit-Remaining: the number of remaining API requests within this 60 second interval +- X-RateLimit-Reset: the moment at which the limit will reset to the full limit amount +- Retry-After: the time in seconds to wait before sending another request to the API + +In the future, we anticipate the API call limits may be tied to different use cases or types of usage and will try to determine more appropriate limits for API requests per minute. + +### Rotating API keys + +Rotating an API key may become necessary if a key is compromised. You can generate a new key in form settings in the API integration tab. This also requires a change in the parameters being queried in the request. + diff --git a/docs/_pages/fr/accueil.md b/docs/_pages/fr/accueil.md new file mode 100644 index 00000000..ec303052 --- /dev/null +++ b/docs/_pages/fr/accueil.md @@ -0,0 +1,9 @@ +--- +layout: page +title: "Accueil" +lang: fr +permalink: "/accueil/" +trans_url: "/home/" +--- + +{% include accueil.md %} diff --git a/docs/_pages/fr/authentification.md b/docs/_pages/fr/authentification.md new file mode 100644 index 00000000..a635bf35 --- /dev/null +++ b/docs/_pages/fr/authentification.md @@ -0,0 +1,44 @@ +--- +layout: page +title: "Authentification" +lang: fr +permalink: "/authentification/" +trans_url: "/authentication/" +--- + +### URL de base + + +https://api.forms-formulaires.alpha.canada.ca/v1/... + + + +> _Remarque : il se pourrait que vous deviez utiliser un fournisseur d’infonuagique comme Microsoft Azure ou Amazon Web Services (AWS) pour obtenir cet accès._ + +### Fonctionnement de l'authentification + +L’API de Formulaires GC exploite un flux de jeton Web JSON OAuth 2.0 signé ([JWT](https://jwt.io/)) avec un fournisseur d’identité autohébergé (IDP), Zitadel, pour établir l’authentification entre des machines de manière sécurisée pour le point de terminaison d’API. + +L’authentification se fait par l’intermédiaire d’un jeton opaque et l’autorisation de base est gérée par le biais du profil utilisateur et de l’identifiant du formulaire demandé. Le serveur OAuth fournit un jeton d’accès de courte durée qui peut être utilisé pour faire des demandes API en vérifiant le JWT du client qui a été signé avec une clé privée et qui comprend son identifiant de client. Ce jeton d’accès est valable 30 minutes. + +L’API requiert une authentification à l’aide d’une adresse courriel du gouvernement du Canada, d’un **identifiant de formulaire** connexe et d’un JWT signé avec une **clé privée** et vérifié par l’intermédiaire d’une **clé publique** avec l’IDP. Ce jeton requiert l’identifiant de formulaire connexe. + +### Exemples d’intégration + +Une fois le formulaire créé, vous pouvez remplir des formulaires de manière fictive afin de simuler un formulaire actif et de tester l’intégration. + +Utilisez ces exemples comme référence pour générer des jetons d’accès dans le langage de programmation de votre choix : +- [.NET / C#](https://github.com/cds-snc/forms-api/blob/main/examples/dotnet/AccessTokenGenerator.cs) +- [Node.JS / Typescript](https://github.com/cds-snc/forms-api/blob/main/examples/nodejs/accessTokenGenerator.ts) +- [Python](https://github.com/cds-snc/forms-api/blob/main/examples/python/access_token_generator.py) +- [Bash /Curl](https://github.com/cds-snc/forms-api/blob/main/examples/bash/get_access_token.sh) + +### En-tête d’autorisation + +Utilisez cet en-tête pour chaque demande d’API pour confirmer votre identité : + +
+
curl \ +
--request GET "$FORMS_URL" \ +
--header "Authorization: Bearer $ACCESS_TOKEN" +
diff --git a/docs/_pages/fr/effectuer-des-demandes.md b/docs/_pages/fr/effectuer-des-demandes.md new file mode 100644 index 00000000..4cf81e03 --- /dev/null +++ b/docs/_pages/fr/effectuer-des-demandes.md @@ -0,0 +1,443 @@ +--- +layout: page +title: "Effectuer des demandes" +lang: fr +permalink: "/effectuer-des-demandes/" +trans_url: "/making-requests/" +childPages: + - Récupérer des nouvelles soumissions de formulaires + - Récupérer des soumissions de formulaires spécifiques + - Confirmer des soumissions de formulaires + - Signaler un problème avec les soumissions de formulaire + - Obtenir les questions du formulaire +--- + +L’API de Formulaires GC vous permet de récupérer des soumissions de formulaire sous forme de fichiers JSON. Vous pouvez obtenir jusqu’à 100 soumissions de formulaire par lot, et celles-ci sont accessibles jusqu’à 30 jours après la soumission, compte tenu de nos périodes actuelles de rétention des données. Les soumissions de formulaire sont chiffrées à l’aide d’une clé publique et peuvent être déchiffrées localement par vous. + +### Paramètres de requête + +Les demandes d’API pour les données de soumission de Formulaires GC sont possibles à l’aide de paramètres de requête tels que : + - **{formID}** + L'identifiant de formulaire est une chaîne alphanumérique de 25 caractères trouvée à la fin de l’URL du formulaire ou au début du nom de fichier de la clé API. + Par exemple : ```clzvj8fzb00226o90r2b7l1gt```. + - **{SubmissionName}** + Le nom de la soumission est une chaîne alphanumérique de 11 caractères, y compris les tirets, parfois appelée identifiant de soumission dans l’application Formulaires GC. + Par exemple : ```e02-08-d732```. + - **{ConfirmationCode}** + Le code de confirmation est une chaîne alphanumérique de 36 caractères, y compris les tirets, parfois appelée code de réception dans l’application Formulaires GC. + Par exemple : ```620b203c-9836-4000-bf30-1c3bcc26b834```. + +### Exemples d'intégration + +Nous avons élaboré quelques exemples auxquels vous pouvez vous référer, ou pour lesquels vous pouvez exécuter le programme, et qui, nous l’espérons, faciliteront l’intégration. Voici les langages de programmation disponibles à l’heure actuelle : + +- [.NET / C#](https://github.com/cds-snc/forms-api/tree/main/examples/dotnet) +- [Node.JS / Typescript](https://github.com/cds-snc/forms-api/tree/main/examples/nodejs) +- [Python](https://github.com/cds-snc/forms-api/tree/main/examples/python) +- [Bash /Curl](https://github.com/cds-snc/forms-api/tree/main/examples/bash) + +Travaillez avec votre équipe de développement pour tester une ébauche de formulaire et vous assurer que l'intégration de l'API reçoit des réponses, qu'elle est capable de décrypter, de vérifier le hachage et de confirmer les réponses ou de signaler un problème. Une fois ce processus terminé, vous êtes prêt à publier votre formulaire avec cette méthode de livraison des données. + +### Récupérer des **nouvelles** soumissions de formulaires + +##### Demande HTTP + +Ce chemin d’URL renvoie une liste de soumissions qui comprend les 100 plus anciennes soumissions de formulaire ayant le statut « Nouveautés » : + + +GET /forms/{formID}/submission/new + + +> _À noter : Le statut de ces soumissions de formulaire ne passera pas à « Téléchargé »._ + +##### Statut de la réponse + + + + + + + + + + +
Code du statut Exemple de message Signification / comment corriger
200 +[ + { + "name": "05-09-09f4", + "createdAt": 1725553403512 + }, + { + "name": "05-09-9620", + "createdAt": 1725553404965 + }, + { + "name": "05-09-75dc", + "createdAt": 1725553404972 + } +] + + Liste des nouveaux noms de soumission récupérés avec succès. +
+ +> _À noter : L'horodatage est en temps universel coordonné (TUC)._ + +### Récupérer des soumissions de formulaires **spécifiques** + +#### Obtenir la soumission du formulaire + +##### Demande HTTP + +Ce chemin d’URL renvoie une soumission de formulaire par demande en fonction du nom de soumission respectif : + +GET /forms/{formID}/submission/{submissionName} + + +> _Remarque : La récupération n’est possible que pour une soumission, avec un téléchargement par demande. Le statut de ces soumissions de formulaire ne passera pas à « Téléchargé ». Pour modifier le statut des soumissions de formulaires, faites une demande de confirmation des soumissions de formulaire une fois qu’elles ont été correctement reçues._ + +##### Statut de la réponse + + + + + + + + + + + + + + + +
Code du statut Exemple de message Signification / comment corriger
200 +{ + "encryptedResponses": "IOWyM7bpo+wVCXpFkU13JeO0HcxFHTIwLX17ol+jUWdvhicIG+fJj", + "encryptedKey": "IOWyM7bpELZg4kPBOPVe7jeHcxFHTIwLX17ol+jUw6KGictIG+fJj", + "encryptedNonce": "GVyPXC/6UTteJ3uf8d6doBNbppHzKjEXDxwE2DXQbD30/vIxlsY", + "encryptedAuthTag": "VRMt87LgedVo+wVCXpFkU13JeO0guDGHb48XVpvWdvhs3bv/D" +} + + Les données chiffrées de la soumission ont été récupérées avec succès. +
404 +{ + "error": "Form submission does not exist" +} + + Le renseignement “Form ID” (identifiant du formulaire) ou “Submission name” (nom de soumission) est incorrect ou introuvable. +
+ +#### Déchiffrement des données + +La sécurité du système est primordiale et elle est renforcée par le chiffrement des soumissions de formulaires. Bien que le chiffrement soit effectué en HTTPS, nous avons ajouté une autre couche de sécurité avec le chiffrement AES-256-GSM. Lorsque vous recevrez une soumission de formulaire, elle sera chiffrée et elle comprendra une clé chiffrée, un nonce chiffré et un élément AuthTag chiffré. Ceux-ci peuvent être déchiffrés par l’intermédiaire d’une clé privée. + +### Obtenir les fichiers joints aux soumissions de formulaires + +La récupération des fichiers joints aux soumissions n'est possible que via la méthode de livraison des données par l'entremise de l'API. Les conditions de stockage sont les mêmes que pour les données soumises, Formulaires GC ne conserve les données et les fichiers que temporairement, jusqu'à leur téléchargement et leur confirmation. Pour en savoir plus sur l'ajout d'éléments de téléversement de fichiers à votre formulaire, consultez notre [guide de référence](https://articles.alpha.canada.ca/forms-formulaires/fr/fichiers-joints/). + +Les fichiers sont directement liés aux réponses (récupérées de AWS) et ils sont accompagnés d'un attribut indiquant si l'analyse des fichiers malveillants a détecté un danger potentiel à l'aide de technologie antivirus de base. Soyez prudent lorsque vous ouvrez n'importe quelle pièces jointes : choisissez un emplacement sécurisé, suivez les consignes de sécurité, et exécutez des capteurs au niveau de l'hôte si possible. La sécurité des fichiers ne peut être garantie, car aucune technologie n'est parfaite pour détecter les menaces. + +Bien qu'un logiciel d'analyse de fichiers soit mis en œuvre avec Amazon Web Services (AWS), pour marquer les fichiers, il est de votre responsabilité de vérifier l'attribut de malveillance isPotentiallyMalicious avant d'ouvrir les fichiers liés. Vous pouvez décider de la manière d'utiliser cet attribut, par exemple : envoyer un avertissement dans les journeaux, exécuter un système de quarantaine ou toute autre logique que vous développez en fonction de la manière dont vous et votre équipe de sécurité souhaitez vous protéger contre les fichiers malveillants. + +#### Pour récupérer les fichiers téléchargés vers un formulaire en toute sécurité : + +Vous recevrez chaque soumission sous forme de fichier JSON brut comprenant des liens directs vers les fichiers, s'ils sont joints. Pour des raisons de sécurité, ces liens de téléchargement direct ne sont valables que pendant 10 secondes. + +Les fichiers étant analysés lors de leur soumission, ils peuvent également être signalés comme potentiellement malveillants, en fonction du logiciel d'analyse des fichiers. Vous devez décider quoi faire avec les fichiers signalés — soit que vous les téléchargez ou non, s'ils comportent un attribut de métadonnées « malveillant » ou « mauvais ». + +Ajoutez du code pour gérer les cas où les fichiers sont signalés et isPotentiallyMalicious:true est rencontré. Vous pourrez ensuite accéder aux données soumises et aux fichiers sécurisés joints dans votre système. + +### **Confirmer** des soumissions de formulaires + +L’étape de confirmation permet de s’assurer que les formulaires soumis sont exploitables et s’affichent comme prévu avant qu’ils ne soient définitivement retirés de la base de données du système Formulaires GC. + +Avant de confirmer une réponse, assurez-vous d'avoir accès à tous les fichiers joints, le cas échéant. Ces liens de téléchargement de fichiers ne sont disponibles que temporairement, car ils ne sont valables que pendant 10 secondes. Si vous n'avez pas reçu tous les fichiers, téléchargez à nouveau le même formulaire soumis pour obtenir de nouveaux liens de téléchargement. Signalez tout problème lié aux fichiers ou aux données. Une fois que vous êtes certain d'avoir bien reçu toutes les données et tous les fichiers soumis, confirmez que toutes les données ont été transférées avec succès. Les réponses et les fichiers seront ensuite supprimés de GC Forms après 30 jours. + +#### Exemple de données de réponses avec fichiers + +Les données ressembleront à ceci lorsque des fichiers sont joints : + + +{ +   "createdAt":1749476854628, +   "status":"New", +   "confirmationCode":"714dfe46-6fa1-4281-8d15-a39bcebc3c4f", +   "answers":"{\"1\":\"Test1\",\"2\":\"form_attachments/2025-06-09/8b42aafd-09e9-44ad-9208-d3891a7858df/output.txt\",\"3\":\"form_attachments/2025-06-09/9064b3c7-eee5-4599-99c8-a257b2b5f37d/a0393b10-396c-4b8d-a97c-15394fddda86.jpg\",\"4\":\"form_attachments/2025-06-09/0c7c3414-05e2-4ae6-a825-683857e4c0c4/IMG_0441.jpeg\"}", +   "checksum":"cc33cb49f6c088bf98b7315794db216e", +   "attachments":[ +      { + "id": "04d8aff7-25d7-49e5-8f01-77a8b6fba214", + "name":"output.txt", + "downloadLink":"https://...", + "isPotentiallyMalicious":true, + "md5":"54b0c58c7ce9f2a8b551351102ee0938" +      } +   ] +} + + +##### Demande HTTP + +Le chemin d’URL confirme que les soumissions de formulaires ont bien été récupérées à partir du système et les supprime : + + +PUT /forms/{formID}/submission/{submissionName}/confirm/{confirmationCode} + + +> _Remarque : La confirmation n’est possible que pour une soumission à la fois, avec un code de confirmation par requête. Le statut de cette soumission passera de « Nouveautés » à « Confirmations ». Il s’agit d’un processus similaire au processus en deux étapes de téléchargement et d’approbation de la suppression dans l’application._ + +##### Statut de la réponse + + + + + + + + + + + + + + + + + + + + + + + + + +
Code du statut Exemple de message Signification / comment corriger
200OK La soumission a bien été confirmée.
200 +{ + "info": "Form submission is already confirmed" +} + + La soumission a déjà été confirmée. +
400 +{ + "error": "Confirmation code is incorrect" +} + + Le code de confirmation fourni ne correspond pas à la soumission à confirmer. +
404 +{ + "error": "Form submission does not exist" +} + + Le renseignement “form ID” ou “Submission name” est incorrect ou introuvable. +
+ +### Signaler un problème avec la soumission de formulaire + +##### Demande HTTP + +Ce chemin d’URL identifie une soumission de formulaire comme ayant un problème si un évènement inattendu se produit : + + +POST /forms/{formID}/submission/{submissionName}/problem + + +> _Remarque : Le signalement d’un problème n’est possible que pour une seule soumission à la fois. Vous pouvez signaler un problème avec les soumissions de formulaires portant le statut « Nouveautés » ou « Confirmations » tant qu’elles n’ont pas été supprimées du système. Cela changera le statut en « Problème » et bloquera la suppression de la soumission du système jusqu’à ce que le problème soit résolu._ + +##### Exemple de charge utile pour le signalement d’un problème + +Pour signaler un problème, incluez un message au format similaire à celui ci-dessous dans le corps de la requête HTTP POST : + + +{ + “contactEmail”: “nom@ministere.gc.ca”, + “description”: “Voici mon problème”, + “preferredLanguage”: “fr” (soit “en” ou “fr”) +} + + +> _Remarque : Ce message sera transmis à notre équipe de soutien._ + + +##### Statut de la réponse + + + + + + + + + + + + + + + + + + + + + + + + + +
Code du statut Exemple de message Signification / comment corriger
200OK La soumission a bien été signalée comme ayant un problème.
200 + +{ + "info": "Form submission is already confirmed" +} + + + La soumission a déjà été signalée comme ayant un problème. +
400 +{ + "error": "Invalid payload", + "details": [ + { + "type": "field", + "value": "test@cds-snc", + "msg": "Invalid value", + "path": "contactEmail", + "location": "body" + }, + { + "type": "field", + "value": "", + "msg": "Must be at least 10 characters long", + "path": "description", + "location": "body" + } + ] +} + + Détails sur la raison pour laquelle la charge utile fournie n’est pas valide. +
400 +{ + "error": "Form submission does not exist" +} + + Le renseignement “Form ID” ou “Submission name” est incorrect ou n’a pas pu être trouvé. +
+ +### Obtenir les questions du formulaire + +##### Demande HTTP + +Ce chemin d’URL récupère les questions qui ont été posées au format JSON afin qu’elles puissent être plus facilement associées aux réponses récupérées : + + +GET /forms/{formID}/template + + +> _Remarque : Les questions seront dans une structure de données au format JSON lorsqu’elles seront récupérées. Cela est utile si vous transformez les données et que vous devez faire correspondre les réponses aux questions._ + +Vous pouvez configurer les attributs des questions avec des **identifiants de question** et des **balises supplémentaires**. Ces deux attributs peuvent être utilisés pour aider à mettre en correspondance les données des réponses aux formulaires avec les systèmes cibles ou les champs de destination dans une base de données. Ceci facilite la mise à jour d'intégrations API avec une manière cohérente de mapper les données des réponses. + +Vous pouvez utiliser ces attributs pour : +- clarifier l'objectif d'un champ +- organiser et trier les données +- soutenir l'automatisation + +Pour en savoir plus, voir ci-dessous. + +##### Statut de la réponse + + + + + + + + + + + + + + + +
Code du statut Exemple de message Signification / comment corriger
200 + + { + "layout": [ + 1 + ], + "titleEn": "Test Form", + "titleFr": "Formulaire de test", + "elements": [ + { + "id": 1, + "type": "textField", + "properties": { + "choices": [ + { + "en": "", + "fr": "" + } + ], + "titleEn": "This is a question", + "titleFr": "C'est une question", + "validation": { + "required": false + }, + "subElements": [], + "descriptionEn": "", + "descriptionFr": "", + "placeholderEn": "", + "placeholderFr": "" + } + } + ], + "confirmation": { + "descriptionEn": "Confirmed", + "descriptionFr": "Confirmation", + "referrerUrlEn": "", + "referrerUrlFr": "" + }, + "introduction": { + "descriptionEn": "Description", + "descriptionFr": "Description" + }, + "privacyPolicy": { + "descriptionEn": "Private", + "descriptionFr": "Privé" + } +} +Les données relatives au modèle de formulaire ont bien été récupérées. + +
404 + +{ + "error": "Form template does not exist" +} + + + Le renseignement “Form ID” est incorrect ou n’a pas pu être trouvé. +
+ +#### Pour définir et personnaliser des identifiants de question uniques + +L'identifiant de la question est une valeur unique qui vous permet de faire référence de manière cohérente à un élément de formulaire afin qu'il puisse être mis en correspondance avec des versions de formulaire republiées ou d'autres structures et systèmes de données. Il peut s'agir d'un moyen plus facile et plus utile d'identifier et de se référer à un champ de question. Par exemple, une manière standard de se reférer à toutes les questions sur le prénom ou les numéros de téléphone dans tous les formulaires, de sorte qu'ils se retrouvent au bon endroit sans avoir à être tracées individuellement. + +**Formulaires GC vous offre la possibilité de configurer un identifiant de question unique :** + +1. Créez un nouveau formulaire ou naviguez vers un formulaire existant dans Formulaires GC. +2. Dans « Modifier », sélectionnez une question et cliquez sur « Plus ». +3. Dans la fenêtre modale, faites défiler vers le bas jusqu'à « Personnaliser les attributs de données de l'API ». +4. Modifiez l'identifiant de la question avec une valeur unique de votre choix. + +#### Pour étiqueter et organiser les données avec des balises supplémentaires + +Les balises supplémentaires sont des étiquettes flexibles qui vous permettent d'ajouter des métadonnées aux éléments du formulaire afin que les questions connexes puissent être marquées, regroupées ou catégorisées, ce qui permet de lire, de rechercher, de trier et d'organiser les données. + +**Formulaires GC vous offre la possibilité de configurer plusieurs balises :** + +1. Créez un nouveau formulaire ou naviguez vers un formulaire existant dans Formulaires GC. +2. Dans « Modifier », sélectionnez une question et cliquez sur « Plus ». +3. Dans la fenêtre modale, faites défiler vers le bas jusqu'à « Personnaliser les attributs de données de l'API ». +4. Ajoutez plusieurs balises à une question pour marquer les données de manière utile. diff --git a/docs/_pages/fr/obtenir-du-soutien.md b/docs/_pages/fr/obtenir-du-soutien.md new file mode 100644 index 00000000..efdd0af0 --- /dev/null +++ b/docs/_pages/fr/obtenir-du-soutien.md @@ -0,0 +1,11 @@ +--- +layout: page +title: "Obtenir du soutien" +lang: fr +permalink: "/obtenir-du-soutien/" +trans_url: "/getting-support/" +--- + +Nous sommes ouvert·e·s à toutes les questions et accueillons tous les commentaires que vous pourriez avoir pour nous aider à nous améliorer. Vous pouvez contacter directement en cas de question ou si vous avez besoin de soutien technique, en remplissant le [formulaire de soutien](https://forms-formulaires.alpha.canada.ca/fr/support). + +> Veuillez inclure votre nom, votre adresse courriel, l’identifiant du formulaire, les identifiants de soumissions (si cela est pertinent) et une description du problème. diff --git a/docs/_pages/fr/pour-commencer.md b/docs/_pages/fr/pour-commencer.md new file mode 100644 index 00000000..64992d84 --- /dev/null +++ b/docs/_pages/fr/pour-commencer.md @@ -0,0 +1,23 @@ +--- +layout: page +title: "Pour commencer" +lang: fr +permalink: "/pour-commencer/" +trans_url: "/getting-started/" +--- + +### Survol + +Le but de cette API est de récupérer des soumissions de formulaires de manière sécurisée et fiable directement depuis le point de terminaison d’API. Cela devrait alléger la charge de travail associée aux volumes élevés de soumissions de formulaires. Ainsi, plutôt que de télécharger et d’approuver manuellement la suppression des réponses pour confirmer leur récupération à partir de la base de données, l’API automatisera le flux de travail avec des systèmes communiquant entre eux et échangeant des données. + +### Ce dont vous aurez besoin + - Un [compte Formulaires GC](https://articles.alpha.canada.ca/forms-formulaires/fr) + - Un formulaire d'ébauche (Non-classifié, Protégé A ou Protégé B) + - Une clé API (que vous générez dans la section Paramètres > Intégration API) + + - Un formulaire configuré pour « Recevoir les réponses via l'API » dans les paramètres. + - Quelques réponses « fictives » soumises dans le formulaire + - Un système cible dans lequel vous prévoyez de recevoir des données de soumission de formulaire + - Des ressources de développement d’infrastructure capables de créer des demandes HTTP pour atteindre l’API. _Vous ne disposez d’aucune capacité de développement d’infrastructure? Cette première version de l’API pourrait ne pas être tout à fait prête pour vous._ + +> **IMPORTANT: Vous devez protéger cette clé API, car elle sert à authentifier les demandes API et pourrait être utilisée pour accéder à des données protégées. Si vous devez communiquer la clé à un·e membre d’une équipe de développement pour configurer l’intégration, utilisez un courriel chiffré.** diff --git a/docs/_pages/fr/surveiller.md b/docs/_pages/fr/surveiller.md new file mode 100644 index 00000000..8aa94f42 --- /dev/null +++ b/docs/_pages/fr/surveiller.md @@ -0,0 +1,41 @@ +--- +layout: page +title: "Surveiller" +lang: fr +permalink: "/surveiller/" +trans_url: "/monitoring/" +--- + +### Erreurs courantes + +Il s’agit d’erreurs courantes qui s’appliquent généralement et sont les mêmes pour tous les chemins d’URL. + +| Code du statut | Exemple de message | Signification / comment corriger | +| :---------------- | :------ | :---- | +| 401 | Unauthorized | Aucun jeton d’accès n’a été fourni. | +| 401 | Access token has expired | Le jeton d’accès a expiré. | +| 403 | Forbidden | Le jeton d’accès n’a pas pu être validé OU le renseignement “Form ID” est inaccessible avec le jeton d’accès fourni. | +| 404 | Not found | L’URL de requête n’est pas valide. | +| 429 | Too many requests | Reportez-vous à la limite du débit de l'API pour le nombre maximal. | +| 500 | Internal server error | Une erreur interne est survenue durant le traitement de la requête. | + +#### Signalement d'erreurs et retours d'information +Nous nous attendons à ce que des fonctionnalités supplémentaires soient nécessaires pour que les données soient transmises avec succès aux systèmes cibles. Si vous rencontrez une erreur, veuillez soumettre un [ticket de support technique] (https://forms-formulaires.alpha.canada.ca/en/support) et nous travaillerons avec vous pour résoudre le problème. N'hésitez pas à nous faire part de vos commentaires sur la mise en œuvre jusqu'à présent - ce qui fonctionne et ce qui ne fonctionne pas. Vos commentaires contribuent à façonner directement le produit en éclairant nos décisions sur les questions les plus importantes. + +### Limite du débit de l'API + +Les limitations en matière de requêtes API nous permet de gérer le trafic élevé et de maintenir la stabilité du système. Cela permettra d’éviter les situations de surcharge où trop de demandes sont traitées simultanément. + +La limite des demandes d'API à partir d'un formulaire est de **500 demandes par minute** par défaut. Si vous dépassez cette limite, vous obtiendrez une erreur 429 RateLimitError. Vous pouvez soit attendre et réessayer, ou demander une limite plus élevée pour votre formulaire. Si vous avez besoin d'une augmentation du nombre de requêtes API, veuillez [contactez l'équipe de soutien](https://forms-formulaires.alpha.canada.ca/fr/support) et nous pourrons augmenter la limite à 1000 demandes par minute pendant certaine période de temps. + +Vous pouvez obtenir des informations supplémentaires dans les en-têtes inclus dans la réponse de l'API : +- X-RateLimit-Limit : la limite actuelle de demandes API pour votre formulaire par 60 secondes +- X-RateLimit-Remaining : le nombre de demandes d'API restantes dans cet intervalle de 60 secondes +- X-RateLimit-Reset : le moment où la limite sera réinitialisée au montant total de la limite +- Retry-After : le temps en secondes à attendre avant d'envoyer une autre demande à l'API + +À l'avenir, nous prévoyons que les limites d’API seront liées à différents cas d’utilisation ou types d’utilisation et nous essaierons de déterminer des limites plus appropriées par minute pour les demandes d’API. + +### Rotation des clés API + +L’La rotation d’une clé API peut s'avérer nécessaire si une clé est compromise. Vous pouvez générer une nouvelle clé dans les paramètre du formulaire, sous l'onglet Intégration API. Cela nécessite simplement une modification des paramètres demandés dans la demande. diff --git a/docs/accueil.html b/docs/accueil.html new file mode 100644 index 00000000..e7448deb --- /dev/null +++ b/docs/accueil.html @@ -0,0 +1,7 @@ +--- +layout: default +lang: fr +trans_url: /home +--- +{% capture index %}{% include accueil.md %}{% endcapture %} +{{ index | markdownify }} diff --git a/docs/assets/Construction GSINs - Hierarchy of CPC, CCS.xlsx b/docs/assets/Construction GSINs - Hierarchy of CPC, CCS.xlsx new file mode 100644 index 00000000..f747ea63 Binary files /dev/null and b/docs/assets/Construction GSINs - Hierarchy of CPC, CCS.xlsx differ diff --git a/docs/assets/DS_Store b/docs/assets/DS_Store new file mode 100644 index 00000000..3643f5ac Binary files /dev/null and b/docs/assets/DS_Store differ diff --git a/docs/assets/css/custom.css b/docs/assets/css/custom.css new file mode 100644 index 00000000..30abf8b1 --- /dev/null +++ b/docs/assets/css/custom.css @@ -0,0 +1,12 @@ +.main-content p { + margin-top: 1.8em; + margin-bottom: 0.2em; +} + +.main-content ul { + margin-top: 0.3em; +} + +.main-content li { + margin-bottom: 0; +} diff --git a/docs/assets/css/main.css b/docs/assets/css/main.css new file mode 100644 index 00000000..8d058240 --- /dev/null +++ b/docs/assets/css/main.css @@ -0,0 +1,540 @@ +--- +--- + +/* +Main.css +================================== +Begin with generic 'mobile first' styles +*/ + +/* +Normalize the box model +================================== +*/ + +*, +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + +/* +global styles +================================== +*/ + +html, body { + height: 100%; +} + +/* +Typography +================================== +*/ + +body { + font-family: "Avenir Next", Arial, sans-serif; + font-weight: 400; + font-style: normal; + line-height: 1.466666667; +} + +h1, +h3, +h4, +h5, +strong { + font-family: "Avenir Next Demi", "Avenir Next", Arial, sans-serif; + font-weight: 600; +} + +.site-title { + font-size: 1.625em; + font-family: "Avenir Next", Arial, sans-serif; + font-weight: bold; + color: #000000; + margin: 0; + line-height: 1.2941176470588236; + display: inline-block; + width: 70%; +} + +h2 { + font-weight: 600; + font-style: normal; + font-size: 1.375em; + margin: 1.4em 0 0 0; +} + +h4 { + font-size: 1em; +} + +.page-title { + margin-top: .727272727em; /* 16/22 */ +} + +.float-right { + display: inline-block; + width: 25%; + margin: 0; + text-align: right; +} + +.language-switcher { +} +/* +Lists +-------------------------------- +*/ + +.main-content ul { + padding-left: 1.1em; +} + +.main-content li { + margin-bottom: 1em; +} + +li h3, +li h4 { + margin: 0; +} + +li p { + margin-top: 0; +} + +/* +Links +================================== +*/ + +a { + -webkit-transition: .2s; + -moz-transition: .2s; + transition: .2s; +} + +a, +a:link, +a:visited { + color: #4b4085ff !important; + border-bottom: 1px dotted #4b4085ff; + text-decoration: none; +} + +a:hover { + border-bottom: 1px solid #6366F1; + color: #6366F1; + text-decoration: none; +} + +a:active { + border-bottom: 1px solid #6366F1; + color: #6366F1; + text-decoration: none; +} + +a:focus { + border-bottom: 1px solid #6366F1; + color: #000000; + outline: thin dotted; + text-decoration: none; +} + +a.title-link { + color: ##000000; + border-bottom: none; +} + +a.title-link:hover, +a.title-link:active, +a.title-link:focus { + color: #6366F1; + border-bottom: none; +} + +a.skip-link { + color: #26374A; + border-bottom: none; + padding: .25em; +} + +a.skip-link:hover, +a.skip-link:active, +a.skip-link:focus { + background-color: #26374A; + color: #fff; + border-bottom: none; +} + + +/* +Navigation +================================== +*/ + +.sidebar-nav a { + display: block; + padding: 10px; + -webkit-transition: unset; + transition: unset; +} +.sidebar-nav a, +.sidebar-nav a:link, +.sidebar-nav a:visited { + border-bottom: none; + color: #26374A; +} +.sidebar-nav li:hover, +.sidebar-nav a:focus, +.sidebar-nav li:active, +.sidebar-nav .sidebar-nav-active { + color: #26374A; + border-left: 4px solid {{ site.brand_color }}; + background-color: transparent; + border-bottom: 1px solid #babbbd; +} +.sidebar-nav li:hover, +.sidebar-nav li:active, +.sidebar-nav .sidebar-nav-active { + padding-left: 0; +} +.sidebar-nav a:focus { + padding-left: 6px; +} +.sidebar-nav li:hover a:focus, +.sidebar-nav li:active a:focus { + border-left: none; + padding-left: 10px; +} +.sidebar-nav ul { + margin: 0; + padding: 0; + /*border-top: 1px solid @gray-50;*/ +} +.sidebar-nav li { + list-style: none; + border-bottom: 1px solid #babbbd; + font-size: 1.125em; + padding-left: 4px; +} +.sidebar-nav li:last-child { + border-bottom: none; +} + + +/* +Layout +================================== +*/ + +.logo { + display: block; +} + +.content { + padding-top: 2em; + padding-bottom: 2em; +} + +/* offset the fixed position header for jump links */ +section:before { + display: block; + content: ""; + height: 60px; + margin: -60px 0 0; +} + +.wrap { + max-width: 1200px; + margin: 0 auto; + padding-left: 20px; + padding-right: 20px; +} + +header { + width: 100%; + border-bottom: 4px solid {{ site.brand_color }}; + background-color: #fff; + padding: 2em 0; +} + + +/* +Footer +================================== +*/ + +/* for sticky footer */ +.container { + display: table; + height: 100%; + width: 100%; +} + +footer { + display: table-row; /* for sticky footer */ + height: 1px; /* for sticky footer */ + border-top: 2px solid #babbbd; + background: #f1f2f2; + width: 100%; + font-size: 0.875em; +} + +footer .wrap { + padding-top: 2em; + padding-bottom: 2em; +} + + +/* +Helpers +================================== +*/ + +/* Hide from both screenreaders and browsers: h5bp.com/u */ +.hidden { + display: none !important; + visibility: hidden; +} + +/* Hide only visually, but have it available for screenreaders: h5bp.com/v */ +.visuallyhidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +/* Extends the .visuallyhidden class to allow the element to be focusable + * when navigated to via the keyboard: h5bp.com/p */ +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; +} + +/* Hide visually and from screenreaders, but maintain layout */ +.invisible { + visibility: hidden; +} + + +/* +Style +================================== +*/ + +.intro { + color: #1E293B; +} + +li h4 { + margin: 0; +} + +.license { + font-family: "Avenir Next Demi", Arial, sans-serif; + font-weight: normal; + font-style: normal; +} + +pre { + max-width: 100%; + font-size: 0.875em; + overflow-y: scroll; + background-color: #f1f2f2; + padding: 10px; +} + +td, th { + border: 1px solid grey +} +/* +Post list +---------------------------------- +*/ + +ul.posts { + padding: 0; +} + +.posts li { + list-style: none; +} + +.post-date { + color: #64748B; +} + +/* +Repo list +---------------------------------- +*/ + +ul.repo-list { + margin: .5em 0 1em 0; + padding: 0; +} + +.repo-list li { + list-style: none; +} + +.repo-list p { + margin: 0; + font-size: 0.875em; +} + +.repo-list h4 { + text-transform: none; +} + +/* +Helper Classes +================================== +*/ + +/* +Clearfix list +---------------------------------- +*/ + +.group:before, +.group:after { + content: " "; + display: table; +} + +.group:after { + clear: both; +} + +.group { + *zoom: 1; +} + +/* +Desktop Styles +================================== +*/ + +@media screen and (min-width: 45em) and (min-height: 32.5em) { + + /* + Typography + ============================== + */ + + /* + Layout + ============================== + */ + + .logo { + max-width: 30%; + padding-right: 20px; + float: right; + } + + aside { + width: 30%; + float: left; + } + + .main-content { + width: 67%; + float: right; + margin-bottom: 120px; + } + + /* + Navigation + ============================== + */ + + + /* + Style + ============================== + */ + + /* + Repo list + ------------------------------ + */ + + .repo-list li { + list-style: none; + display: block; + float: left; + height: 4.0625em; + max-height: 4.0625em; + background-color: #E7E7E6; + border-left: 1px solid #64748B; + width: 30%; + } + + .repo-list a:link, + .repo-list a:visited { + display: block; + max-height: 4.0625em; + background-color: #E7E7E6; + border-bottom: none; + padding: .625em 1em 1em 1em; + } + + .repo-list a:hover { + color: #64748B; + background-color: #4b4085ff; + } + + .repo-list li:first-child { + text-align: center; + border-left: none; + line-height: 60px; + padding: .625em 1em; + width: 10%; + } + +} + +@media screen and (max-width: 54.375em) and (min-height: 32.5em) { + + /* keep the repo list containers the same height, but account for the need for more height */ + + .repo-list li { + height: 6em; + max-height: 6em; + } + + .repo-list a:link, + .repo-list a:visited { + max-height: 6em; + } +} + +/* +Mobile Styles +================================== +*/ + +@media screen and (max-width: 40.5em) { + + .main-content { + margin-top: 1.5em; + } + +} diff --git a/docs/assets/css/normalize.css b/docs/assets/css/normalize.css new file mode 100644 index 00000000..daf1392c --- /dev/null +++ b/docs/assets/css/normalize.css @@ -0,0 +1,540 @@ +--- +--- + +/* +Main.css +================================== +Begin with generic 'mobile first' styles +*/ + +/* +Normalize the box model +================================== +*/ + +*, +*:before, +*:after { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } + +/* +global styles +================================== +*/ + +html, body { + height: 100%; +} + +/* +Typography +================================== +*/ + +body { + font-family: "Avenir Next", Arial, sans-serif; + font-weight: 400; + font-style: normal; + line-height: 1.466666667; +} + +h1, +h3, +h4, +h5, +strong { + font-family: "Avenir Next Demi", "Avenir Next", Arial, sans-serif; + font-weight: 600; +} + +.site-title { + font-size: 1.625em; + font-family: "Avenir Next", Arial, sans-serif; + font-weight: bolder; + color: #0000000; + margin: 0; + line-height: 1.2941176470588236; + display: inline-block; + width: 70%; +} + +h2 { + font-weight: 600; + font-style: normal; + font-size: 1.375em; + margin: 1.4em 0 0 0; +} + +h4 { + font-size: 1em; +} + +.page-title { + margin-top: .727272727em; /* 16/22 */ +} + +.float-right { + display: inline-block; + width: 25%; + margin: 0; + text-align: right; +} + +.language-switcher { +} +/* +Lists +-------------------------------- +*/ + +.main-content ul { + padding-left: 1.1em; +} + +.main-content li { + margin-bottom: 1em; +} + +li h3, +li h4 { + margin: 0; +} + +li p { + margin-top: 0; +} + +/* +Links +================================== +*/ + +a { + -webkit-transition: .2s; + -moz-transition: .2s; + transition: .2s; +} + +a, +a:link, +a:visited { + color: #4b4085ff; + border-bottom: 1px dotted #4b4085ff; + text-decoration: none; +} + +a:hover { + border-bottom: 1px solid #7eb8dd; + color: #4b4085ff; + text-decoration: none; +} + +a:active { + border-bottom: 1px solid #002d72; + color: #4b4085ff; + text-decoration: none; +} + +a:focus { + border-bottom: 1px solid #0072ce; + color: #4b4085ff; + outline: thin dotted; + text-decoration: none; +} + +a.title-link { + color: #1E293B; + border-bottom: none; +} + +a.title-link:hover, +a.title-link:active, +a.title-link:focus { + color: #4b4085ff; + border-bottom: none; +} + +a.skip-link { + color: #0072ce; + border-bottom: none; + padding: .25em; +} + +a.skip-link:hover, +a.skip-link:active, +a.skip-link:focus { + background-color: #0072ce; + color: #fff; + border-bottom: none; +} + + +/* +Navigation +================================== +*/ + +.sidebar-nav a { + display: block; + padding: 10px; + -webkit-transition: unset; + transition: unset; +} +.sidebar-nav a, +.sidebar-nav a:link, +.sidebar-nav a:visited { + border-bottom: none; + color: #1E293B; +} +.sidebar-nav li:hover, +.sidebar-nav a:focus, +.sidebar-nav li:active, +.sidebar-nav .sidebar-nav-active { + color: #1E293B; + border-left: 4px solid {{ site.brand_color }}; + background-color: transparent; + border-bottom: 1px solid #babbbd; +} +.sidebar-nav li:hover, +.sidebar-nav li:active, +.sidebar-nav .sidebar-nav-active { + padding-left: 0; +} +.sidebar-nav a:focus { + padding-left: 6px; +} +.sidebar-nav li:hover a:focus, +.sidebar-nav li:active a:focus { + border-left: none; + padding-left: 10px; +} +.sidebar-nav ul { + margin: 0; + padding: 0; + /*border-top: 1px solid @gray-50;*/ +} +.sidebar-nav li { + list-style: none; + border-bottom: 1px solid #babbbd; + font-size: 1.125em; + padding-left: 4px; +} +.sidebar-nav li:last-child { + border-bottom: none; +} + + +/* +Layout +================================== +*/ + +.logo { + display: block; +} + +.content { + padding-top: 2em; + padding-bottom: 2em; +} + +/* offset the fixed position header for jump links */ +section:before { + display: block; + content: ""; + height: 60px; + margin: -60px 0 0; +} + +.wrap { + max-width: 1200px; + margin: 0 auto; + padding-left: 20px; + padding-right: 20px; +} + +header { + width: 100%; + border-bottom: 4px solid {{ site.brand_color }}; + background-color: #fff; + padding: 2em 0; +} + + +/* +Footer +================================== +*/ + +/* for sticky footer */ +.container { + display: table; + height: 100%; + width: 100%; +} + +footer { + display: table-row; /* for sticky footer */ + height: 1px; /* for sticky footer */ + border-top: 2px solid #babbbd; + background: #f1f2f2; + width: 100%; + font-size: 0.875em; +} + +footer .wrap { + padding-top: 2em; + padding-bottom: 2em; +} + + +/* +Helpers +================================== +*/ + +/* Hide from both screenreaders and browsers: h5bp.com/u */ +.hidden { + display: none !important; + visibility: hidden; +} + +/* Hide only visually, but have it available for screenreaders: h5bp.com/v */ +.visuallyhidden { + border: 0; + clip: rect(0 0 0 0); + height: 1px; + margin: -1px; + overflow: hidden; + padding: 0; + position: absolute; + width: 1px; +} + +/* Extends the .visuallyhidden class to allow the element to be focusable + * when navigated to via the keyboard: h5bp.com/p */ +.visuallyhidden.focusable:active, +.visuallyhidden.focusable:focus { + clip: auto; + height: auto; + margin: 0; + overflow: visible; + position: static; + width: auto; +} + +/* Hide visually and from screenreaders, but maintain layout */ +.invisible { + visibility: hidden; +} + + +/* +Style +================================== +*/ + +.intro { + color: #1E293B; +} + +li h4 { + margin: 0; +} + +.license { + font-family: "Avenir Next Demi", Arial, sans-serif; + font-weight: normal; + font-style: normal; +} + +pre { + max-width: 100%; + font-size: 0.875em; + overflow-y: scroll; + background-color: #f1f2f2; + padding: 10px; +} + +td, th { + border: 1px solid grey +} +/* +Post list +---------------------------------- +*/ + +ul.posts { + padding: 0; +} + +.posts li { + list-style: none; +} + +.post-date { + color: #64748B; +} + +/* +Repo list +---------------------------------- +*/ + +ul.repo-list { + margin: .5em 0 1em 0; + padding: 0; +} + +.repo-list li { + list-style: none; +} + +.repo-list p { + margin: 0; + font-size: 0.875em; +} + +.repo-list h4 { + text-transform: none; +} + +/* +Helper Classes +================================== +*/ + +/* +Clearfix list +---------------------------------- +*/ + +.group:before, +.group:after { + content: " "; + display: table; +} + +.group:after { + clear: both; +} + +.group { + *zoom: 1; +} + +/* +Desktop Styles +================================== +*/ + +@media screen and (min-width: 45em) and (min-height: 32.5em) { + + /* + Typography + ============================== + */ + + /* + Layout + ============================== + */ + + .logo { + max-width: 30%; + padding-right: 20px; + float: right; + } + + aside { + width: 30%; + float: left; + } + + .main-content { + width: 67%; + float: right; + margin-bottom: 120px; + } + + /* + Navigation + ============================== + */ + + + /* + Style + ============================== + */ + + /* + Repo list + ------------------------------ + */ + + .repo-list li { + list-style: none; + display: block; + float: left; + height: 4.0625em; + max-height: 4.0625em; + background-color: #E7E7E6; + border-left: 1px solid #64748B; + width: 30%; + } + + .repo-list a:link, + .repo-list a:visited { + display: block; + max-height: 4.0625em; + background-color: #E7E7E6; + border-bottom: none; + padding: .625em 1em 1em 1em; + } + + .repo-list a:hover { + color: #64748B; + background-color: #4b4085ff; + } + + .repo-list li:first-child { + text-align: center; + border-left: none; + line-height: 60px; + padding: .625em 1em; + width: 10%; + } + +} + +@media screen and (max-width: 54.375em) and (min-height: 32.5em) { + + /* keep the repo list containers the same height, but account for the need for more height */ + + .repo-list li { + height: 6em; + max-height: 6em; + } + + .repo-list a:link, + .repo-list a:visited { + max-height: 6em; + } +} + +/* +Mobile Styles +================================== +*/ + +@media screen and (max-width: 40.5em) { + + .main-content { + margin-top: 1.5em; + } + +} diff --git a/docs/assets/css/style.scss b/docs/assets/css/style.scss new file mode 100644 index 00000000..5742f930 --- /dev/null +++ b/docs/assets/css/style.scss @@ -0,0 +1,4 @@ +--- +--- + +@import "{{ site.theme }}"; diff --git a/docs/assets/css/syntax.css b/docs/assets/css/syntax.css new file mode 100644 index 00000000..9779c73c --- /dev/null +++ b/docs/assets/css/syntax.css @@ -0,0 +1,60 @@ +.highlight { background: #ffffff; } +.highlight .c { color: #64748B; font-style: italic } /* Comment */ +.highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ +.highlight .k { font-weight: bold } /* Keyword */ +.highlight .o { font-weight: bold } /* Operator */ +.highlight .cm { color: #64748B; font-style: italic } /* Comment.Multiline */ +.highlight .cp { color: #64748B; font-weight: bold } /* Comment.Preproc */ +.highlight .c1 { color: #64748B; font-style: italic } /* Comment.Single */ +.highlight .cs { color: #64748B; font-weight: bold; font-style: italic } /* Comment.Special */ +.highlight .gd { color: ##1E293B; background-color: #ffdddd } /* Generic.Deleted */ +.highlight .gd .x { color: ##1E293B; background-color: #ffaaaa } /* Generic.Deleted.Specific */ +.highlight .ge { font-style: italic } /* Generic.Emph */ +.highlight .gr { color: #aa0000 } /* Generic.Error */ +.highlight .gh { color: #999999 } /* Generic.Heading */ +.highlight .gi { color: ##1E293B; background-color: #ddffdd } /* Generic.Inserted */ +.highlight .gi .x { color: ##1E293B; background-color: #aaffaa } /* Generic.Inserted.Specific */ +.highlight .go { color: #888888 } /* Generic.Output */ +.highlight .gp { color: #555555 } /* Generic.Prompt */ +.highlight .gs { font-weight: bold } /* Generic.Strong */ +.highlight .gu { color: #aaaaaa } /* Generic.Subheading */ +.highlight .gt { color: #aa0000 } /* Generic.Traceback */ +.highlight .kc { font-weight: bold } /* Keyword.Constant */ +.highlight .kd { font-weight: bold } /* Keyword.Declaration */ +.highlight .kp { font-weight: bold } /* Keyword.Pseudo */ +.highlight .kr { font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #445588; font-weight: bold } /* Keyword.Type */ +.highlight .m { color: #4b408ff } /* Literal.Number */ +.highlight .s { color: #d14 } /* Literal.String */ +.highlight .na { color: #008080 } /* Name.Attribute */ +.highlight .nb { color: #0086B3 } /* Name.Builtin */ +.highlight .nc { color: #445588; font-weight: bold } /* Name.Class */ +.highlight .no { color: #008080 } /* Name.Constant */ +.highlight .ni { color: #800080 } /* Name.Entity */ +.highlight .ne { color: #990000; font-weight: bold } /* Name.Exception */ +.highlight .nf { color: #990000; font-weight: bold } /* Name.Function */ +.highlight .nn { color: #555555 } /* Name.Namespace */ +.highlight .nt { color: #4b408ff } /* Name.Tag */ +.highlight .nv { color: #4b408ff } /* Name.Variable */ +.highlight .ow { font-weight: bold } /* Operator.Word */ +.highlight .w { color: #bbbbbb } /* Text.Whitespace */ +.highlight .mf { color: #4b408ff } /* Literal.Number.Float */ +.highlight .mh { color: #4b408ff } /* Literal.Number.Hex */ +.highlight .mi { color: #4b408ff } /* Literal.Number.Integer */ +.highlight .mo { color: #4b408ff } /* Literal.Number.Oct */ +.highlight .sb { color: #d14 } /* Literal.String.Backtick */ +.highlight .sc { color: #d14 } /* Literal.String.Char */ +.highlight .sd { color: #d14 } /* Literal.String.Doc */ +.highlight .s2 { color: #d14 } /* Literal.String.Double */ +.highlight .se { color: #d14 } /* Literal.String.Escape */ +.highlight .sh { color: #d14 } /* Literal.String.Heredoc */ +.highlight .si { color: #d14 } /* Literal.String.Interpol */ +.highlight .sx { color: #d14 } /* Literal.String.Other */ +.highlight .sr { color: #009926 } /* Literal.String.Regex */ +.highlight .s1 { color: #d14 } /* Literal.String.Single */ +.highlight .ss { color: #990073 } /* Literal.String.Symbol */ +.highlight .bp { color: #64748B } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #4b408ff } /* Name.Variable.Class */ +.highlight .vg { color: #4b408ff } /* Name.Variable.Global */ +.highlight .vi { color: #4b408ff } /* Name.Variable.Instance */ +.highlight .il { color: #4b408ff } /* Literal.Number.Integer.Long */ diff --git a/docs/assets/img/octocat.png b/docs/assets/img/octocat.png new file mode 100644 index 00000000..5c0b9cd0 Binary files /dev/null and b/docs/assets/img/octocat.png differ diff --git a/docs/assets/js/respond.min.js b/docs/assets/js/respond.min.js new file mode 100644 index 00000000..e3dc2c0d --- /dev/null +++ b/docs/assets/js/respond.min.js @@ -0,0 +1,6 @@ +/*! matchMedia() polyfill - Test a CSS media type/query in JS. Authors & copyright (c) 2012: Scott Jehl, Paul Irish, Nicholas Zakas. Dual MIT/BSD license */ +/*! NOTE: If you're already including a window.matchMedia polyfill via Modernizr or otherwise, you don't need this part */ +window.matchMedia=window.matchMedia||function(a){"use strict";var c,d=a.documentElement,e=d.firstElementChild||d.firstChild,f=a.createElement("body"),g=a.createElement("div");return g.id="mq-test-1",g.style.cssText="position:absolute;top:-100em",f.style.background="none",f.appendChild(g),function(a){return g.innerHTML='­',d.insertBefore(f,e),c=42===g.offsetWidth,d.removeChild(f),{matches:c,media:a}}}(document); + +/*! Respond.js v1.3.0: min/max-width media query polyfill. (c) Scott Jehl. MIT/GPLv2 Lic. j.mp/respondjs */ +(function(a){"use strict";function x(){u(!0)}var b={};if(a.respond=b,b.update=function(){},b.mediaQueriesSupported=a.matchMedia&&a.matchMedia("only all").matches,!b.mediaQueriesSupported){var q,r,t,c=a.document,d=c.documentElement,e=[],f=[],g=[],h={},i=30,j=c.getElementsByTagName("head")[0]||d,k=c.getElementsByTagName("base")[0],l=j.getElementsByTagName("link"),m=[],n=function(){for(var b=0;l.length>b;b++){var c=l[b],d=c.href,e=c.media,f=c.rel&&"stylesheet"===c.rel.toLowerCase();d&&f&&!h[d]&&(c.styleSheet&&c.styleSheet.rawCssText?(p(c.styleSheet.rawCssText,d,e),h[d]=!0):(!/^([a-zA-Z:]*\/\/)/.test(d)&&!k||d.replace(RegExp.$1,"").split("/")[0]===a.location.host)&&m.push({href:d,media:e}))}o()},o=function(){if(m.length){var b=m.shift();v(b.href,function(c){p(c,b.href,b.media),h[b.href]=!0,a.setTimeout(function(){o()},0)})}},p=function(a,b,c){var d=a.match(/@media[^\{]+\{([^\{\}]*\{[^\}\{]*\})+/gi),g=d&&d.length||0;b=b.substring(0,b.lastIndexOf("/"));var h=function(a){return a.replace(/(url\()['"]?([^\/\)'"][^:\)'"]+)['"]?(\))/g,"$1"+b+"$2$3")},i=!g&&c;b.length&&(b+="/"),i&&(g=1);for(var j=0;g>j;j++){var k,l,m,n;i?(k=c,f.push(h(a))):(k=d[j].match(/@media *([^\{]+)\{([\S\s]+?)$/)&&RegExp.$1,f.push(RegExp.$2&&h(RegExp.$2))),m=k.split(","),n=m.length;for(var o=0;n>o;o++)l=m[o],e.push({media:l.split("(")[0].match(/(only\s+)?([a-zA-Z]+)\s?/)&&RegExp.$2||"all",rules:f.length-1,hasquery:l.indexOf("(")>-1,minw:l.match(/\(\s*min\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||""),maxw:l.match(/\(\s*max\-width\s*:\s*(\s*[0-9\.]+)(px|em)\s*\)/)&&parseFloat(RegExp.$1)+(RegExp.$2||"")})}u()},s=function(){var a,b=c.createElement("div"),e=c.body,f=!1;return b.style.cssText="position:absolute;font-size:1em;width:1em",e||(e=f=c.createElement("body"),e.style.background="none"),e.appendChild(b),d.insertBefore(e,d.firstChild),a=b.offsetWidth,f?d.removeChild(e):e.removeChild(b),a=t=parseFloat(a)},u=function(b){var h="clientWidth",k=d[h],m="CSS1Compat"===c.compatMode&&k||c.body[h]||k,n={},o=l[l.length-1],p=(new Date).getTime();if(b&&q&&i>p-q)return a.clearTimeout(r),r=a.setTimeout(u,i),void 0;q=p;for(var v in e)if(e.hasOwnProperty(v)){var w=e[v],x=w.minw,y=w.maxw,z=null===x,A=null===y,B="em";x&&(x=parseFloat(x)*(x.indexOf(B)>-1?t||s():1)),y&&(y=parseFloat(y)*(y.indexOf(B)>-1?t||s():1)),w.hasquery&&(z&&A||!(z||m>=x)||!(A||y>=m))||(n[w.media]||(n[w.media]=[]),n[w.media].push(f[w.rules]))}for(var C in g)g.hasOwnProperty(C)&&g[C]&&g[C].parentNode===j&&j.removeChild(g[C]);for(var D in n)if(n.hasOwnProperty(D)){var E=c.createElement("style"),F=n[D].join("\n");E.type="text/css",E.media=D,j.insertBefore(E,o.nextSibling),E.styleSheet?E.styleSheet.cssText=F:E.appendChild(c.createTextNode(F)),g.push(E)}},v=function(a,b){var c=w();c&&(c.open("GET",a,!0),c.onreadystatechange=function(){4!==c.readyState||200!==c.status&&304!==c.status||b(c.responseText)},4!==c.readyState&&c.send(null))},w=function(){var b=!1;try{b=new a.XMLHttpRequest}catch(c){b=new a.ActiveXObject("Microsoft.XMLHTTP")}return function(){return b}}();n(),b.update=n,a.addEventListener?a.addEventListener("resize",x,!1):a.attachEvent&&a.attachEvent("onresize",x)}})(this); diff --git a/docs/cache-polyfill.js b/docs/cache-polyfill.js new file mode 100644 index 00000000..7b4e6bf7 --- /dev/null +++ b/docs/cache-polyfill.js @@ -0,0 +1,72 @@ +/** + * Copyright 2015 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +if (!Cache.prototype.addAll) { + Cache.prototype.addAll = function addAll(requests) { + var cache = this; + + // Since DOMExceptions are not constructable: + function NetworkError(message) { + this.name = 'NetworkError'; + this.code = 19; + this.message = message; + } + NetworkError.prototype = Object.create(Error.prototype); + + return Promise.resolve().then(function() { + if (arguments.length < 1) throw new TypeError(); + + // Simulate sequence<(Request or USVString)> binding: + var sequence = []; + + requests = requests.map(function(request) { + if (request instanceof Request) { + return request; + } + else { + return String(request); // may throw TypeError + } + }); + + return Promise.all( + requests.map(function(request) { + if (typeof request === 'string') { + request = new Request(request); + } + + var scheme = new URL(request.url).protocol; + + if (scheme !== 'http:' && scheme !== 'https:') { + throw new NetworkError("Invalid scheme"); + } + + return fetch(request.clone()); + }) + ); + }).then(function(responses) { + // TODO: check that requests don't overwrite one another + // (don't think this is possible to polyfill due to opaque responses) + return Promise.all( + responses.map(function(response, i) { + return cache.put(requests[i], response); + }) + ); + }).then(function() { + return undefined; + }); + }; +} diff --git a/docs/favicon.ico b/docs/favicon.ico new file mode 100644 index 00000000..7848a38a Binary files /dev/null and b/docs/favicon.ico differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..7bfc0bcd --- /dev/null +++ b/docs/index.html @@ -0,0 +1,7 @@ +--- +layout: default +lang: en +trans_url: /accueil +--- +{% capture index %}{% include index.md %}{% endcapture %} +{{ index | markdownify }} diff --git a/docs/sw.js b/docs/sw.js new file mode 100644 index 00000000..b44ac23c --- /dev/null +++ b/docs/sw.js @@ -0,0 +1,38 @@ +--- +layout: null +--- + +importScripts( '{{ site.baseurl }}/cache-polyfill.js' ); + +var filesToCache = [ + // root + '{{ site.baseurl }}/', + '{{ site.baseurl }}/index.html', + // css + '{{ site.baseurl }}/assets/css/main.css', + '{{ site.baseurl }}/assets/css/normalize.css', + '{{ site.baseurl }}/assets/css/syntax.css', + // images + '{{ site.baseurl }}/assets/img/octocat.png', + // pages + {% for page in site.documents %}'{{ site.baseurl }}{{ page.url }}',{% endfor %} + // posts + {% for post in site.posts %}'{{ site.baseurl }}{{ post.url }}',{% endfor %} +]; + +self.addEventListener( 'install', function( e ) { + e.waitUntil( + caches.open( '{{ site.cache_name }}' ) + .then( function( cache ) { + return cache.addAll( filesToCache ); + }) + ); +}); + +self.addEventListener( 'fetch', function( event ) { + event.respondWith( + caches.match( event.request ).then( function( response ) { + return response || fetch( event.request ); + }) + ); +});