GitBook: [master] 86 pages and 110 assets modified

This commit is contained in:
consuldocs
2020-07-14 22:51:03 +00:00
committed by gitbook-bot
parent 06fe16d6b4
commit 5ed5960428
196 changed files with 5304 additions and 122 deletions

View File

@@ -0,0 +1,8 @@
# Technical Features
* [OAuth](oauth.md)
* [GraphQL](graphql.md)
* [Recommendations](recommendations.md)
* [Configure Census Connection](census_configuration.md)
* [Local Census](local_census.md)

View File

@@ -0,0 +1,149 @@
# Configure Census Connection
The objective of this service is to be able to configure the connection with the Town Hall Census through the Administration panel without having to modify the application code.
It should be noted that to properly configure this connection will require a technical profile that knows the WebService of your City Council.
Currently the application was designed to send only the **document number** and **document type**. With this new feature is enabled the possibility of sending if necessary the fields **date of birth** and **postal code**.
## Activate feature
In the section **Configuration > Global Configuration** a new tab **Remote Census Configuration** has been added.
If we have the feature deactivated we will see an informative text that will indicate us how to activate it: ![Feature disabled](../../.gitbook/assets/feature-disabled-en.png)
To activate the feature you must follow the instructions of the previous image: 1. Access through the administration panel of your application to the section **Settings > Features** and activate the module **Configure connection to the remote census \(SOAP\)** as shown below: ![Feature enabled](../../.gitbook/assets/feature-enabled-en.png)
## Configuration
Once the feature is activated, we can access the section **Settings > Global Settings** and click on the tab **Remote Census Configuration**. In this screen you will be able to fill in all the necessary information to be able to configure the connection with the Census of each Town Hall.
The information to be filled in is divided into three sections:
1. **General Information**
* **Endpoint**: Host name where the census service is available \(wsdl\).
![General information - Endpoint](../../.gitbook/assets/general-information-endpoint-en.png)
2. **Request Data**
In this section we will fill in all the necessary fields to be able to make a request to verify a user through the Census of the City council.
![Request Data](../../.gitbook/assets/request-data-en.png)
To help you understand how to fill in each of the fields, we will rely on a supposed WebService that receives a method called `:get_habita_datos` with the following structure:
```text
{
request: {
codigo_institucion: 12, # Static Value
codigo_portal: 5, # Static Value
codigo_usuario: 10, # Static Value
documento: 12345678Z, # Dynamic value related to Document Number
tipo_documento: 1, # Dynamic value related to Document Type
nivel: 3 # Static Value
}
}
```
Required fields for the request:
* **Request method name**: Request method name accepted by the City Census WebService.
Example: ![Request Data - Method name](../../.gitbook/assets/request-data-method-name-en.png)
* **Request Structure**: Structure of the request received by the WebService of the Census of the City Council. The "static" values of this request should be reported. The "dynamic" values related to Document Type, Document Number, Date of Birth and Postal Code should be filled with null value.
Example: ![Request Data - Structure](../../.gitbook/assets/request-data-structure-en.png) ![Request Data - Structure](../../.gitbook/assets/request-data-structure-info-en.png)
* **Path for document type**: Path in the request structure that sends the Document Type.
_NOTE: DO NOT FILL IN if the WebService does not require the Document Type to verify a user._
Example: ![Request Data - Path document type](../../.gitbook/assets/request-data-path-document-type-en.png)
* **Path for document number**: Path in the request structure that sends the Document Number.
_NOTE: DO NOT FILL IN if the WebService does not require the Document Number to verify a user._
Example: ![Request Data - Path document number](../../.gitbook/assets/request-data-path-document-number-en.png)
* **Path for date of birth**: Path in the request structure that sends the Date of Birth.
_NOTE: DO NOT FILL IN if the WebService does not require the Date of Birth to verify a user._
In the case of _Example_ we will fill it in blank, since it is not necessary to send the date of birth to verify a user.
Example: ![Request Data - Path date of birth](../../.gitbook/assets/request-data-path-date-of-birth-en.png)
* **Path for Postal Code**: Path in the request structure that sends the Postal Code.
_NOTE: DO NOT FILL IN if the WebService does not require the Postal Code to verify a user._
En el caso del _Example_ lo dejaríamos en blanco, ya que no se necesita enviar el código postal para verificar a un usuario.
Example: ![Request Data - Path postal code](../../.gitbook/assets/request-data-path-postal-code-en.png)
3. **Response data**
In this section we will configure all the necessary fields to be able to receive the answer of the WebService and to verify a user in the application.
![Response Data](../../.gitbook/assets/response-data-en.png)
As in the previous section we will define an example answer, to help you understand how to fill in each of the fields in this section.
```text
{
get_habita_datos_response: {
get_habita_datos_return: {
datos_habitante: {
item: {
fecha_nacimiento_string: "31-12-1980",
identificador_documento: "12345678Z",
descripcion_sexo: "Varón",
nombre: "José",
apellido1: "García"
}
},
datos_vivienda: {
item: {
codigo_postal: "28013",
codigo_distrito: "01"
}
}
}
}
}
```
Required fields to parse the response:
* **Path for Date of Birth**: In what path of the response is the user's Date of Birth?.
Example: ![Response Data - Path date of birth](../../.gitbook/assets/response-data-path-date-of-birth-en.png)
* **Path for Postal Code**: In what path of the response is the user's Postal Code?.
Example: ![Response Data - Path postal code](../../.gitbook/assets/response-data-path-postal-code-en.png)
* **Path for District**: In what path of the response is the user's District?.
Example: ![Response Data - Path district](../../.gitbook/assets/response-data-path-district-en.png)
* **Path for Gender**: In what path of response is the user's Gender?.
Example: ![Response Data - Path Gender](../../.gitbook/assets/response-data-path-gender-en.png)
* **Path for Name**: In what path of the response is the user's Name?.
Example: ![Response Data - Path Name](../../.gitbook/assets/response-data-path-name-en.png)
* **Path for the Last Name**: In what path of the response is the user's Last Name?.
Example: ![Response Data - Path Last Name](../../.gitbook/assets/response-data-path-last-name-en.png)
* **Condition for detecting a valid response**: What response path has to come informed to be considered a valid response and user verified.
Example: ![Response Data - Path valid response](../../.gitbook/assets/response-data-path-valid-response-en.png)
Once the general data, the necessary fields of the request and "all" fields to validate the response have been filled in correctly, the application will be able to verify any user through the defined WebService.

