GitBook: [master] 86 pages and 110 assets modified
This commit is contained in:
8
docs/english-documentation/features/README.md
Normal file
8
docs/english-documentation/features/README.md
Normal 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)
|
||||
|
||||
149
docs/english-documentation/features/census_configuration.md
Normal file
149
docs/english-documentation/features/census_configuration.md
Normal 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: 
|
||||
|
||||
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: 
|
||||
|
||||
## 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\).
|
||||
|
||||

|
||||
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.
|
||||
|
||||

|
||||
|
||||
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 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:  
|
||||
|
||||
* **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: 
|
||||
|
||||
* **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: 
|
||||
|
||||
* **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: 
|
||||
|
||||
* **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: 
|
||||
|
||||
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.
|
||||
|
||||

|
||||
|
||||
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: 
|
||||
|
||||
* **Path for Postal Code**: In what path of the response is the user's Postal Code?.
|
||||
|
||||
Example: 
|
||||
|
||||
* **Path for District**: In what path of the response is the user's District?.
|
||||
|
||||
Example: 
|
||||
|
||||
* **Path for Gender**: In what path of response is the user's Gender?.
|
||||
|
||||
Example: 
|
||||
|
||||
* **Path for Name**: In what path of the response is the user's Name?.
|
||||
|
||||
Example: 
|
||||
|
||||
* **Path for the Last Name**: In what path of the response is the user's Last Name?.
|
||||
|
||||
Example: 
|
||||
|
||||
* **Condition for detecting a valid response**: What response path has to come informed to be considered a valid response and user verified.
|
||||
|
||||
Example: 
|
||||
|
||||
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.
|
||||
|
||||
431
docs/english-documentation/features/graphql.md
Normal file
431
docs/english-documentation/features/graphql.md
Normal 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.
|
||||
|
||||

|
||||
|
||||
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_:
|
||||
|
||||

|
||||
|
||||
Example of `POST` request, with the query as part of the _body_ and encoded as `application/json`:
|
||||
|
||||

|
||||
|
||||
The query must be located inside a valid JSON document, as the value of the `"query"` key:
|
||||
|
||||

|
||||
|
||||
#### 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.
|
||||
|
||||
33
docs/english-documentation/features/local_census.md
Normal file
33
docs/english-documentation/features/local_census.md
Normal 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
|
||||
|
||||

|
||||
|
||||
* Add new record
|
||||
|
||||

|
||||
|
||||
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 
|
||||
* Import CSV 
|
||||
|
||||
33
docs/english-documentation/features/oauth.md
Normal file
33
docs/english-documentation/features/oauth.md
Normal 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.
|
||||
|
||||
26
docs/english-documentation/features/recommendations.md
Normal file
26
docs/english-documentation/features/recommendations.md
Normal 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:
|
||||
|
||||

|
||||
|
||||
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)
|
||||
|
||||

|
||||
|
||||
After following any proposal with the "Follow citizen proposal" on the side menu:
|
||||
|
||||

|
||||
|
||||
We can finally see some recommendations:
|
||||
|
||||

|
||||
|
||||
The feature works the same for debates
|
||||
|
||||
Reference in New Issue
Block a user