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