View File

@@ -0,0 +1,431 @@
# GraphQL
* [Characteristics](graphql.md#characteristics)
* [GraphQL](graphql.md#graphql)
* [Making API requests](graphql.md#making-api-requests)
* [Supported clients](graphql.md#supported-clients)
* [GraphiQL](graphql.md#graphiql)
* [Postman](graphql.md#postman)
* [HTTP libraries](graphql.md#http-libraries)
* [Available information](graphql.md#available-information)
* [Examples of queries](graphql.md#examples-of-queries)
* [Request a single record from a collection](graphql.md#request-a-single-record-from-a-collection)
* [Request a complete collection](graphql.md#request-a-complete-collection)
* [Pagination](graphql.md#pagination)
* [Accessing several resources in a single request](graphql.md#accessing-several-resources-in-a-single-request)
* [Security limitations](graphql.md#security-limitations)
* [Example of too deep query](graphql.md#example-of-too-deep-query)
* [Example of too complex query](graphql.md#example-of-too-complex-query)
* [Code examples](graphql.md#code-examples)
## Characteristics
* Read-only API
* Public access, no authentication needed
* Uses GraphQL technology:
* Maximum page size \(and the default\) is 25 records
* Maximum query depth is set at 8 levels
* A maximum of two collections can be requested within the same query
* Support for GET requests \(query must be inside the _query string_\) and POST requests \(query must be within the _body_, encoded as `application/json` or `application/graphql`\)
## GraphQL
The CONSUL API uses GraphQL [http://graphql.org](http://graphql.org), the [Ruby implementation](http://graphql-ruby.org/), to be specific. If you're not familiar with this kind of APIs, it's recommended to make some research about GraphQL before.
One of the characteristics that differentiates a REST API from a GraphQL one is that with the last one it's possible for the client to build its own _custom queries_, so the server will only return information in which we're interested.
GraphQL queries are written following a standard which ressembles to JSON, for example:
```text
{
proposal(id: 1) {
id,
title,
public_author {
id,
username
}
}
}
```
Responses are formatted in JSON:
```javascript
{
"data": {
"proposal": {
"id": 1,
"title": "Hacer las calles del centro de Madrid peatonales",
"public_author": {
"id": 2,
"username": "electrocronopio"
}
}
}
}
```
## Making API requests
Following [the official recommendations](http://graphql.org/learn/serving-over-http/), the CONSUL API supports the following kind of requests:
* GET requests, with the query inside the _query string_.
* POST requests
* With the query inside the _body_, with `Content-Type: application/json`
* With the query inside the _body_, with `Content-Type: application/graphql`
### Supported clients
Because it's an API that works through HTTP, any tool capable of making this kind of requests is capable of querying the API.
This section presents a few examples about how to make requests using:
* GraphiQL
* Chrome extensions like Postman
* Any HTTP library
#### GraphiQL
[GraphiQL](https://github.com/graphql/graphiql) is a browser interface for making queries against a GraphQL API. It's also an additional source of documentation. It's deployed in the route `/graphiql` and it's the best way to get familiar with GraphQL-based APIs.
![GraphiQL](../../.gitbook/assets/graphiql.png)
It has three main panels:
* The left panel is used to write the query.
* The central panel shows the result of the request.
* The right panel \(occultable\) shows a documentation autogenerated from the models and fields exposed in the API.
#### Postman
Example of `GET` request, with the query as part of the _query string_:
![Postman GET](../../.gitbook/assets/graphql-postman-get.png)
Example of `POST` request, with the query as part of the _body_ and encoded as `application/json`:
![Postman POST](../../.gitbook/assets/graphql-postman-post-headers.png)
The query must be located inside a valid JSON document, as the value of the `"query"` key:
![Postman POST](../../.gitbook/assets/graphql-postman-post-body.png)
#### HTTP libraries
Sure you can use any HTTP library available for most programming languages.
**IMPORTANT**: Due to security protocols from the Madrid City Council servers, it's necessary to include a _User Agent_ header from a web browser so the request is not rejected. For example:
`User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36`
## Available information:
The [config/api.yml](https://github.com/consul/docs/tree/b4ff5e1fdec31baa3e732562392eba3d2805505c/config/api.yml) file contains a complete list of all the models \(and their attributes\) which are currently being exposed in the API.
The models are the following:
| Model | Description |
| :--- | :--- |
| `User` | Users |
| `Debate` | Debates |
| `Proposal` | Proposals |
| `Comment` | Comments on debates, proposals and other comments |
| `Geozone` | Geozones \(districts\) |
| `ProposalNotification` | Notifications related to proposals |
| `Tag` | Tags on debates and proposals |
| `Vote` | Information related to votes |
## Examples of queries
### Request a single record from a collection
```text
{
proposal(id: 2) {
id,
title,
comments_count
}
}
```
Response:
```javascript
{
"data": {
"proposal": {
"id": 2,
"title": "Crear una zona cercada para perros en Las Tablas",
"comments_count": 10
}
}
}
```
### Request a complete collection
```text
{
proposals {
edges {
node {
title
}
}
}
}
```
Response:
```javascript
{
"data": {
"proposals": {
"edges": [
{
"node": {
"title": "ELIMINACION DE ZONA APARCAMIENTO EXCLUSIVO FUNCIONARIOS EN MADRID"
}
},
{
"node": {
"title": "iluminación de zonas deportivas"
}
}
]
}
}
}
```
#### Pagination
The maximum \(and default\) number of records that each page contains is set to 25. For navigating through the different pages it's necessary to request also information relative to the `endCursor`:
```text
{
proposals(first: 25) {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
title
}
}
}
}
```
The response:
```javascript
{
"data": {
"proposals": {
"pageInfo": {
"hasNextPage": true,
"endCursor": "NQ=="
},
"edges": [
# ...
]
}
}
}
```
To retrieve the next page, you have to pass as a parameter the cursor received in the previous request, and so on:
```text
{
proposals(first: 25, after: "NQ==") {
pageInfo {
hasNextPage
endCursor
}
edges {
node {
title
}
}
}
}
```
### Accessing several resources in a single request
This query requests information about several models in a single request: `Proposal`,`User`, `Geozone` and`Comment`:
```text
{
proposal(id: 15262) {
id,
title,
public_author {
username
},
geozone {
name
},
comments(first: 2) {
edges {
node {
body
}
}
}
}
}
```
## Security limitations
Allowing a client to customize queries is a major risk factor. If too complex queries were allowed, it would be possible to perform a DoS attack against the server.
There are three main mechanisms to prevent such abuses:
* Pagination of results
* Limit the maximum depth of the queries
* Limit the amount of information that is possible to request in a query
### Example of too deep query
The maximum depth of queries is currently set at 8. Deeper queries \(such as the following\) will be rejected:
```text
{
user(id: 1) {
public_proposals {
edges {
node {
id,
title,
comments {
edges {
node {
body,
public_author {
username
}
}
}
}
}
}
}
}
}
```
The response will look something like this:
```javascript
{
"errors": [
{
"message": "Query has depth of 9, which exceeds max depth of 8"
}
]
}
```
### Example of too complex query
The main risk factor is when multiple collections of resources are requested in the same query. The maximum number of collections that can appear in the same query is limited to 2. The following query requests information from the `users`,`debates` and `proposals` collections, so it will be rejected:
```text
{
users {
edges {
node {
public_debates {
edges {
node {
title
}
}
},
public_proposals {
edges {
node {
title
}
}
}
}
}
}
}
```
The response will look something like this:
```javascript
{
"errors": [
{
"message": "Query has complexity of 3008, which exceeds max complexity of 2500"
},
{
"message": "Query has complexity of 3008, which exceeds max complexity of 2500"
},
{
"message": "Query has complexity of 3008, which exceeds max complexity of 2500"
}
]
}
```
However, it is possible to request information belonging to more than two models in a single query, as long as you do not try to access the entire collection. For example, the following query that accesses the `User`,`Proposal` and `Geozone` models is valid:
```text
{
user(id: 468501) {
id
public_proposals {
edges {
node {
title
geozone {
name
}
}
}
}
}
}
```
The response:
```javascript
{
"data": {
"user": {
"id": 468501,
"public_proposals": {
"edges": [
{
"node": {
"title": "Empadronamiento necesario para la admisión en GoFit Vallehermoso",
"geozone": {
"name": "Chamberí"
}
}
}
]
}
}
}
}
```
## Code examples
The [doc/api/examples](https://github.com/consul/consul/tree/master/doc/api/examples/ruby) directory contains examples of code to access the API.

View File

@@ -0,0 +1,33 @@
# Local Census
To provide to administrator users a way to manage the local census database through the administration panel **Settings > Manage local census**. Currently the only way to manipulate this table records is through the rails console.
Allow adiministrators users to manage this table in two different ways:
* **Manually**: one by one through a CRUD interface.
* **Automatically**: through an importation process.
## Manually
Provide a way to manage local census records to administrator users through administration interface.
* Local Census Page
![Manage local census](../../.gitbook/assets/manage-local-census-en.png)
* Add new record
![Create local census record](../../.gitbook/assets/add-local-census-record-en.png)
Features:
1. Search by document\_number: As local\_census\_records could contain a lot of records we have added a search feature to allow administrators to find existing records by document\_number.
2. Avoid the introduction of duplicated records: A model validation has been added to the following attributes pair \[:document\_number, :document\_type\]
## Automatically
Allow administrator users to import local census records though CSV file.
* Local Census Page ![Manage local census csv](../../.gitbook/assets/manage-local-census-csv-en%20%281%29.png)
* Import CSV ![Create local census records csv](../../.gitbook/assets/add-local-census-records-csv-en.png)

View File

@@ -0,0 +1,33 @@
# OAuth
You can configure authentication services with external OAuth suppliers, right now Twitter, Facebook and Google are supported.
## 1. Create an App on the platform
For each platform, go to their developers section and follow their guides to create an app.
## 2. Set your CONSUL's url
They'll ask you for your CONSUL's auth URL, and as you can see running `rake routes` at your CONSUL repo locally:
```bash
user_omniauth_authorize GET|POST /users/auth/:provider(.:format) users/omniauth_callbacks#passthru {:provider=>/twitter|facebook|google_oauth2/}
```
So for example the URL for facebook application would be `yourdomain.com/users/auth/facebook/callback`
## 3. Set key & secret values
When you complete the application registration you'll get a _key_ and _secret_ values, those need to be stored at your `config/secrets.yml` file:
```text
twitter_key: ""
twitter_secret: ""
facebook_key: ""
facebook_secret: ""
google_oauth2_key: ""
google_oauth2_secret: ""
```
_NOTE:_ Also in the case of Google, verify that the APIs _Contacts API_ and _Google+ API_ are enabled for the application.

View File

@@ -0,0 +1,26 @@
# Recommendations
Logged in users can see recommended Debates or Proposals listed with the ordering option "recommendations".
The list shows, ordered by votes descending, those elements that: 1. Have tags that interests the user. Being those tags the ones on the proposals that the user follows. 2. The user isn't the author. 3. In the case of proposals: only those that haven't reached the required threshold of votes, hiding as well those that the user is already following.
## How to try it
In our local installation, if we haven't logged in, we can check at [http://localhost:3000/proposals](http://localhost:3000/proposals) that the "recommendations" ordering isn't present:
![Recommendations not logged in](../../.gitbook/assets/recommendations_not_logged_in%20%281%29.jpg)
Once we log in we see the menu, but because we don't aren't following any proposals we get the message "Follow proposals so we can give you recommendations" at [http://localhost:3000/proposals?locale=en&order=recommendations&page=1](http://localhost:3000/proposals?locale=en&order=recommendations&page=1)
![Recommendations no follows](../../.gitbook/assets/recommendations_no_follows%20%281%29.jpg)
After following any proposal with the "Follow citizen proposal" on the side menu:
![Recommendations follow button](../../.gitbook/assets/recommendations_follow_button%20%281%29.jpg)
We can finally see some recommendations:
![Recommendations with follows](../../.gitbook/assets/recommendations_with_follows%20%281%29.jpg)
The feature works the same for debates