Introduction à la théorie de l'apprentissage automatique et à ses applications: un didacticiel visuel avec des exemples
Science Des Données Et Bases De Données
Dans cette série d'articles, nous développerons un prototype de site Web à contenu statique. Il générera des pages HTML statiques simples mises à jour quotidiennement pour les référentiels GitHub populaires afin de suivre leurs dernières versions. Les frameworks de génération de pages Web statiques disposent d'excellentes fonctionnalités pour y parvenir. Nous utiliserons Gatsby.js, l'un des plus populaires.
Dans Gatsby, il existe de nombreuses façons de collecter des données pour un front-end sans avoir de back-end (sans serveur), Plateformes CMS sans tête et Plugins sources Gatsby parmi eux. Mais nous allons implémenter un back-end pour stocker des informations de base sur les référentiels GitHub et leurs dernières versions. Ainsi, nous aurons un contrôle total sur notre back-end et notre front-end.
Je vais également couvrir un ensemble d'outils pour déclencher une mise à jour quotidienne de votre application. Vous pouvez également le déclencher manuellement ou chaque fois qu'un événement spécifique se produit.
Notre application frontale sera exécutée sur Netlify, et l'application back-end fonctionnera sur Heroku en utilisant un plan gratuit. Il dormira périodiquement : 'Quand quelqu'un accède à l'application, le gestionnaire de dynamomètre réveillera automatiquement le dynamomètre Web pour exécuter le type de processus Web.' Donc, nous pouvons le réveiller via AWS Lambda et AWS CloudWatch. Au moment d'écrire ces lignes, c'est le moyen le plus rentable d'avoir un prototype en ligne 24/7.
Pour que ces articles restent concentrés sur un seul sujet, je ne traiterai pas de l'authentification, de la validation, de l'évolutivité ou d'autres sujets généraux. La partie codage de cet article sera aussi simple que possible. La structure du projet et l'utilisation du bon ensemble d'outils sont plus importantes.
Dans cette première partie de la série, nous développerons et déploierons notre application back-end. Dans La seconde partie , nous développerons et déploierons notre application frontale et déclencherons des builds quotidiens.
L'application back-end sera écrite en Node.js (pas obligatoire, mais par souci de simplicité) et toutes les communications se feront via les API REST. Nous ne collecterons pas de données à partir du front-end de ce projet. (Si cela vous intéresse, jetez un œil à Formulaires Gatsby .)
Tout d'abord, nous allons commencer par implémenter un back-end d'API REST simple qui expose les opérations CRUD de la collection de référentiels dans notre MongoDB. Ensuite, nous planifierons une tâche cron qui consomme l'API GitHub v4 (GraphQL) afin de mettre à jour les documents de cette collection. Ensuite, nous déploierons tout cela sur le cloud Heroku. Enfin, nous déclencherons une reconstruction du front-end à la fin de notre tâche cron.
Dans le deuxième article, nous nous concentrerons sur la mise en œuvre du createPages
FEU . Nous rassemblerons tous les référentiels du back-end et générerons une seule page d'accueil contenant une liste de tous les référentiels, plus une page pour chaque document de référentiel renvoyé. Alors bien déployer notre front-end sur Netlify .
Cette partie n'est pas obligatoire si votre application ne se met pas en veille. Sinon, vous devez vous assurer que votre back-end est opérationnel au moment de la mise à jour des référentiels. En guise de solution, vous pouvez créer une planification cron sur AWS CloudWatch 10 minutes avant votre mise à jour quotidienne et la lier en tant que déclencheur à votre GET
méthode dans AWS Lambda. L'accès à l'application back-end réveillera l'instance Heroku. Plus de détails seront à la fin du deuxième article.
Voici l'architecture que nous allons implémenter:
Je suppose que les lecteurs de cet article ont des connaissances dans les domaines suivants:
C’est aussi bien si vous savez:
Plongeons-nous dans la mise en œuvre du back-end. Nous allons le diviser en deux tâches. Le premier consiste à préparer les points de terminaison de l'API REST et à les lier à notre collection de référentiels. La seconde consiste à implémenter une tâche cron qui consomme l'API GitHub et met à jour la collection.
Nous utiliserons Express pour notre cadre d'application Web et Mongoose pour notre connexion MongoDB. Si vous connaissez Express et Mongoose, vous pourrez peut-être passer à l'étape 2.
(D'un autre côté, si vous avez besoin de plus de familiarité avec Express, vous pouvez consulter le guide de démarrage officiel Express ; si vous n'êtes pas sur Mongoose, le guide de démarrage officiel Mongoose devrait être utile.)
La hiérarchie des fichiers / dossiers de notre projet sera simple:
Plus en détail:
env.config.js
est le fichier de configuration des variables d'environnementroutes.config.js
sert à mapper les points de terminaison de reposrepository.controller.js
contient des méthodes pour travailler sur notre modèle de référentielrepository.model.js
contient le schéma MongoDB des opérations de référentiel et CRUDindex.js
est une classe d'initialisationpackage.json
contient des dépendances et des propriétés de projetExécuter npm install
(ou yarn
, si vous avez installé Yarn) après avoir ajouté ces dépendances à package.json
:
{ // ... 'dependencies': { 'body-parser': '1.7.0', 'express': '^4.8.7', 'moment': '^2.17.1', 'moment-timezone': '^0.5.13', 'mongoose': '^5.1.1', 'node-uuid': '^1.4.8', 'sync-request': '^4.0.2' } // ... }
Notre env.config.js
le fichier n'a que port
, environment
(dev
ou prod
), et mongoDbUri
propriétés pour l'instant:
module.exports = ;
routes.config.js
contient des mappages de requêtes et appellera la méthode correspondante de notre contrôleur:
const RepositoryController = require('../controller/repository.controller'); exports.routesConfig = function(app) { app.post('/repositories', [ RepositoryController.insert ]); app.get('/repositories', [ RepositoryController.list ]); app.get('/repositories/:id', [ RepositoryController.findById ]); app.patch('/repositories/:id', [ RepositoryController.patchById ]); app.delete('/repositories/:id', [ RepositoryController.deleteById ]); };
Le repository.controller.js
file est notre couche de service. Sa responsabilité est d'appeler la méthode correspondante de notre modèle de référentiel:
const RepositoryModel = require('../model/repository.model'); exports.insert = (req, res) => { RepositoryModel.create(req.body) .then((result) => { res.status(201).send({ id: result._id }); }); }; exports.findById = (req, res) => { RepositoryModel.findById(req.params.id) .then((result) => { res.status(200).send(result); }); }; exports.list = (req, res) => { RepositoryModel.list() .then((result) => { res.status(200).send(result); }) }; exports.patchById = (req, res) => { RepositoryModel.patchById(req.params.id, req.body) .then(() => { res.status(204).send({}); }); }; exports.deleteById = (req, res) => { RepositoryModel.deleteById(req.params.id, req.body) .then(() => { res.status(204).send({}); }); };
repository.model.js
gère la connexion MongoDb et les opérations CRUD pour le modèle de référentiel. Les champs du modèle sont:
comment obtenir un terminal bloomberg
owner
: Le propriétaire du référentiel (société ou utilisateur)name
: Le nom du référentielcreatedAt
: La date de création de la dernière versionresourcePath
: Le dernier chemin de versiontagName
: La dernière balise de versionreleaseDescription
: Notes de versionhomepageUrl
: URL d'accueil du projetrepositoryDescription
: La description du référentielavatarUrl
: L'URL de l'avatar du propriétaire du projetconst Mongoose = require('mongoose'); const Config = require('../config/env.config'); const MONGODB_URI = Config.mongoDbUri; Mongoose.connect(MONGODB_URI, { useNewUrlParser: true }); const Schema = Mongoose.Schema; const repositorySchema = new Schema({ owner: String, name: String, createdAt: String, resourcePath: String, tagName: String, releaseDescription: String, homepageUrl: String, repositoryDescription: String, avatarUrl: String }); repositorySchema.virtual('id').get(function() { return this._id.toHexString(); }); // Ensure virtual fields are serialised. repositorySchema.set('toJSON', { virtuals: true }); repositorySchema.findById = function(cb) { return this.model('Repository').find({ id: this.id }, cb); }; const Repository = Mongoose.model('repository', repositorySchema); exports.findById = (id) => { return Repository.findById(id) .then((result) => { if (result) { result = result.toJSON(); delete result._id; delete result.__v; return result; } }); }; exports.create = (repositoryData) => { const repository = new Repository(repositoryData); return repository.save(); }; exports.list = () => { return new Promise((resolve, reject) => { Repository.find() .exec(function(err, users) { if (err) { reject(err); } else { resolve(users); } }) }); }; exports.patchById = (id, repositoryData) => { return new Promise((resolve, reject) => { Repository.findById(id, function(err, repository) { if (err) reject(err); for (let i in repositoryData) { repository[i] = repositoryData[i]; } repository.save(function(err, updatedRepository) { if (err) return reject(err); resolve(updatedRepository); }); }); }) }; exports.deleteById = (id) => { return new Promise((resolve, reject) => { Repository.deleteOne({ _id: id }, (err) => { if (err) { reject(err); } else { resolve(err); } }); }); }; exports.findByOwnerAndName = (owner, name) => { return Repository.find({ owner: owner, name: name }); };
Voici ce que nous avons après notre premier commit: Une connexion MongoDB et nos opérations REST .
Nous pouvons exécuter notre application avec la commande suivante:
node index.js
Pour les tests, envoyez les requêtes à localhost:3000
(en utilisant par exemple Postman ou cURL):
Publier: http: // localhost: 3000 / repositories
Corps:
{ 'owner' : 'facebook', 'name' : 'react' }
Avoir: http: // localhost: 3000 / repositories
Avoir: http: // localhost: 3000 / repositories /: id
Pièce: http: // localhost: 3000 / repositories /: id
Corps:
{ 'owner' : 'facebook', 'name' : 'facebook-android-sdk' }
Cela fonctionne, il est temps d'automatiser les mises à jour.
Dans cette partie, nous allons configurer un simple travail cron (qui commencera à minuit UTC) pour mettre à jour les référentiels GitHub que nous avons insérés dans notre base de données. Nous avons ajouté uniquement le owner
et name
paramètres uniquement dans notre exemple ci-dessus, mais ces deux champs nous suffisent pour accéder aux informations générales sur un référentiel donné.
Afin de mettre à jour nos données, nous devons consommer l'API GitHub. Pour cette partie, il est préférable de se familiariser avec GraphQL et v4 de l'API GitHub .
Nous devons également créer un jeton d'accès GitHub . Les portées minimales requises pour cela sont:
Cela générera un jeton et nous pourrons envoyer des requêtes à GitHub avec lui.
Revenons maintenant à notre code.
Nous avons deux nouvelles dépendances dans package.json
:
'axios': '^0.18.0'
est un client HTTP, nous pouvons donc faire des requêtes à l'API GitHub'cron': '^1.7.0'
est un planificateur de tâches cronComme d'habitude, exécutez npm install
ou yarn
après avoir ajouté des dépendances.
Nous aurons également besoin de deux nouvelles propriétés dans config.js
:
'githubEndpoint': 'https://api.github.com/graphql'
'githubAccessToken': process.env.GITHUB_ACCESS_TOKEN
(vous devrez définir la variable d'environnement GITHUB_ACCESS_TOKEN
avec votre propre jeton d'accès personnel)Créez un nouveau fichier sous le controller
dossier avec le nom cron.controller.js
. Il appellera simplement le updateResositories
méthode de repository.controller.js
aux heures prévues:
const RepositoryController = require('../controller/repository.controller'); const CronJob = require('cron').CronJob; function updateDaily() { RepositoryController.updateRepositories(); } exports.startCronJobs = function () { new CronJob('0 0 * * *', function () {updateDaily()}, null, true, 'UTC'); };
Les modifications finales pour cette partie seront dans repository.controller.js
. Par souci de concision, nous allons le concevoir de manière à mettre à jour tous les dépôts à la fois. Mais si vous avez un grand nombre de référentiels, vous pouvez dépasser le limitations des ressources de l'API de GitHub . Si tel est le cas, vous devrez le modifier pour qu'il s'exécute par lots limités, répartis dans le temps.
L'implémentation tout-à-une de la fonctionnalité de mise à jour ressemblera à ceci:
async function asyncUpdate() { await RepositoryModel.list().then((array) => { const promises = array.map(getLatestRelease); return Promise.all(promises); }); } exports.updateRepositories = async function update() { console.log('GitHub Repositories Update Started'); await asyncUpdate().then(() => { console.log('GitHub Repositories Update Finished'); }); };
Enfin, nous allons appeler le point de terminaison et mettre à jour le modèle de référentiel.
Le getLatestRelease
La fonction générera une requête GraphQL et appellera l'API GitHub. La réponse de cette demande sera ensuite traitée dans le updateDatabase
fonction.
async function updateDatabase(responseData, owner, name) { let createdAt = ''; let resourcePath = ''; let tagName = ''; let releaseDescription = ''; let homepageUrl = ''; let repositoryDescription = ''; let avatarUrl = ''; if (responseData.repository.releases) { createdAt = responseData.repository.releases.nodes[0].createdAt; resourcePath = responseData.repository.releases.nodes[0].resourcePath; tagName = responseData.repository.releases.nodes[0].tagName; releaseDescription = responseData.repository.releases.nodes[0].description; homepageUrl = responseData.repository.homepageUrl; repositoryDescription = responseData.repository.description; if (responseData.organization && responseData.organization.avatarUrl) { avatarUrl = responseData.organization.avatarUrl; } else if (responseData.user && responseData.user.avatarUrl) { avatarUrl = responseData.user.avatarUrl; } const repositoryData = { owner: owner, name: name, createdAt: createdAt, resourcePath: resourcePath, tagName: tagName, releaseDescription: releaseDescription, homepageUrl: homepageUrl, repositoryDescription: repositoryDescription, avatarUrl: avatarUrl }; await RepositoryModel.findByOwnerAndName(owner, name) .then((oldGitHubRelease) => { if (!oldGitHubRelease[0]) { RepositoryModel.create(repositoryData); } else { RepositoryModel.patchById(oldGitHubRelease[0].id, repositoryData); } console.log(`Updated latest release: http://github.com${repositoryData.resourcePath}`); }); } } async function getLatestRelease(repository) { const owner = repository.owner; const name = repository.name; console.log(`Getting latest release for: http://github.com/${owner}/${name}`); const query = ` query { organization(login: '${owner}') { avatarUrl } user(login: '${owner}') { avatarUrl } repository(owner: '${owner}', name: '${name}') { homepageUrl description releases(first: 1, orderBy: {field: CREATED_AT, direction: DESC}) { nodes { createdAt resourcePath tagName description } } } }`; const jsonQuery = JSON.stringify({ query }); const headers = { 'User-Agent': 'Release Tracker', 'Authorization': `Bearer ${GITHUB_ACCESS_TOKEN}` }; await Axios.post(GITHUB_API_URL, jsonQuery, { headers: headers }).then((response) => { return updateDatabase(response.data.data, owner, name); }); }
Après notre deuxième commit, nous aurons implémenté un planificateur cron pour obtenir des mises à jour quotidiennes de nos dépôts GitHub .
Nous avons presque terminé avec le back-end. Mais la dernière étape devrait être effectuée après la mise en œuvre du front-end, nous allons donc la couvrir dans le prochain article.
Dans cette étape, nous déploierons notre application sur Heroku, donc vous devrez créer un compte avec eux si vous n'en avez pas déjà. Si nous lions notre compte Heroku à GitHub, il sera beaucoup plus facile pour nous d'avoir un déploiement continu. À cette fin, J'héberge mon projet sur GitHub .
Après vous être connecté à votre compte Heroku, ajoutez une nouvelle application depuis le tableau de bord:
Donnez-lui un nom unique:
Vous serez redirigé vers une section de déploiement. Sélectionnez GitHub comme méthode de déploiement, recherchez votre référentiel, puis cliquez sur le bouton «Connecter»:
Pour plus de simplicité, vous pouvez activer les déploiements automatiques. Il se déploiera chaque fois que vous enverrez un commit à votre dépôt GitHub:
Nous devons maintenant ajouter MongoDB en tant que ressource. Accédez à l'onglet Ressources et cliquez sur 'Rechercher d'autres modules complémentaires'. (J'utilise personnellement mLab mongoDB.)
Installez-le et entrez le nom de votre application dans la zone de saisie 'Application à approvisionner':
Enfin, nous devons créer un fichier nommé Procfile
au niveau racine de notre projet, qui spécifie les commandes qui sont exécutées par l'application lorsque Heroku la démarre.
Notre Procfile
est aussi simple que ceci:
web: node index.js
Créez le fichier et validez-le. Une fois que vous avez poussé le commit, Heroku déploiera automatiquement votre application, qui sera accessible en tant que https://[YOUR_UNIQUE_APP_NAME].herokuapp.com/
.
Pour vérifier si cela fonctionne, nous pouvons envoyer les mêmes demandes que celles que nous avons envoyées à localhost
.
Après notre troisième engagement, voici à quoi ressemblera notre repo .
Jusqu'à présent, nous avons mis en œuvre le Node.js / API REST basée sur Express sur notre back-end, le programme de mise à jour qui utilise l'API de GitHub et une tâche cron pour l'activer. Ensuite, nous avons déployé notre back-end qui fournira plus tard des données pour notre générateur de contenu Web statique en utilisant Heroku avec un crochet pour une intégration continue. Vous êtes maintenant prêt pour La seconde partie , où nous implémentons le front-end et complétons l'application!
En relation: Les 10 erreurs les plus courantes commises par les développeurs Node.jsAprès la publication, les pages Web statiques contiennent les mêmes données pour toutes les sessions. Dans les pages Web dynamiques, les données peuvent être mises à jour à la volée.
Node.js est léger, rapide, évolutif, open source et bien pris en charge par sa communauté.
Node.js sert d'environnement d'exécution principal pour créer des applications Web évolutives, légères, asynchrones et basées sur les événements avec JavaScript.
Node.js utilise le même langage (JavaScript) pour le côté serveur qui est normalement utilisé dans le navigateur. Il est léger et conçu pour utiliser des opérations d'E / S non bloquantes pendant le traitement des demandes.
En tant que membre de la populaire pile MEAN — MongoDB, Express.js, Angular et Node.js — Node.js est important pour développer des applications Web évolutives et hautes performances avec JavaScript.
Certains des avantages de GraphQL incluent la collecte uniquement de ce dont vous avez besoin sur le serveur, l'obtention de plusieurs ressources en une seule demande et le fait que ses API sont auto-documentées.
GraphQL permet un prototypage et un déploiement de production rapides. De plus, il utilise un seul point de terminaison pour toutes les ressources, ce qui facilite la communication client-serveur.
Heroku est une plate-forme cloud axée sur la rationalisation du lancement et de la mise à l'échelle des applications.