├── .gitignore ├── README.md ├── app.js ├── controllers ├── freelance.js ├── freelances.js ├── results.js └── survey.js ├── index.js ├── models ├── freelances.js ├── results.js └── survey.js ├── package.json ├── public ├── images │ ├── 1.jpeg │ ├── 10.jpeg │ ├── 11.jpeg │ ├── 12.jpeg │ ├── 2.jpeg │ ├── 3.jpeg │ ├── 4.jpeg │ ├── 5.jpeg │ ├── 6.jpeg │ ├── 7.jpeg │ ├── 8.jpeg │ ├── 9.jpeg │ └── ada-lovelace.png └── stylesheets │ └── style.css ├── routes └── index.js └── views ├── error.pug ├── index.pug └── layout.pug /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenClassrooms - React intermédiaire 2 | 3 | Ce repo contient le code de l'API dont vous aurez besoin pour le cours OpenClassrooms ------. 4 | Il va de paire avec [le repository de la partie frontend](https://github.com/OpenClassrooms-Student-Center/7150606-React-intermediaire.git). 5 | 6 | 7 | ## Lancer l'API en local 8 | 9 | Pour suivre le cours, vous aurez besoin d'installer l'API en local sur votre machine. Pour cela : 10 | 1. Faites un `git clone https://github.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire.git` 11 | 2. Installez les `node_modules` avec `yarn` 12 | 3. Faites tourner l'API avec `yarn start` 13 | 14 | 15 | ## Consommer l 'API 16 | L'API Shiny est une API REST. Si vous avez un doute sur ce qu'est une API REST, n'hésitez pas à jeter un oeil à l'excellent cours [Adoptez les API REST pour vos projets web. 17 | ](https://openclassrooms.com/fr/courses/6573181-adoptez-les-api-rest-pour-vos-projets-web). 18 | Une fois lancée, cette API met plusieurs routes à votre disposition : 19 | 20 | - La route pour récupérer les profils des freelances : 21 | `GET /freelances` 22 | 23 | - La route pour avoir le détail d'un profil de freelance : 24 | `GET /profile/?id={id}` 25 | 26 | - La route pour avoir le questionnaire : 27 | `GET /survey/` 28 | 29 | - La route pour obtenir le résultat du questionnaire : 30 | `GET /results/?a1={answer1}&a2={answer2}&a3={answer3}...` 31 | 32 | 33 | ## Modifier l'API 34 | 35 | N'hésitez pas à explorer l'API pour tester. Cette base de code a été initialisée avec [Express Generator](https://expressjs.com/fr/starter/generator.html). -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | const createError = require('http-errors') 2 | const express = require('express') 3 | const path = require('path') 4 | const cookieParser = require('cookie-parser') 5 | const logger = require('morgan') 6 | const indexRouter = require('./routes/index') 7 | const app = express() 8 | 9 | const cors = require('cors') 10 | 11 | app.use(cors()) 12 | // view engine setup 13 | app.set('views', path.join(__dirname, 'views')) 14 | app.set('view engine', 'pug') 15 | 16 | app.use(logger('dev')) 17 | app.use(express.json()) 18 | app.use(express.urlencoded({ extended: false })) 19 | app.use(cookieParser()) 20 | app.use(express.static(path.join(__dirname, 'public'))) 21 | 22 | app.use(function(req, res, next){ 23 | setTimeout(next, Math.floor( ( Math.random() * 2000 ) + 100 ) ) 24 | }); 25 | 26 | app.use('/', indexRouter) 27 | 28 | // catch 404 and forward to error handler 29 | app.use(function (req, res, next) { 30 | next(createError(404)) 31 | }) 32 | 33 | // error handler 34 | app.use(function (err, req, res, next) { 35 | // set locals, only providing error in development 36 | res.locals.message = err.message 37 | res.locals.error = req.app.get('env') === 'development' ? err : {} 38 | 39 | // render the error page 40 | res.status(err.status || 500) 41 | res.render('error') 42 | }) 43 | 44 | module.exports = app 45 | -------------------------------------------------------------------------------- /controllers/freelance.js: -------------------------------------------------------------------------------- 1 | const freelancesData = require('../models/freelances') 2 | 3 | function getFreelance(id) { 4 | return freelancesData.find((freelancer => freelancer.id === id)) 5 | } 6 | 7 | module.exports = getFreelance 8 | -------------------------------------------------------------------------------- /controllers/freelances.js: -------------------------------------------------------------------------------- 1 | const freelancesData = require('../models/freelances') 2 | 3 | function getFreelances() { 4 | return freelancesData.map(({ id, name, job, picture }) => ({ 5 | id, 6 | name, 7 | job, 8 | picture 9 | })) 10 | } 11 | 12 | module.exports = getFreelances 13 | -------------------------------------------------------------------------------- /controllers/results.js: -------------------------------------------------------------------------------- 1 | const { jobAnswersData, jobsDefinitionData } = require('../models/results') 2 | 3 | function getResults(a1, a2, a3, a4, a5, a6) { 4 | 5 | const answers = { a1, a2, a3, a4, a5, a6 } 6 | const answerNumbers = Object.keys(answers) 7 | 8 | const jobsList = Object.keys(jobAnswersData) 9 | 10 | const requiredJobsList = answerNumbers.reduce((prevJobs, answerNumber) => { 11 | if (!answers[answerNumber] || answers[answerNumber] === 'false') { 12 | return prevJobs 13 | } 14 | 15 | const jobs = jobsList.reduce((prevJobAnswers, jobTitle) => { 16 | if (jobAnswersData[jobTitle].includes(answerNumber)) { 17 | return [...prevJobAnswers, jobTitle] 18 | } 19 | return prevJobAnswers 20 | }, [] ) 21 | 22 | return [...prevJobs, ...jobs] 23 | }, [] ) 24 | 25 | const uniqueJobs = [...new Set(requiredJobsList)]; 26 | return uniqueJobs.map(job => ({ 27 | title: job, 28 | description: jobsDefinitionData[job] 29 | })) 30 | } 31 | 32 | module.exports = getResults 33 | -------------------------------------------------------------------------------- /controllers/survey.js: -------------------------------------------------------------------------------- 1 | const surveyData = require('../models/survey') 2 | 3 | function getSurvey() { 4 | // return surveyData[questionNumber] 5 | return surveyData 6 | } 7 | 8 | module.exports = getSurvey 9 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * Module dependencies. 5 | */ 6 | 7 | var app = require('./app') 8 | var http = require('http') 9 | 10 | /** 11 | * Get port from environment and store in Express. 12 | */ 13 | 14 | var port = normalizePort(process.env.PORT || '8000') 15 | app.set('port', port) 16 | 17 | /** 18 | * Create HTTP server. 19 | */ 20 | 21 | var server = http.createServer(app) 22 | 23 | /** 24 | * Listen on provided port, on all network interfaces. 25 | */ 26 | 27 | server.listen(port) 28 | server.on('error', onError) 29 | server.on('listening', onListening) 30 | 31 | /** 32 | * Normalize a port into a number, string, or false. 33 | */ 34 | 35 | function normalizePort(val) { 36 | var port = parseInt(val, 10) 37 | 38 | if (isNaN(port)) { 39 | // named pipe 40 | return val 41 | } 42 | 43 | if (port >= 0) { 44 | // port number 45 | return port 46 | } 47 | 48 | return false 49 | } 50 | 51 | /** 52 | * Event listener for HTTP server "error" event. 53 | */ 54 | 55 | function onError(error) { 56 | if (error.syscall !== 'listen') { 57 | throw error 58 | } 59 | 60 | var bind = typeof port === 'string' ? 'Pipe ' + port : 'Port ' + port 61 | 62 | // handle specific listen errors with friendly messages 63 | switch (error.code) { 64 | case 'EACCES': 65 | console.error(bind + ' requires elevated privileges') 66 | process.exit(1) 67 | break 68 | case 'EADDRINUSE': 69 | console.error(bind + ' is already in use') 70 | process.exit(1) 71 | break 72 | default: 73 | throw error 74 | } 75 | } 76 | 77 | /** 78 | * Event listener for HTTP server "listening" event. 79 | */ 80 | 81 | function onListening() { 82 | var addr = server.address() 83 | var bind = typeof addr === 'string' ? 'pipe ' + addr : 'port ' + addr.port 84 | console.log('Listening on ' + bind); 85 | } 86 | -------------------------------------------------------------------------------- /models/freelances.js: -------------------------------------------------------------------------------- 1 | const freelances = [ 2 | { 3 | id: '1', 4 | name: 'Julien Brun', 5 | job: 'Développeur mobile', 6 | picture: 'http://localhost:8000/images/4.jpeg', 7 | skills: ['React Native'], 8 | location: 'Lyon', 9 | available: true, 10 | tjm: 500 11 | }, 12 | { 13 | id: '2', 14 | name: 'Arielle Gautier', 15 | job: 'Développeuse fullstack', 16 | picture: 'http://localhost:8000/images/1.jpeg', 17 | skills: ['Node JS', 'Vue.js', 'React', 'AWS'], 18 | location: 'Paris', 19 | available: false, 20 | tjm: 620 21 | }, 22 | { 23 | id: '3', 24 | name: 'Marine Carpentier', 25 | job: 'Développeuse frontend', 26 | picture: 'http://localhost:8000/images/2.jpeg', 27 | skills: ['React', 'Gatsby', 'Next.js'], 28 | location: 'Bordeaux', 29 | available: true, 30 | tjm: 520 31 | }, 32 | { 33 | id: '4', 34 | name: 'Lucille Barre', 35 | job: 'Product Designer', 36 | picture: 'http://localhost:8000/images/3.jpeg', 37 | skills: ['Figma', 'Webflow'], 38 | location: 'Lille', 39 | available: false, 40 | tjm: 650 41 | }, 42 | { 43 | id: '5', 44 | name: 'Clément Rolland', 45 | job: 'Développeur mobile', 46 | picture: 'http://localhost:8000/images/5.jpeg', 47 | skills: ['IOS', 'Android'], 48 | location: 'Lyon', 49 | available: false, 50 | tjm: 450 51 | }, 52 | { 53 | id: '6', 54 | name: 'Grégoire Chevalier', 55 | job: 'Développeur backend', 56 | picture: 'http://localhost:8000/images/6.jpeg', 57 | skills: ['Python', 'Django', 'Docker'], 58 | location: 'Paris', 59 | available: true, 60 | tjm: 510 61 | }, 62 | { 63 | id: '7', 64 | name: 'Raphaël Rodriguez', 65 | job: 'Designer', 66 | picture: 'http://localhost:8000/images/7.jpeg', 67 | skills: ['Sketch', 'Illustrator'], 68 | location: 'Paris', 69 | available: true, 70 | tjm: 480 71 | }, 72 | { 73 | id: '8', 74 | name: 'Hugo Vysa', 75 | job: 'Développeur frontend', 76 | picture: 'http://localhost:8000/images/8.jpeg', 77 | skills: ['SEO', 'Javascript Vanilla'], 78 | location: 'Toulouse', 79 | available: false, 80 | tjm: 560 81 | }, 82 | { 83 | id: '9', 84 | name: 'Mina Toman', 85 | job: 'Développeuse Mobile', 86 | picture: 'http://localhost:8000/images/11.jpeg', 87 | skills: ['Android', 'React Native'], 88 | location: 'Bayonne', 89 | available: true, 90 | tjm: 630 91 | }, 92 | { 93 | id: '10', 94 | name: 'Amélie Leroy', 95 | job: 'Développeuse backend', 96 | picture: 'http://localhost:8000/images/12.jpeg', 97 | skills: ['Node.js', 'Express', 'Docker'], 98 | location: 'Paris', 99 | available: false, 100 | tjm: 400 101 | }, 102 | { 103 | id: '11', 104 | name: 'Maxime Lebrun', 105 | job: 'Intégrateur SEO', 106 | picture: 'http://localhost:8000/images/9.jpeg', 107 | skills: ['SEO'], 108 | location: 'Rennes', 109 | available: false, 110 | tjm: 600 111 | }, 112 | 113 | ] 114 | 115 | module.exports = freelances 116 | -------------------------------------------------------------------------------- /models/results.js: -------------------------------------------------------------------------------- 1 | const jobAnswersData = { 2 | seo: ['a2'], 3 | frontend: ['a4'], 4 | design: ['a3'], 5 | backend: ['a1','a4','a5'], 6 | mobile: ['a6'] 7 | } 8 | 9 | const jobsDefinitionData = { 10 | seo: `Le SEO est en charge du référencement web d'une page`, 11 | frontend: `Le développeur ou la développeuse frontend se charge de l'interface : interactions avec l'utilisateur, style, etc.`, 12 | design: `La personne en charge du design va devoir préparer les maquettes du site`, 13 | backend: `Le backend consiste en la partie émergée de l'iceberg : ce qui permet de faire tourner une application mais qui n'est pas visible par l'utilisateur`, 14 | mobile: `Les développeurs mobile conçoivent des applications mobiles en natif (et non simplement en JavaScript)` 15 | } 16 | 17 | module.exports = { jobAnswersData, jobsDefinitionData } -------------------------------------------------------------------------------- /models/survey.js: -------------------------------------------------------------------------------- 1 | const survey = { 2 | 1: 'Votre site doit-il sauvegarder des données entrées par vos utilisateurs ?', 3 | 2: 'Votre application doit-elle impérativement apparaître en premier dans les résultats de recherche ?', 4 | 3: `Avez-vous déjà des maquettes pour l'application que vous voulez créer ?`, 5 | 4: `Le site comporte-t-il une fonction d'authentification ?`, 6 | 5: `Souhaitez-vous avoir plusieurs types de comptes pour votre application (administrateur, visiteur, utilisateur, etc). ?`, 7 | 6: `Avez-vous prévu une version mobile à part entière ?` 8 | } 9 | 10 | module.exports = survey 11 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "oc-react2-api", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./index.js", 7 | "devstart": "nodemon ./index.js" 8 | }, 9 | "dependencies": { 10 | "cookie-parser": "~1.4.4", 11 | "cors": "^2.8.5", 12 | "debug": "~2.6.9", 13 | "express": "~4.16.1", 14 | "http-errors": "~1.6.3", 15 | "morgan": "~1.9.1", 16 | "pug": "2.0.0-beta11", 17 | "uuid": "^8.3.2" 18 | }, 19 | "devDependencies": { 20 | "nodemon": "^2.0.7" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /public/images/1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/1.jpeg -------------------------------------------------------------------------------- /public/images/10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/10.jpeg -------------------------------------------------------------------------------- /public/images/11.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/11.jpeg -------------------------------------------------------------------------------- /public/images/12.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/12.jpeg -------------------------------------------------------------------------------- /public/images/2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/2.jpeg -------------------------------------------------------------------------------- /public/images/3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/3.jpeg -------------------------------------------------------------------------------- /public/images/4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/4.jpeg -------------------------------------------------------------------------------- /public/images/5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/5.jpeg -------------------------------------------------------------------------------- /public/images/6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/6.jpeg -------------------------------------------------------------------------------- /public/images/7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/7.jpeg -------------------------------------------------------------------------------- /public/images/8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/8.jpeg -------------------------------------------------------------------------------- /public/images/9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/9.jpeg -------------------------------------------------------------------------------- /public/images/ada-lovelace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenClassrooms-Student-Center/7150606-API-React-intermediaire/caf1356fae545455602e4eb09148e49873078ed9/public/images/ada-lovelace.png -------------------------------------------------------------------------------- /public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } 9 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | const express = require('express') 2 | const router = express.Router() 3 | const getSurvey = require('../controllers/survey') 4 | const getFreelances = require('../controllers/freelances') 5 | const getFreelance = require('../controllers/freelance') 6 | const getResults = require('../controllers/results') 7 | 8 | router.get('/survey', function (req, res) { 9 | const surveyData = getSurvey() 10 | if (!surveyData) { 11 | res.status(400).send('Not found.') 12 | } else { 13 | res.send({ surveyData }) 14 | } 15 | }) 16 | 17 | router.get('/freelance', function (req, res) { 18 | const { id } = req.query 19 | const freelanceData = getFreelance(id) 20 | if (!freelanceData) { 21 | res.status(400).send('Not found.') 22 | } else { 23 | res.send({ freelanceData }) 24 | } 25 | }) 26 | 27 | router.get('/results', function (req, res) { 28 | const { a1, a2, a3, a4, a5, a6 } = req.query 29 | const resultsData = getResults(a1, a2, a3, a4, a5, a6) 30 | if (!resultsData) { 31 | res.status(400).send('Not found.') 32 | } else { 33 | res.send({ resultsData }) 34 | } 35 | }) 36 | 37 | router.get('/freelances', function (req, res, next) { 38 | const freelancersList = getFreelances() 39 | res.send({ freelancersList }) 40 | }) 41 | 42 | 43 | router.get('/', function (req, res, next) { 44 | res.render('index', { title: 'API - React intermédiaire' }) 45 | }) 46 | 47 | module.exports = router 48 | -------------------------------------------------------------------------------- /views/error.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /views/index.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Bienvenue sur l'API du cours OpenClassrooms - React intermédiaire. 6 | p Voici les routes disponibles : 7 | ul 8 | li /freelances 9 | li /profile/?id={id} 10 | li /survey 11 | li /results/?a1={answer1}&a2={answer2}&a3={answer3}... 12 | -------------------------------------------------------------------------------- /views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | --------------------------------------------------------------------------------