mirror of https://github.com/01-edu/public.git
lee
4 years ago
5 changed files with 503 additions and 0 deletions
@ -0,0 +1,126 @@
|
||||
## GraphQL |
||||
|
||||
### Objectives |
||||
|
||||
The objective of this project is to learn graphql query language by creating your own profile. It will be provided,\ |
||||
by the platform, a graphql endpoint that is connected to the database. So you can query this endpoint to obtain the information you desire.\ |
||||
**Note** that for security reasons some tables are private and some are public, you will only be provided with certain content. |
||||
|
||||
Your profile must have at least: |
||||
|
||||
- Basic user identification |
||||
- XP amount |
||||
- level |
||||
- grades |
||||
- skills |
||||
- statistics |
||||
|
||||
### Instructions |
||||
|
||||
> Here is a small introduction and advantages of graphQL, if you want to read more about graphQL you can visit there [site](https://graphql.org/) |
||||
|
||||
GraphQL is a query language for any API endpoint and runtime. The syntax lets you specify what data you want to receive from that API.\ |
||||
You must have in mind that this language is used for API endpoints and not database. Unlike SQL,\ |
||||
graphql query does not go to your database directly but to your graphql API endpoint which can connect to a database or any other data source. |
||||
|
||||
You are already familiar to REST, since REST is a robust methodology of creating APIs and not elastic/scalable.\ |
||||
It can be at the same time painful, because it requires individual creation of each API, example: `v1/user/item/{id}`, `v1/post/item/{id}` and so on. |
||||
|
||||
The main feature of graphql compared to REST is that it lets you ask for specific information. And even better then that is the nesting feature. |
||||
|
||||
Lets take for instance the social network project, if the server had a Graphql API that was connected to the database we could query this API using graphql language: |
||||
|
||||
```js |
||||
query { |
||||
user { |
||||
name |
||||
} |
||||
} |
||||
``` |
||||
|
||||
This simple query will return an array with the name of the users. Imagine if you wanted the `date_of_birth`,\ |
||||
you could just add this attribute to the query, example: |
||||
|
||||
```js |
||||
query { |
||||
user { |
||||
name |
||||
dateOfBirth |
||||
} |
||||
} |
||||
``` |
||||
|
||||
Example for the nesting fetching: |
||||
|
||||
```js |
||||
query { |
||||
user(id: "13") { |
||||
name |
||||
dateOfBirth |
||||
followers { |
||||
id |
||||
name |
||||
} |
||||
} |
||||
} |
||||
``` |
||||
|
||||
For this example we ask for the users followers and request their names and ids.\ |
||||
The examples above are simple examples, **note** that this query is require the introduction of variables(arguments)\ |
||||
so it will return just one user, the user that as the id equal to `13`. |
||||
|
||||
In graphql the usage of arguments can be specified in the schema of the API endpoint. Here is the *docs* for the graphql endpoint you are going to use, [Schema](https://01.alem.school/public/subjects/grapqhl) |
||||
|
||||
<!-- EXPLAIN WHAT IS GOING TO HAPPEN --> |
||||
|
||||
|
||||
|
||||
--- |
||||
|
||||
### Usage |
||||
|
||||
> Here are the list of tables that you are available to query: |
||||
|
||||
- **User table**: |
||||
|
||||
| id | githubLogin | |
||||
| --- | ----------: | |
||||
| 1 | person1 | |
||||
| 2 | person2 | |
||||
| 3 | person3 | |
||||
|
||||
- **Transactions table**: |
||||
|
||||
| id | type | amount | userId | createdAt | |
||||
| --- | :--: | -----: | -----: | -------------------------------: | |
||||
| 1 | xp | 234 | 1 | 2019-03-14T12:02:23.168726+00:00 | |
||||
| 2 | xp | 1700 | 2 | 2019-03-14T12:02:23.168726+00:00 | |
||||
| 3 | xp | 175 | 3 | 2019-03-14T12:02:23.168726+00:00 | |
||||
|
||||
- **Progress table**: |
||||
|
||||
| id | userId | attrs | bestResultId | objectId | |
||||
| --- | :----: | -----------------------: | -----------: | -------: | |
||||
| 1 | 1 | `{"name": "memory", ...` | s | 198 | |
||||
| 2 | 2 | `{"name": "memory", ...` | s | 198 | |
||||
| 3 | 3 | `{"name": "memory", ...` | s | 198 | |
||||
|
||||
- **Results table**: |
||||
|
||||
| id | grade | progressId | |
||||
| --- | ----: | ---------: | |
||||
| 1 | 0 | 58 | |
||||
| 2 | 0 | 58 | |
||||
| 3 | 1 | 58 | |
||||
|
||||
- **Object table**: |
||||
|
||||
| id | grade | progressId | |
||||
| --- | ----: | ---------: | |
||||
| 1 | 0 | 58 | |
||||
| 2 | 0 | 58 | |
||||
| 3 | 1 | 58 | |
||||
|
||||
This project will help you learn about: |
||||
|
||||
- [Graphql](https://graphql.org/) language |
@ -0,0 +1,119 @@
|
||||
<!DOCTYPE html> |
||||
<html> |
||||
<head> |
||||
<title>Graphql Doc</title> |
||||
<meta charset="utf-8"> |
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
||||
<style> |
||||
|
||||
:root { |
||||
--color: hsl(0, 0%, 65%); |
||||
} |
||||
|
||||
* { |
||||
box-sizing: border-box; |
||||
} |
||||
|
||||
body { |
||||
background: hsl(0, 0%, 16%); |
||||
font-family: sans-serif; |
||||
letter-spacing: 1.5px; |
||||
display: flex; |
||||
flex-wrap: wrap; |
||||
justify-content: center; |
||||
align-content: center; |
||||
width: inherit; |
||||
height: inherit; |
||||
padding: 5rem; |
||||
font-size: 12px; |
||||
} |
||||
|
||||
input { |
||||
border: none; |
||||
position: absolute; |
||||
margin: 6px 24px 8px 15px; |
||||
background: rgba(255, 255, 255, 0); |
||||
border-bottom: 1px solid var(--color); |
||||
outline: none; |
||||
} |
||||
|
||||
.docExplorerWrap { |
||||
position: inherit; |
||||
width: 80%; |
||||
height: 100%; |
||||
top: 50%; |
||||
left: 50%; |
||||
background-color: hsl(0, 1%, 35%); |
||||
} |
||||
|
||||
.doc-title, .doc-explore-content { |
||||
padding: 10px; |
||||
margin: 10px; |
||||
align-content: center; |
||||
border-bottom: 1px solid var(--color); |
||||
} |
||||
|
||||
.doc-title { |
||||
display: flex; |
||||
} |
||||
|
||||
.back { |
||||
width: 50px; |
||||
height: 50px; |
||||
} |
||||
|
||||
h3, h4 { |
||||
font-weight: 300; |
||||
width: 100%; |
||||
margin: 15px; |
||||
} |
||||
|
||||
h3 { |
||||
font-size: 1.25rem; |
||||
} |
||||
|
||||
h4 { |
||||
font-size: 0.8rem; |
||||
white-space: pre; |
||||
} |
||||
|
||||
b { |
||||
padding: 4px; |
||||
border-bottom: 1px solid var(--color); |
||||
} |
||||
|
||||
a { |
||||
color:lightcoral; |
||||
} |
||||
|
||||
a:hover { |
||||
text-decoration: underline; |
||||
cursor: pointer; |
||||
} |
||||
|
||||
img { |
||||
cursor: pointer; |
||||
width: 30px; |
||||
height: 30px; |
||||
} |
||||
|
||||
</style> |
||||
</head> |
||||
<body> |
||||
<script type="module" src="./lib/schema.js"></script> |
||||
<div class="docExplorerWrap"> |
||||
<div class="doc-title"><h3 class="title">Schema Documentation</h3></div> |
||||
<div class="doc-explore-content"> |
||||
<label class="search-box"> |
||||
<img src="https://cdn1.iconfinder.com/data/icons/thin-ui-1/100/Noun_Project_100Icon_1px_grid_thin_ic_cookie-512.png"> |
||||
<input class="search" type="text" placeholder="Search Schema..."> |
||||
</label> |
||||
<div class="doc-type-description"> <h4 class="comment">A Graphql schema provides a root type for each kind of operation</h4><!-- description of... --></div> |
||||
<br> |
||||
<div class="doc-category"><h4><b class="doc-type">ROOT TYPES</b></h4></div> |
||||
<br> |
||||
<div class="display-doc"></h4></div> |
||||
</div> |
||||
</div> |
||||
</body> |
||||
</html> |
@ -0,0 +1,157 @@
|
||||
const backImg = |
||||
'https://cdn1.iconfinder.com/data/icons/thin-ui-1/100/Noun_Project_100Icon_1px_grid_thin_ic_arrow_left_simple-512.png' |
||||
const body = document.body |
||||
const main = document.querySelector('.doc-explore-content') |
||||
const docDisplay = document.querySelector('.display-doc') |
||||
const exist = () => document.querySelector('.back') |
||||
const removeComments = (obj) => |
||||
Object.fromEntries(obj.filter((v) => v[0] !== 'comment')) |
||||
|
||||
const getData = async (pathToFile) => { |
||||
const data = await fetch(pathToFile) |
||||
.then((data) => data.json()) |
||||
.catch(console.log) |
||||
return data |
||||
} |
||||
|
||||
const generateLinks = (k, v) => { |
||||
const h4 = document.createElement('h4') |
||||
const a = document.createElement('a') |
||||
a.innerText = v |
||||
h4.innerHTML = k |
||||
h4.appendChild(a) |
||||
docDisplay.appendChild(h4) |
||||
return a |
||||
} |
||||
|
||||
const generateAllQueries = (data) => { |
||||
const docTitle = document.querySelector('.doc-title') |
||||
docDisplay.innerHTML = '' |
||||
const back = backArrow() |
||||
docTitle.appendChild(back) |
||||
for (let obj of data.types) { |
||||
for (const [k, _] of Object.entries(removeComments(Object.entries(obj)))) { |
||||
generateContent(k, obj) |
||||
} |
||||
} |
||||
} |
||||
|
||||
const loadRootTypes = async () => { |
||||
const data = await getData('./lib/schema.json') |
||||
for (let v of data.root_types) { |
||||
const h4 = document.createElement('h4') |
||||
const a = document.createElement('a') |
||||
a.addEventListener('click', (event) => changeContent(event, data)) |
||||
|
||||
for (const [k, value] of Object.entries(v)) { |
||||
a.innerText = value |
||||
h4.innerHTML = `${k}: ` |
||||
h4.appendChild(a) |
||||
docDisplay.appendChild(h4) |
||||
} |
||||
} |
||||
const a = generateLinks('all: ', 'all_available_queries') |
||||
a.addEventListener('click', () => generateAllQueries(data)) |
||||
} |
||||
|
||||
const generateContent = (t, data) => { |
||||
docDisplay.innerHTML += `type ${t.toUpperCase()} {` |
||||
for (const [k, v] of Object.entries(data[t])) { |
||||
generateLinks(` ${k}: `, v) |
||||
} |
||||
docDisplay.innerHTML += `}<br><br>` |
||||
for (let v of document.getElementsByTagName('a')) { |
||||
v.addEventListener('click', (e) => changeContent(e, data)) |
||||
} |
||||
} |
||||
const backArrow = () => { |
||||
const title = document.querySelector('.title') |
||||
const typeDescription = document.querySelector('.comment') |
||||
const contType = document.querySelector('.doc-type') |
||||
const origType = contType.innerText |
||||
const origTitle = title.innerText |
||||
const origTypeDescription = typeDescription.innerText |
||||
const back = document.createElement('img') |
||||
back.className = 'back' |
||||
back.src = backImg |
||||
|
||||
back.addEventListener('click', () => { |
||||
back.remove() |
||||
docDisplay.innerHTML = '' |
||||
title.innerText = origTitle |
||||
contType.innerText = origType |
||||
typeDescription.innerText = origTypeDescription |
||||
loadRootTypes() |
||||
}) |
||||
return back |
||||
} |
||||
|
||||
const allArgs = (data) => { |
||||
for (const [k, v] of Object.entries(data.arguments)) { |
||||
docDisplay.innerHTML += `<h4>${k}: <a>(${v.description})</a> // ${v.comment}</h4>` |
||||
} |
||||
} |
||||
|
||||
const changeContent = ({ target }, data) => { |
||||
const title = document.querySelector('.title') |
||||
const typeDescription = document.querySelector('.comment') |
||||
const contType = document.querySelector('.doc-type') |
||||
const docTitle = document.querySelector('.doc-title') |
||||
|
||||
const t = target.innerText |
||||
const back = backArrow() |
||||
|
||||
title.innerText = t.toUpperCase() |
||||
contType.innerText = `${t.toUpperCase()} fields` |
||||
docDisplay.innerHTML = '' |
||||
if (/\[\w*\!\]\!/g.test(t)) { |
||||
const b = t.replace(/\[|\]|\!/g, '') |
||||
const obj = data.types.filter((v) => v[b])[0] |
||||
typeDescription.innerText = obj.comment |
||||
generateContent(b, obj) |
||||
} else if (t === 'arguments') { |
||||
allArgs(data) |
||||
!exist() && docTitle.appendChild(back) |
||||
} else { |
||||
generateContent(t, data) |
||||
!exist() && docTitle.appendChild(back) |
||||
} |
||||
} |
||||
|
||||
loadRootTypes() |
||||
|
||||
/* search */ |
||||
const updateResult = (dataToSearch, { target }) => { |
||||
docDisplay.innerHTML = '' |
||||
|
||||
dataToSearch.search.map((type) => |
||||
target.value.split(' ').map((w) => { |
||||
if (type.toLowerCase().indexOf(w.toLowerCase()) !== -1) { |
||||
docDisplay.innerHTML += `<li><a class="query-result">${type}</a></li>` |
||||
document |
||||
.querySelectorAll('.query-result') |
||||
.forEach((ele) => |
||||
ele.addEventListener('click', (e) => changeContent(e, dataToSearch)) |
||||
) |
||||
} |
||||
}) |
||||
) |
||||
} |
||||
|
||||
const initSearch = async () => { |
||||
const data = await getData('./lib/schema.json') |
||||
const input = document.querySelector('.search') |
||||
|
||||
input.addEventListener('focus', () => { |
||||
const bg = document.querySelector('.back') |
||||
if (!bg) { |
||||
const back = backArrow() |
||||
document.querySelector('.doc-title').appendChild(back) |
||||
} |
||||
docDisplay.innerHTML = '' |
||||
}) |
||||
input.oninput = (e) => updateResult(data, e) |
||||
} |
||||
|
||||
initSearch() |
||||
/*--------------*/ |
@ -0,0 +1,101 @@
|
||||
{ |
||||
"search": [ |
||||
"[user!]!", |
||||
"[object!]!", |
||||
"[transaction!]!", |
||||
"[progress!]!", |
||||
"[result!]!", |
||||
"query", |
||||
"arguments" |
||||
], |
||||
"root_types": [{ "query": "query" }, { "arguments": "arguments" }], |
||||
"query": { |
||||
"user": "[user!]!", |
||||
"object": "[object!]!", |
||||
"transaction": "[transaction!]!", |
||||
"progress": "[progress!]!", |
||||
"result": "[result!]!" |
||||
}, |
||||
"types": [ |
||||
{ |
||||
"user": { |
||||
"id": "ID!", |
||||
"attrs": "String!", |
||||
"createdAt": "String!", |
||||
"discordDMChannelId": "String!", |
||||
"discordId": "String!", |
||||
"discordLogin": "String!", |
||||
"githubId": "String!", |
||||
"githubLogin": "String!", |
||||
"profile": "String!", |
||||
"updatedAt": "String!" |
||||
}, |
||||
"comment": "Fetch data from the table: \"user\"" |
||||
}, |
||||
{ |
||||
"object": { |
||||
"id": "ID!", |
||||
"attrs": "String!", |
||||
"authorId": "String!", |
||||
"childrenAttrs": "String!", |
||||
"createdAt": "String!", |
||||
"externalRelationUrl": "String!", |
||||
"name": "String!", |
||||
"type": "String!", |
||||
"status": "String!", |
||||
"updatedAt": "String!" |
||||
}, |
||||
"comment": "Fetch data from the table: \"object\"" |
||||
}, |
||||
{ |
||||
"transaction": { |
||||
"id": "ID!", |
||||
"attrs": "String!", |
||||
"amount": "Int!", |
||||
"createdAt": "String!", |
||||
"type": "String!", |
||||
"userId": "Int!" |
||||
}, |
||||
"comment": "Fetch data from the table: \"transaction\"" |
||||
}, |
||||
{ |
||||
"progress": { |
||||
"id": "ID!", |
||||
"attrs": "String!", |
||||
"bestResultId": "Int!", |
||||
"createdAt": "String!", |
||||
"eventId": "Int!", |
||||
"groupId": "Int!", |
||||
"objectId": "Int!", |
||||
"updatedAt": "String!", |
||||
"userId": "Int!" |
||||
}, |
||||
"comment": "Fetch data from the table: \"progress\"" |
||||
}, |
||||
{ |
||||
"result": { |
||||
"id": "ID!", |
||||
"progressId": "Int!", |
||||
"attrs": "String!", |
||||
"createdAt": "String!", |
||||
"grade": "Int!", |
||||
"updatedAt": "String!" |
||||
}, |
||||
"comment": "Fetch data from the table: \"result\"" |
||||
} |
||||
], |
||||
"arguments": { |
||||
"where": { |
||||
"description": "bool_exp", |
||||
"comment": "filter the rows returned" |
||||
}, |
||||
"order_by": { |
||||
"description": "[order_by!]", |
||||
"comment": "sort the rows by one or more columns" |
||||
}, |
||||
"limit": { |
||||
"description": "Int", |
||||
"comment": "limit the number of rows returned" |
||||
} |
||||
} |
||||
} |
Loading…
Reference in new issue