From e6109413aa37e14a3b12b146214c81f314c3941f Mon Sep 17 00:00:00 2001 From: Matthias Kretschmann Date: Tue, 14 May 2019 20:52:48 +0200 Subject: [PATCH] =?UTF-8?q?initial=20commit=20=F0=9F=90=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .editorconfig | 13 +++++++ .eslintrc | 3 ++ .gitignore | 4 +++ .nowignore | 3 ++ .travis.yml | 5 +++ LICENSE | 21 ++++++++++++ README.md | 81 ++++++++++++++++++++++++++++++++++++++++++++ index.js | 40 ++++++++++++++++++++++ networks/bounties.js | 62 +++++++++++++++++++++++++++++++++ networks/github.js | 51 ++++++++++++++++++++++++++++ networks/medium.js | 29 ++++++++++++++++ now.json | 14 ++++++++ package.json | 23 +++++++++++++ util/logger.js | 7 ++++ 14 files changed, 356 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 .nowignore create mode 100644 .travis.yml create mode 100755 LICENSE create mode 100644 README.md create mode 100644 index.js create mode 100644 networks/bounties.js create mode 100755 networks/github.js create mode 100644 networks/medium.js create mode 100644 now.json create mode 100644 package.json create mode 100644 util/logger.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b64e0cd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,13 @@ + +# EditorConfig is awesome: http://EditorConfig.org + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +insert_final_newline = true +charset = utf-8 +trim_trailing_whitespace = true + +[*.json] +indent_size = 2 diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..c053478 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "oceanprotocol" +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fad8828 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +node_modules +npm-debug.log +yarn.lock +package-lock.json diff --git a/.nowignore b/.nowignore new file mode 100644 index 0000000..9b9234d --- /dev/null +++ b/.nowignore @@ -0,0 +1,3 @@ +media/ +test/ +package-lock.json diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..83a38c6 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: node + +notifications: + email: false diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..5ddfb9b --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 BigchainDB GmbH + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..9969cad --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +[![banner](https://raw.githubusercontent.com/oceanprotocol/art/master/github/repo-banner%402x.png)](https://oceanprotocol.com) + +

community-numbers

