# SobSession 😭 A place for people who love a good cry 😭 ![](https://hackmd.io/_uploads/ryKxgHJw2.gif) --- ## Project Intro It works! View the deployed site here: https://sob-sessions-2.fly.dev/ --- ## Our Roles - Beth: :palm_tree: SCRUM Facilitator :palm_tree: - Tom: :package: DevOps :package: - Mark: :pager: QA :pager: - Taha: :panda_face: UX :panda_face: --- ## SCRUM facilitator - Facilitate stand ups - Discuss roles and tasks with the team - Record progress by updating the kanban board/ the whiteboard in person --- ## DevOps OAuth. --- ## QA * Agreed overarching conventions (e.g. single quotes, tab spaces...) * Two approvals before merge to main * Set up eslint and prettier and incorporated into workflow * However this :point_up: wasn't set up until day two so had nearly 50 linting issues to work through. Next time do it at project start as a chore * Did not automate linting/prettier with e.g. husky - didn't fancy the gitHassle * Set up jest for testing and used supertest to manage http requests * Worked well and removed the need for the helpers.js file and made for readable and concise testing code --- ## UX * Wireframe using [tldraw](https://www.tldraw.com/r/v2_E2B4vhDM-y3bh5FhSJRXa?viewport=335%2C-30%2C1755%2C1005&page=page%3A9VhkqMKi6LCu7kKg2lkFD) * Dark Mode * Stars input slider * Component structure * Input form still quite ugly :zombie: * Couldn't add usernames to ratings :cry: --- ### Setbacks πŸ’§ * Using ES6 modules cost us development time to sort out unpredicatble issues with some node modules like dotenv, jest * --- ### Something we’re proud of #### Sanitise function ```javascipt= export function sanitiseInput(input) { const regex = /[&<>"']/g; const replacements = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#x27;', }; const sanitisedInput = input.replace( regex, match => replacements[match] || '' ); return sanitisedInput; } ``` #### Example jest test ```javascript= import request from 'supertest'; import server from '../src/server'; describe('GET home route handler', () => { it('responds with an html tag ', async () => { const response = await request(server).get('/'); expect(response.status).toBe(200); expect(response.text).toMatch(/<html/); }); it('responds with the sobSessions logo text', async () => { const logoText = 'ΰ²₯_ΰ²₯'; const response = await request(server).get('/'); expect(response.text).toContain(logoText); }); }); ``` --- ## Velocity :fast_forward: On the secon day, we used velocity predictions for smaller tasks ![](https://hackmd.io/_uploads/B1XpZBkvn.jpg) --- ## Aria-Label ```javascript return `<figure aria-label="song rated ${number} star${ number === 1 ? 's' : '' }">${rating}</figure>`; ``` --- ### Paradigms #### Imperative ```javascript= export default function stars(number) { const goldStars = number; const greyStars = 5 - number; let rating = ``; for (let i = 0; i < goldStars; i++) { rating += `<i class="fa-solid fa-star" style="color: #d7a63c;" aria-hidden="true"></i>`; } for (let i = 0; i < greyStars; i++) { rating += `<i class="fa-solid fa-star" style="color: #636363;" aria-hidden="true"></i>`; } return `<figure aria-label="song rated ${number} star${ number > 1 ? 's' : '' }">${rating}</figure>`; } ``` --- #### Declarative ```javascript describe('GET home route handler', () => { it //some code })); ``` #### Reactive ```javascript const content = /*html*/ ` ${header()} <main class="all-songs-container"> <h1> ${req.signedCookies.name} </h1> ${songs} </main> `; ``` ### CLIENT_ID and CLIENT_SECRET ```javascript export const getClient = () => { return { id: process.env.CLIENT_ID, secret: process.env.CLIENT_SECRET, }; }; ``` - The ```CLIENT_ID``` acts as a website id for github to recognise the website. - The ```CLIENT_SECRET``` acts as the website secret for github to recognise the website. - These ```.env``` variables must be set in fly for OAuth to work on the deployed site. - These are set up in developer's github in: ```Settings/ developer settings/ gitHub Apps / appname``` ### oauth ![](https://hackmd.io/_uploads/BJmpMB1Pn.gif) ```javasccript!= const TOKEN_URL = 'https://github.com/login/oauth/access_token'; export function getToken(code) { const body = { client_id: getClient().id, client_secret: getClient().secret, code, }; return fetch(TOKEN_URL, { method: 'POST', body: JSON.stringify(body), headers: { accept: 'application/json', 'content-type': 'application/json' }, }).then(getJson); } ``` | then --- ```javascript!= export default function auth(req, res) { const code = req.query.code; api .getToken(code) .then(api.getUser) .then(user => { res.cookie('refresh_token', user.refresh_token, {...} }); res.cookie('access_token', user.access_token, {...}); res.cookie('name', user.login, {...}); res.redirect('/'); }) .catch(() => res.redirect('/')); } ``` | then --- ```javascript!= const USER_URL = 'https://api.github.com/user'; export function getUser(user) { return fetch(USER_URL, { headers: { accept: 'application/json', authorization: `token ${user.access_token}`, }, }) .then(async res => { if (res.status != 401) return res; const res2 = await refreshToken(res); if (res2.status == 401) throw new Error('Refresh token failed'); return res2; }) .then(res => res.json()) .then(json => { return { ...user, ...json }; }) .catch(err => { console.error('err getting user: ', err); res.redirect('/'); }); } ```
{"metaMigratedAt":"2023-06-18T06:53:01.969Z","metaMigratedFrom":"Content","title":"SobSession","breaks":true,"contributors":"[{\"id\":\"c5c54e30-9692-49dc-baf9-f45297507939\",\"add\":2174,\"del\":704},{\"id\":\"5a3f4bdd-9e0a-4a00-b0eb-3ee951877df7\",\"add\":973,\"del\":1396},{\"id\":\"a210ffc0-41a5-4f68-b66f-05dcc31f5785\",\"add\":5759,\"del\":2268},{\"id\":\"fabff5d3-4757-44f9-9d3c-6ea0d5d4251f\",\"add\":1300,\"del\":81}]"}
    96 views
   Owned this note