Browse Source

corrections and adding GraphiQL for the students to use

content-update
lee 4 years ago
parent
commit
4e712d15ed
  1. 4
      subjects/good-practices/README.md
  2. 137
      subjects/graphql/README.md
  3. 154
      subjects/graphql/index.html
  4. 163
      subjects/graphql/lib/schema.js
  5. 136
      subjects/graphql/lib/schema.json

4
subjects/good-practices/README.md

@ -85,3 +85,7 @@ It is possible to remove painting by adding a layer:
```
By creating a new layer you can remove painting, but "there is always a tradeoff". If we add to much layers it will increase the **composition** and **update tree**. In conclusion you must promote a new layer only if you now you are going to use it. Performance is the key to a good animation. "Performance is the art of avoiding work".
### UI/UX
- [Best Practices](https://www.uxpin.com/studio/blog/guide-design-consistency-best-practices-ui-ux-designers/)

137
subjects/graphql/README.md

@ -2,112 +2,98 @@
### Objectives
The objective of this project is to learn graphql query language by creating profiles. 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.\
The objective of this project is to learn the query language [graphQL](https://graphql.org/) by creating your profile page. 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:
Your profile must have at least 3 sections of content at your choice, for example:
- Basic user identification
- XP amount
- level
- grades
- audits
- 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 with 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.
Beside those sections it will have a mandatory section for the generation of statistic graphs.
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.
---
### Instructions
### **profile**
You will have to create a profile UI where you can see your own school information. This information/data is present on the graphQL endpoint, where you will have to query it.
You will have to create a profile system where you can see all the info of a student. The information/data is present\
on the graphQL endpoint, where you will have to query it.
For the UI it will be up to you to design it. But have in mind the principles of a [good UI](https://public.01-edu.org/subjects/good-practices/).\
Your UI will have a statistic section where you can generate graphs to see more about your journey and achievements on the school. This graphs must be done using [SVG](https://developer.mozilla.org/en-US/docs/Web/SVG).
The display of the information is up to you to design, but it must include:
Here are some combination that you will be able to use for the graphs:
- **Basic user identification**, for example **githubLogin**
- **XP** amount
- **level**
- **grades**
- **skills**
- **statistics**
- XP earned in a time period (progress over time)
- Levels over time
- XP earned by project
- Audit ratio
- Projects `PASS` and `FAIL` ratio
- Piscine (JS/Go) stats
- `PASS` and `FAIL` ratio
- Attempts for each exercise
Any other information you desire to display is welcome and will be noted
Beside this information, you will have to create a search engine which returns a selection of students profiles, based on the students:
- Name
- XP
- level
- skills
For instance you can search for a student by their `githubLogin` or filter the students by the amount of `XP` or which `level`/`skill` they have.
---
### Usage
> Here is the list of tables that you are allowed to query, for more information about the graphql endpoint you are going to use, check out the _docs_ _https://[[DOMAIN]]/public/subjects/grapqhl_:
> To test your queries you can access the GraphQL IDE on _https://[[DOMAIN]]/public/subjects/graphql/_ or create your own [**GraphiQL Docs**](https://github.com/graphql/graphiql). This will give you a bigger picture of the tables, attributes and all the types of queries that you can do.
Here is the list of tables that you are allowed to query (it will be only provided the columns present on the tables):
- **User table**:
This table will have information about the user
| id | githubLogin |
| --- | ----------: |
| 1 | person1 |
| 2 | person2 |
| 3 | person3 |
| id | login |
| --- | ------: |
| 1 | person1 |
| 2 | person2 |
| 3 | person3 |
- **Transactions table**:
This table will give you access to XP and audits ratio
| id | type | amount | userId | attrs | createdAt |
| --- | :--: | -----: | -----: | -----------------------: | -------------------------------: |
| 1 | xp | 234 | 1 | `{"objectId": 3001, ...` | 2019-03-14T12:02:23.168726+00:00 |
| 2 | xp | 1700 | 2 | `{"objectId": 3001, ...` | 2019-03-14T12:02:23.168726+00:00 |
| 3 | xp | 175 | 3 | `{"objectId": 3001, ...` | 2019-03-14T12:02:23.168726+00:00 |
| id | type | amount | objectId | userId |
| --- | :--: | -----: | -------: | -----: |
| 1 | xp | 234 | 42 | 1 |
| 2 | xp | 1700 | 2 | 2 |
| 3 | xp | 175 | 64 | 3 |
- **Progress_view table**:
| id | userId | attrs | bestResultId | objectId |
| --- | :----: | -----------------------: | -----------: | -------: |
| 1 | 1 | `{}` | 61 | 3001 |
| 2 | 2 | `{"name": "memory", ...` | NULL | 198 |
| 3 | 3 | `{"name": "memory", ...` | 14319 | 177 |
| id | userId | objectId | grade |
| --- | :----: | -------: | ----: |
| 1 | 1 | 3001 | 1 |
| 2 | 2 | 198 | 0 |
| 3 | 3 | 177 | 1 |
- **Results table**:
Both progress and result table will give you the student progression
| id | createdAt | updatedAt | grade | progressId | attrs |
| --- | --------------------: | --------------------: | ----: | ---------: | ---------------------------: |
| 1 | 2019-07-06T13:52:5... | 2019-07-06T13:52:5... | 0 | 58 | `{}` |
| 2 | 2019-07-06T13:52:5... | 2019-07-06T13:52:5... | 0 | 58 | `{"errors": {"steps": ...}}` |
| 3 | 2019-07-06T13:52:5... | 2019-07-06T13:52:5... | 1 | 58 | `{}` |
| id | objectId | userId | grade | progressId | type |
| --- | -------: | -----: | ----: | ---------: | ---: |
| 1 | 3 | 1 | 0 | 58 | |
| 2 | 23 | 1 | 0 | 58 | |
| 3 | 41 | 6 | 1 | 58 | |
- **Object table**:
This table will give you information about all objects (exercises/projects)
| id | name | type | status | attrs | childrenAttrs | createdAt | updatedAt |
| --- | ---: | -------: | -----: | ------------------------: | ------------: | --------------------: | --------------------: |
| 1 | 0 | exercise | draft | `{"language": "dom", ...` | `{}` | 2019-03-14T12:02:2... | 2019-03-14T12:02:2... |
| 2 | 0 | project | online | `{"language": "go", ...` | `{}` | 2019-03-14T12:02:2... | 2019-03-14T12:02:2... |
| 3 | 1 | exercise | online | `{"language": "js", ...` | `{}` | 2019-03-14T12:02:2... | 2019-03-14T12:02:2... |
| id | name | type | attrs | childrenAttrs |
| --- | ---: | -------: | ------------------------: | ------------: |
| 1 | 0 | exercise | `{"language": "dom", ...` | `{}` |
| 2 | 0 | project | `{"language": "go", ...` | `{}` |
| 3 | 1 | exercise | `{"language": "js", ...` | `{}` |
Examples:
Lets take for instance the table `user` and try to query it:
@ -121,7 +107,7 @@ Lets take for instance the table `user` and try to query it:
}
```
This simple query will return an array with the `ids` of the users. Imagine if you wanted the `githubLogin`,\
This simple query will return an array with the `id`s of the users. Imagine if you wanted the `login`,\
you could just add this attribute to the query, example:
```js
@ -129,7 +115,7 @@ you could just add this attribute to the query, example:
query {
user {
id
githubLogin
login
}
}
}
@ -137,31 +123,31 @@ you could just add this attribute to the query, example:
You can try to `curl` the API endpoint to see the result given by the server:
- `curl "https://[[DOMANIN]]/api/graphql-engine/v1/graphql" --data '{"query":"{user{id githubLogin}}"}'`
- `curl "https://[[DOMANIN]]/api/graphql-engine/v1/graphql" --data '{"query":"{user{id login}}"}'`
Here is another example of a query using the table `user`:
```js
{
query: {
query {
user(where: { id: { _eq: 6 }}) {
id
githubLogin
login
}
}
}
```
**Note** that for this query is required the introduction of variables (arguments)\
so it will return just one user, the user that as the id equal to `6`.
so it will return just one user, the user that as the `id` equal to `6`.
You can see the result using `curl`:
- `curl "https://[[DOMANIN]]/api/graphql-engine/v1/graphql" --data '{"query":"{user(where:{id:{_eq:6}}){id githubLogin}}"}'`
- `curl "https://[[DOMANIN]]/api/graphql-engine/v1/graphql" --data '{"query":"{user(where:{id:{_eq:6}}){id login}}"}'`
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, _https://[[DOMAIN]]/public/subjects/grapqhl_
In graphQL the usage of arguments can be specified in the schema of the API. Like said above you can visit the _docs_ for the graphQL endpoint you are going to use, _https://[[DOMAIN]]/public/subjects/grapqhl_
nesting fetching, using the :
Example using the nesting, using the result and user table :
```js
{
@ -169,17 +155,18 @@ nesting fetching, using the :
id
user {
id
githubLogin
login
}
}
}
```
For this example we ask for the results `id` and `user`s that are associated to the `result`, requesting the users `name`s and `id`s.\
For this example we ask for the results `id` and `user`s that are associated to the `result`, requesting the users `name`s and `id`s.
This project will help you learn about:
- [Graphql](https://graphql.org/) language
- Custom search operations (`include`/`exclude`/`fuzzy`)
- [GraphQL](https://graphql.org/)
- [GraphiQL](https://github.com/graphql/graphiql)
- Basics of human-computer interface
- UI/UX

154
subjects/graphql/index.html

@ -1,119 +1,37 @@
<!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, li {
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>
<head>
<title>Simple GraphiQL Example</title>
<link href="https://unpkg.com/graphiql/graphiql.min.css" rel="stylesheet" />
</head>
<body style="margin: 0;">
<div id="graphiql" style="height: 100vh;"></div>
<script
crossorigin
src="https://unpkg.com/react/umd/react.production.min.js"
></script>
<script
crossorigin
src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"
></script>
<script
crossorigin
src="https://unpkg.com/graphiql/graphiql.min.js"
></script>
<script>
const graphQLFetcher = graphQLParams =>
fetch(`https://${location.hostname}/api/graphql-engine/v1/graphql`, {
method: 'post',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(graphQLParams),
})
.then(response => response.json())
.catch(() => response.text());
ReactDOM.render(
React.createElement(GraphiQL, { fetcher: graphQLFetcher }),
document.getElementById('graphiql'),
);
</script>
</body>
</html>

163
subjects/graphql/lib/schema.js

@ -1,163 +0,0 @@
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 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>`
if (t === 'query') {
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 += `<hr><h4><b>${k}</b>: <a>(${v.description})</a> // ${v.comment}</h4>Expressions:<br>`
for (const exp of v.expressions) {
docDisplay.innerHTML += `<h4>${exp}</h4>`
}
docDisplay.innerHTML += `<a>Example: </a><h4>${v.example}</h4><br><br>`
}
}
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()
/*--------------*/

136
subjects/graphql/lib/schema.json

@ -1,136 +0,0 @@
{
"search": [
"[user!]!",
"[object!]!",
"[transaction!]!",
"[progress_view!]!",
"[result!]!",
"query",
"arguments"
],
"root_types": [{ "query": "query" }, { "arguments": "arguments" }],
"query": {
"user": "[user!]!",
"object": "[object!]!",
"transaction": "[transaction!]!",
"progress_view": "[progress_view!]!",
"result": "[result!]!"
},
"types": [
{
"user": {
"id": "ID!",
"attrs": "Jsonb!",
"createdAt": "String!",
"discordDMChannelId": "String!",
"discordId": "String!",
"discordLogin": "String!",
"githubId": "String!",
"githubLogin": "String!",
"profile": "Jsonb!",
"updatedAt": "String!"
},
"comment": "Fetch data from the table: \"user\"\nThis table will have information about the user"
},
{
"object": {
"id": "ID!",
"attrs": "Jsonb!",
"authorId": "String!",
"childrenAttrs": "String!",
"createdAt": "String!",
"externalRelationUrl": "String!",
"name": "String!",
"type": "String!",
"status": "String!",
"updatedAt": "String!"
},
"comment": "Fetch data from the table: \"object\"\nThis table will give you information about all objects (exercises/projects)"
},
{
"transaction": {
"id": "ID!",
"attrs": "Jsonb!",
"amount": "Int!",
"createdAt": "String!",
"type": "String!",
"userId": "Int!"
},
"comment": "Fetch data from the table: \"transaction\"\nThis table will give you access to XP and audits ratio"
},
{
"progress_view": {
"id": "ID!",
"attrs": "Jsonb!",
"bestResultId": "Int!",
"createdAt": "String!",
"eventId": "Int!",
"groupId": "Int!",
"objectId": "Int!",
"updatedAt": "String!",
"userId": "Int!"
},
"comment": "Fetch data from the table: \"progress\"\nBoth progress and result table will give you the student progression"
},
{
"result": {
"id": "ID!",
"progressId": "Int!",
"attrs": "Jsonb!",
"createdAt": "String!",
"grade": "Int!",
"updatedAt": "String!"
},
"comment": "Fetch data from the table: \"result\"\nBoth progress and result table will give you the student progression"
}
],
"arguments": {
"where": {
"description": "bool_exp",
"expressions": [
"_eq (equal)",
"_gt (grater then)",
"_gte (grater then equal)",
"_in (in)",
"_is_null (boolean)",
"_lt (lower then)",
"_lte (lower then equal)",
"_neq (not equal)",
"_nin (not in)"
],
"example": "user(where: {id: { _eq: x } }){\n id \n}",
"comment": "filter the rows returned"
},
"order_by": {
"description": "[order_by!]",
"expressions": [
"asc (ascending)",
"asc_nulls_first",
"asc_nulls_last",
"desc (descending)",
"desc_nulls_first",
"asc_nulls_last"
],
"example": "user(order_by: {id: desc }){\n id \n}",
"comment": "sort the rows by one or more columns"
},
"limit": {
"description": "Int",
"expressions": ["Int"],
"example": "user(limit: 1 ){\n id \n}",
"comment": "limit the number of rows returned"
},
"distinct_on": {
"description": "column name",
"expressions": ["column name"],
"example": "object(distinct_on: name, where: { name:{ _eq:\"forum\" } }){\n name \n}",
"comment": "distinct select on columns"
},
"offset": {
"description": "int",
"expressions": ["Int"],
"example": "object(distinct_on: name, where: { name:{ _eq:\"forum\" } }){\n name\n }",
"comment": "skip the first n rows by one or more columns"
}
}
}
Loading…
Cancel
Save