+ +> Microservice to cache and expose community numbers for use throughout [oceanprotocol.com](https://oceanprotocol.com). + +[![Build Status](https://travis-ci.com/oceanprotocol/community-numbers.svg?branch=master)](https://travis-ci.com/oceanprotocol/community-numbers) +[![js oceanprotocol](https://img.shields.io/badge/js-oceanprotocol-7b1173.svg)](https://github.com/oceanprotocol/eslint-config-oceanprotocol) +[![Greenkeeper badge](https://badges.greenkeeper.io/oceanprotocol/community-numbers.svg)](https://greenkeeper.io/) + + + + +## API + +Endpoint: [`https://oceanprotocol-community.now.sh`](https://oceanprotocol-community.now.sh) + +### GET / + +**200**: Returns a list of network numbers as follows: + +```json +{ + "data": { + "github": { + "stars": 1000, + "repos": 1000 + }, + "medium": { + "followers": 1000 + }, + "bounties": { + "gitcoin": 1000, + "bountiesNetwork": 1000, + "total": 1000 + } + } +} + +``` + +## Development + +Install dependencies: + +```bash +npm install +``` + +And run the server: + +```bash +npm start +``` + +## Test + +Run the tests: + +```bash +npm test +``` + +## Deployment + +Deploy to [now](https://zeit.co/now), make sure to switch to Ocean Protocol org before deploying: + +```bash +# first run +now login +now switch + +# deploy +now +# switch alias to new deployment +now alias +``` + +## Authors + +- Matthias Kretschmann ([@kremalicious](https://github.com/kremalicious)) - [Ocean Protocol](https://oceanprotocol.com) diff --git a/index.js b/index.js new file mode 100644 index 0000000..0b44f6c --- /dev/null +++ b/index.js @@ -0,0 +1,40 @@ +const ms = require('ms') +const { logError } = require('./util/logger') + +const fetchGitHubRepos = require('./networks/github') +const fetchBounties = require('./networks/bounties') +const fetchMedium = require('./networks/medium') + +let cacheGithub = null +let cacheBounties = null +let cacheMedium = null + +// +// Create the response +// +module.exports = async (req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*') + res.setHeader('Access-Control-Allow-Methods', 'GET') + + try { + if (!cacheGithub || Date.now() - cacheGithub.lastUpdate > ms('5m')) { + cacheGithub = await fetchGitHubRepos() + } + + if (!cacheBounties || Date.now() - cacheBounties.lastUpdate > ms('5m')) { + cacheBounties = await fetchBounties() + } + + if (!cacheMedium || Date.now() - cacheMedium.lastUpdate > ms('5m')) { + cacheMedium = await fetchMedium() + } + } catch (error) { + logError(error.message) + } + + res.end(JSON.stringify({ + github: cacheGithub, + bounties: cacheBounties, + medium: cacheMedium + })) +} diff --git a/networks/bounties.js b/networks/bounties.js new file mode 100644 index 0000000..f545a0e --- /dev/null +++ b/networks/bounties.js @@ -0,0 +1,62 @@ +const fetch = require('node-fetch') +const { log, logError } = require('../util/logger') + +const getGitcoin = async () => { + const response = await fetch('https://gitcoin.co/api/v0.1/bounties/?&org=oceanprotocol&is_open=true') + + if (response.status !== 200) { + logError(`Non-200 response code from Gitcoin: ${response.status}`) + return null + } + + const gitcoin = await response.json() // returns only open bounties by default + + return gitcoin.length +} + +const getBountiesNetwork = async () => { + const response = await fetch('https://api.bounties.network/bounty/?search=ocean%20protocol&bountyStage=1&platform=bounties-network') + + if (response.status !== 200) { + logError(`Non-200 response code from Bounties Network: ${response.status}`) + return null + } + + const bountiesNetwork = await response.json() + + return bountiesNetwork.results.length +} + +const getTotal = async () => { + const response = await fetch('https://api.bounties.network/bounty/?search=ocean%20protocol') + + if (response.status !== 200) { + logError(`Non-200 response code from Bounties Network: ${response.status}`) + return null + } + + const allBounties = await response.json() + + return allBounties.count +} + +const fetchBounties = async () => { + const start = Date.now() + const gitcoin = await getGitcoin() + const bountiesNetwork = await getBountiesNetwork() + const total = await getTotal() + + log( + `Re-built bounties cache. ` + + `Total: ${total} bounties. ` + + `Elapsed: ${new Date() - start}ms` + ) + + return { + gitcoin, + bountiesNetwork, + total + } +} + +module.exports = fetchBounties diff --git a/networks/github.js b/networks/github.js new file mode 100755 index 0000000..461ff90 --- /dev/null +++ b/networks/github.js @@ -0,0 +1,51 @@ +const fetch = require('node-fetch') +const { log, logError } = require('../util/logger') + +// Request options for all fetch calls +const options = { + headers: { + // For getting topics, see note on https://developer.github.com/v3/search/ + // Accept: 'application/vnd.github.mercy-preview+json' + Accept: 'application/vnd.github.preview' + } +} + +const arrSum = arr => arr.reduce((a, b) => a + b, 0) + +// +// Fetch all public GitHub repos +// +const fetchGitHubRepos = async () => { + const url = 'https://api.github.com/orgs/oceanprotocol/repos?type=public&per_page=200' + const start = Date.now() + const response = await fetch(url, options) + + if (response.status !== 200) { + logError(`Non-200 response code from GitHub: ${response.status}`) + return null + } + + const json = await response.json() + + let numbers = [] + + json.map(item => { + if (item.stargazers_count) { + return numbers.push(item.stargazers_count) + } + return null + }) + + const stars = arrSum(numbers) + const repositories = json.length + + log( + `Re-built github cache. ` + + `Total: ${repositories} public projects with a total of ${stars} stargazers. ` + + `Elapsed: ${new Date() - start}ms` + ) + + return { stars, repositories } +} + +module.exports = fetchGitHubRepos diff --git a/networks/medium.js b/networks/medium.js new file mode 100644 index 0000000..82a97a1 --- /dev/null +++ b/networks/medium.js @@ -0,0 +1,29 @@ +const fetch = require('node-fetch') +const { log, logError } = require('../util/logger') + +const fetchMedium = async () => { + const url = 'https://medium.com/oceanprotocol?format=json' + const start = Date.now() + const response = await fetch(url) + + if (response.status !== 200) { + logError(`Non-200 response code from Medium: ${response.status}`) + return null + } + + const responseText = await response.text() + const json = await JSON.parse(responseText.replace('])}while(1);', '')) + const { collection } = json.payload + + const followers = collection.metadata.followerCount + + log( + `Re-built medium cache. ` + + `Total: ${followers} followers. ` + + `Elapsed: ${new Date() - start}ms` + ) + + return { followers } +} + +module.exports = fetchMedium diff --git a/now.json b/now.json new file mode 100644 index 0000000..3d56519 --- /dev/null +++ b/now.json @@ -0,0 +1,14 @@ +{ + "version": 2, + "name": "oceanprotocol-community", + "alias": "oceanprotocol-community.now.sh", + "env": { + "NODE_ENV": "production" + }, + "builds": [ + { + "src": "index.js", + "use": "@now/node" + } + ] +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..d1e5495 --- /dev/null +++ b/package.json @@ -0,0 +1,23 @@ +{ + "name": "@oceanprotocol/community-numbers", + "private": true, + "version": "1.0.0", + "main": "index.js", + "scripts": { + "start": "micro", + "dev": "micro-dev", + "test": "eslint --ignore-path .gitignore --ext .js,.jsx ." + }, + "dependencies": { + "chalk": "2.4.2", + "micro": "^9.3.4", + "ms": "^2.1.1", + "node-fetch": "2.5.0" + }, + "devDependencies": { + "eslint": "^5.16.0", + "eslint-config-oceanprotocol": "^1.3.0", + "eslint-plugin-node": "^9.0.1", + "micro-dev": "^3.0.0" + } +} diff --git a/util/logger.js b/util/logger.js new file mode 100644 index 0000000..c935a33 --- /dev/null +++ b/util/logger.js @@ -0,0 +1,7 @@ +/* eslint-disable no-console */ +const chalk = require('chalk') + +const log = text => console.log(text) +const logError = text => console.log(chalk.bold.red(text)) + +module.exports = { log, logError }