From 57b251554ed28a7fb3cf14b01be55e631b945769 Mon Sep 17 00:00:00 2001 From: Baoyuan Date: Mon, 23 Aug 2021 23:59:37 +0800 Subject: [PATCH 1/6] feat: change generate SVG function --- api-go/cmd/contributor/main.go | 2 +- api-go/internal/graph/graph.go | 112 +++++++++---------- api-go/internal/utils/utils.go | 2 +- api-go/tools/puppeteer/fetch.js | 79 +++++++++++++ api-go/tools/puppeteer/index.js | 92 ++++++++------- api-go/tools/puppeteer/package.json | 15 ++- api-go/tools/puppeteer/utils.js | 168 ++++++++++++++++++++++++++++ 7 files changed, 366 insertions(+), 104 deletions(-) create mode 100644 api-go/tools/puppeteer/fetch.js create mode 100644 api-go/tools/puppeteer/utils.js diff --git a/api-go/cmd/contributor/main.go b/api-go/cmd/contributor/main.go index fdf9150..c2fc50e 100644 --- a/api-go/cmd/contributor/main.go +++ b/api-go/cmd/contributor/main.go @@ -130,7 +130,7 @@ func getContributorSVG(w http.ResponseWriter, r *http.Request) { return } } - w.Header().Add("content-type", "image/svg+xml;charset=utf-8") + w.Header().Add("content-type", "image/png;charset=utf-8") w.Header().Add("cache-control", "public, max-age=86400") svg = strings.Replace(svg, "%", "%%", -1) diff --git a/api-go/internal/graph/graph.go b/api-go/internal/graph/graph.go index c0f88a3..23acd34 100644 --- a/api-go/internal/graph/graph.go +++ b/api-go/internal/graph/graph.go @@ -7,8 +7,6 @@ import ( "io" "io/ioutil" "net/http" - "strconv" - "strings" "time" "cloud.google.com/go/datastore" @@ -29,7 +27,7 @@ func GenerateAndSaveSVG(ctx context.Context, repo string, merge bool, chartType } defer client.Close() - graphFunctionUrl := "https://cloudfunction.contributor-graph.com/svg?repo=" + repo + graphFunctionUrl := "http://localhost:8081?repo=" + repo if merge { graphFunctionUrl += "&merge=true" } @@ -81,7 +79,7 @@ func GenerateAndSaveSVG(ctx context.Context, repo string, merge bool, chartType wc := client.Bucket(bucket).Object(object).NewWriter(ctx) wc.CacheControl = "public, max-age=86400" - wc.ContentType = "image/svg+xml;charset=utf-8" + wc.ContentType = "image/png" if _, err = io.Copy(wc, bytes.NewReader(svg)); err != nil { return "", fmt.Errorf("upload svg failed: io.Copy: %v", err) @@ -161,58 +159,58 @@ func SubGetSVG(w http.ResponseWriter, repo string, merge bool, charType string) // we need to also tell if the graph is ready to use on this side. // Try to get the endpoint of the line drawn and tell if it's on the right-most side func svgSucceed(svgBytes []byte) ([]byte, error) { - svg := string(svgBytes[:]) - lines := strings.Split(svg, "\n") - var svgWidth float64 - for _, l := range lines { - if strings.Contains(l, "= 0; i-- { - if strings.Contains(lines[i], renderLengthMarker) { - words := strings.Split(lines[i], " ") - svgWidthStr := fmt.Sprintf("%f", svgWidth) - for j := range words { - if words[j] == "L" && j+1 < len(words) && words[j+1] != svgWidthStr { - lines[i] = strings.ReplaceAll(lines[i], words[j+1], svgWidthStr) - break - } - } - return []byte(strings.Join(lines, "\n")), nil - } - } + // svg := string(svgBytes[:]) + // lines := strings.Split(svg, "\n") + // var svgWidth float64 + // for _, l := range lines { + // if strings.Contains(l, "= 0; i-- { + // if strings.Contains(lines[i], renderLengthMarker) { + // words := strings.Split(lines[i], " ") + // svgWidthStr := fmt.Sprintf("%f", svgWidth) + // for j := range words { + // if words[j] == "L" && j+1 < len(words) && words[j+1] != svgWidthStr { + // lines[i] = strings.ReplaceAll(lines[i], words[j+1], svgWidthStr) + // break + // } + // } + // return []byte(strings.Join(lines, "\n")), nil + // } + // } return svgBytes, nil } diff --git a/api-go/internal/utils/utils.go b/api-go/internal/utils/utils.go index 86c852b..7bf8e1c 100644 --- a/api-go/internal/utils/utils.go +++ b/api-go/internal/utils/utils.go @@ -71,7 +71,7 @@ func RepoNameToFileName(str string, merge bool, charType string) string { if charType == ContributorMonthlyActivity { filename = "monthly/" + filename } - return filename + ".svg" + return filename + ".png" } func FileNameToRepoName(str string) string { diff --git a/api-go/tools/puppeteer/fetch.js b/api-go/tools/puppeteer/fetch.js new file mode 100644 index 0000000..dd4260c --- /dev/null +++ b/api-go/tools/puppeteer/fetch.js @@ -0,0 +1,79 @@ +const moment = require('moment'); +const axios = require('axios'); + +const isSameDay = (d1, d2) => { + return ( + d1.getFullYear() === d2.getFullYear() && + d1.getMonth() === d2.getMonth() && + d1.getDate() === d2.getDate() + ); +}; + +const fetchData = (repo) => { + if (repo === "null" || repo === null) { + repo = "apache/apisix"; + } + return new Promise((resolve, reject) => { + axios.get( + `https://contributor-overtime-api.apiseven.com/contributors?repo=${repo}` + ).then(response => { + return response.data; + }).then(data => { + const { Contributors = [] } = data; + const sortContributors = Contributors.map(item => ({ + ...item, + date: item.date.substring(0, 10) + })).sort( + (a, b) => new Date(a.date).getTime() - new Date(b.date).getTime() + ); + if ( + !isSameDay( + new Date(sortContributors[sortContributors.length - 1].date), + new Date() + ) + ) { + sortContributors.push({ + repo, + idx: sortContributors[sortContributors.length - 1].idx, + date: moment(new Date()).format("YYYY-MM-DD") + }); + }; + + const processContributors = []; + sortContributors.forEach((item, index) => { + processContributors.push(item); + + if (index !== sortContributors.length - 1) { + const diffDays = moment(sortContributors[index + 1].date).diff( + item.date, + "days" + ); + if (diffDays > 1) { + for (let index = 1; index < diffDays; index++) { + processContributors.push({ + ...item, + date: moment(item.date) + .add(index, "days") + .format() + .substring(0, 10) + }); + } + } + } + }); + + const filterData = processContributors.filter( + (item, index) => + index === 0 || + index === processContributors.length - 1 || + new Date(item.date).getDate() % 10 === 5 + ); + + resolve({ repo, ...{ Contributors: filterData } }); + }) + }) +}; + +module.exports = { + fetchData, +} diff --git a/api-go/tools/puppeteer/index.js b/api-go/tools/puppeteer/index.js index 2159548..ff4c3c9 100644 --- a/api-go/tools/puppeteer/index.js +++ b/api-go/tools/puppeteer/index.js @@ -1,54 +1,66 @@ +const echarts = require("echarts"); +const { createCanvas } = require('canvas'); +const { Storage } = require('@google-cloud/storage'); +const { fetchData } = require('./fetch'); +const { updateSeries } = require('./utils'); + +const fs = require('fs'); + +const config = { + width: 896, + height: 550, +}; + /** * Responds to any HTTP request. * * @param {!express:Request} req HTTP request context. * @param {!express:Response} res HTTP response context. */ -const puppeteer = require('puppeteer'); -const querystring = require('querystring'); -exports.svg = async (req, res) => { +exports.png = (req, res) => { const repo = req.query.repo; const merge = req.query.merge; - const chart = req.query.chart; + const chartType = req.query.chart; - const browser = await puppeteer.launch({ - args: ['--no-sandbox',] + const storage = new Storage(); + const myBucket = storage.bucket('api7-301102.appspot.com'); + + const ctx = createCanvas(config.width, config.height); + echarts.setCanvasCreator(function () { + return ctx; }); - const page = await browser.newPage(); - await page.setViewport({ width: 1920, height: 1080 }); - - graphUrl = "https://contributor-graph-git-no-animation-apiseven.vercel.app/?repo=" + repo; - if (merge) { - graphUrl += "&merge=true" - } - if (chart) { - graphUrl += "&chart=" + chart - } - - await page.goto(graphUrl); - - var getSVG = function () { - return window.echartInstance.getDataURL(); - }; - - var svgReady = function () { - return window.echartsRenderFinished; - } - - function sleep(time) { - return new Promise((resolve) => setTimeout(resolve, time)); - } - - sleep(1000).then(() => { - var _flagCheck = setInterval(async function () { - if (await page.evaluate(svgReady) === true) { - clearInterval(_flagCheck); - var image = await page.evaluate(getSVG); - res.set('Content-Type', 'image/svg+xml'); - res.send(querystring.unescape(image).split("data:image/svg+xml;charset=UTF-8,")[1]); - await browser.close(); + let repoList = [repo]; + Promise.all(repoList.map(item => fetchData(item))).then(data => { + const tmpDataSouce = {}; + data.forEach(item => { + const { Contributors = [], repo } = item; + const data = Contributors.map(item => ({ + repo, + contributorNum: item.idx, + date: item.date + })); + + if (!tmpDataSouce[item.repo]) { + tmpDataSouce[repo] = data; } - }, 100); + }); + + const option = updateSeries(["1970-01-01"], tmpDataSouce); + let chart = echarts.init(createCanvas(config.width, config.height)); + chart.setOption(option, true); + const base64 = chart.getDom().toDataURL(); + + res.set('Content-Type', 'image/png'); + res.send(base64); + // file.save(buffer, (err) => { + // if (err) { + // console.log('save file error'); + // throw err; + // } else { + // console.log(`return filename ${fileName} ok ~`); + // res.status(200).send(fileName); + // } + // }); }); }; diff --git a/api-go/tools/puppeteer/package.json b/api-go/tools/puppeteer/package.json index c9987eb..62521c2 100644 --- a/api-go/tools/puppeteer/package.json +++ b/api-go/tools/puppeteer/package.json @@ -1,7 +1,12 @@ { - "name": "sample-http", - "version": "0.0.1", - "dependencies": { - "puppeteer": "8.0.0" - } + "name": "sample-http", + "version": "0.0.1", + "dependencies": { + "@google-cloud/storage": "^5.13.1", + "axios": "^0.21.1", + "canvas": "^2.8.0", + "echarts": "5.1.2", + "lodash.clonedeep": "4.5.0", + "moment": "^2.29.1" + } } diff --git a/api-go/tools/puppeteer/utils.js b/api-go/tools/puppeteer/utils.js new file mode 100644 index 0000000..108bae9 --- /dev/null +++ b/api-go/tools/puppeteer/utils.js @@ -0,0 +1,168 @@ +const cloneDeep = require('lodash.clonedeep'); +const echarts = require("echarts"); + +const DEFAULT_COLOR = "#39a85a"; + +const generateDefaultOption = () => { + return { + color: ["#39a85a", "#4385ee", "#fabc37", "#2dc1dd", "#f972cf", "#8331c8"], + legend: { + top: "5%", + data: [], + textStyle: { + fontSize: 14 + } + }, + toolbox: { + feature: { + myShare: { + show: true, + title: "Share", + icon: "path://M830.506667 642.688c-57.344 0-104.192 30.72-126.037334 78.336l-277.162666-117.888c16.170667-25.045333 25.045333-55.722667 25.045333-88.832a167.253333 167.253333 0 0 0-4.864-40.362667l176.981333-137.301333c23.466667 17.749333 53.333333 28.245333 86.485334 28.245333 80 0 139.776-59.733333 139.776-138.88S790.912 87.04 710.954667 87.04c-80 0-139.008 59.733333-139.008 138.922667 0 20.181333 4.053333 38.741333 10.496 54.912L419.2 406.101333c-32.298667-48.469333-85.632-82.389333-146.218667-82.389333a178.944 178.944 0 0 0-179.413333 178.474667v0.853333a178.944 178.944 0 0 0 179.413333 179.2c37.12 0 71.893333-9.642667 100.181334-27.392l318.378666 135.68c4.053333 75.093333 62.250667 130.816 138.965334 130.816 80.042667 0 139.008-59.733333 139.008-138.922667 0-79.146667-59.818667-139.733333-138.965334-139.733333z", + onclick: function () { + handleShareClick(); + } + } + } + }, + dataset: [], + title: { + text: "Contributor Over Time" + }, + tooltip: { + trigger: "axis" + }, + xAxis: { + type: "time", + nameLocation: "middle", + axisLabel: { + show: true, + textStyle: { + fontSize: 14 + }, + rotate: 45 + } + }, + yAxis: { + name: "", + axisLabel: { + show: true, + textStyle: { + fontSize: 14 + } + } + }, + series: [], + grid: { + x: 10, + x2: 15, + y: 80, + containLabel: true, + y2: 5 + } + }; +}; + +const updateSeries = (passXAxis, dataSource) => { + const newClonedOption = cloneDeep( + generateDefaultOption() + ); + const datasetWithFilters = [ + ["ContributorNum", "Repo", "Date", "DateValue"] + ]; + const legend = []; + const limitDate = new Date(passXAxis[0]).getTime(); + Object.entries(dataSource).forEach(([key, value]) => { + legend.push(key); + value.forEach(item => { + datasetWithFilters.push([ + item.contributorNum, + item.repo, + item.date, + new Date(item.date).getTime() + ]); + }); + }); + + const newDateSet = datasetWithFilters.sort( + (a, b) => new Date(a[2]) - new Date(b[2]) + ); + + const filterDataset = legend.map(item => ({ + id: item, + fromDatasetId: "dataset_raw", + transform: { + type: "filter", + config: { + and: [ + { dimension: "Repo", "=": item }, + { dimension: "DateValue", gte: limitDate } + ] + } + } + })); + + const series = legend.map(item => ({ + name: item, + type: "line", + datasetId: item, + showSymbol: false, + smooth: true, + encode: { + x: "Date", + y: "ContributorNum", + itemName: "Repo", + tooltip: ["ContributorNum"] + } + })); + + if (series.length === 1) { + series[0].areaStyle = { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: DEFAULT_COLOR + "80" + }, + { + offset: 1, + color: DEFAULT_COLOR + "00" + } + ]) + }; + series[0].itemStyle = { + normal: { + color: DEFAULT_COLOR, + lineStyle: { + color: DEFAULT_COLOR + } + } + }; + } + + newClonedOption.dataset = [ + { + id: "dataset_raw", + source: newDateSet + } + ].concat(filterDataset); + + newClonedOption.series = series; + newClonedOption.legend.data = legend; + return newClonedOption; +}; + +const repoNameToFileName = (repo, merge, charType) => { + let fileName = repo; + if (merge) { + fileName = 'merge/' + fileName; + } + if (charType === 'contributorMonthlyActivity') { + fileName = "monthly/" + fileName; + } + return fileName + ".png"; +}; + +module.exports = { + updateSeries, + repoNameToFileName, +} From 47145198ca199593c99d0c9d5b5c447603f88eb8 Mon Sep 17 00:00:00 2001 From: Baoyuan Date: Tue, 24 Aug 2021 14:44:34 +0800 Subject: [PATCH 2/6] fix: change wrong return --- .gitignore | 1 + api-go/cmd/contributor/main.go | 16 ++++++++++++---- api-go/tools/puppeteer/fetch.js | 2 ++ api-go/tools/puppeteer/index.js | 2 ++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 0c45ee7..c8fb3cf 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ /node_modules /.pnp .pnp.js +/api-go/tools/puppeteer/node_modules # testing /coverage diff --git a/api-go/cmd/contributor/main.go b/api-go/cmd/contributor/main.go index c2fc50e..fe06d3a 100644 --- a/api-go/cmd/contributor/main.go +++ b/api-go/cmd/contributor/main.go @@ -2,8 +2,8 @@ package main import ( "context" + "encoding/base64" "encoding/json" - "fmt" "log" "net/http" "strings" @@ -130,11 +130,19 @@ func getContributorSVG(w http.ResponseWriter, r *http.Request) { return } } - w.Header().Add("content-type", "image/png;charset=utf-8") + w.Header().Add("content-type", "image/png") w.Header().Add("cache-control", "public, max-age=86400") - svg = strings.Replace(svg, "%", "%%", -1) - fmt.Fprintf(w, svg) + base64String := strings.Split(svg, "data:image/png;base64,")[1] + buffer, err := base64.StdEncoding.DecodeString(base64String) + + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode(err.Error()) + return + } + + w.Write(buffer) } func getRepos(w http.ResponseWriter, r *http.Request) { diff --git a/api-go/tools/puppeteer/fetch.js b/api-go/tools/puppeteer/fetch.js index dd4260c..28a92ef 100644 --- a/api-go/tools/puppeteer/fetch.js +++ b/api-go/tools/puppeteer/fetch.js @@ -70,6 +70,8 @@ const fetchData = (repo) => { ); resolve({ repo, ...{ Contributors: filterData } }); + }).catch(error => { + reject(error); }) }) }; diff --git a/api-go/tools/puppeteer/index.js b/api-go/tools/puppeteer/index.js index ff4c3c9..b627efd 100644 --- a/api-go/tools/puppeteer/index.js +++ b/api-go/tools/puppeteer/index.js @@ -62,5 +62,7 @@ exports.png = (req, res) => { // res.status(200).send(fileName); // } // }); + }).catch(error => { + console.log(`generate file of ${repo} error`, error); }); }; From 66230e6b64e3eca993a22d151d053288e3c1fd78 Mon Sep 17 00:00:00 2001 From: Baoyuan Date: Tue, 24 Aug 2021 20:58:01 +0800 Subject: [PATCH 3/6] fix: Support multiple repo --- api-go/tools/puppeteer/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api-go/tools/puppeteer/index.js b/api-go/tools/puppeteer/index.js index b627efd..46a3870 100644 --- a/api-go/tools/puppeteer/index.js +++ b/api-go/tools/puppeteer/index.js @@ -30,7 +30,7 @@ exports.png = (req, res) => { echarts.setCanvasCreator(function () { return ctx; }); - let repoList = [repo]; + let repoList = repo.split(","); Promise.all(repoList.map(item => fetchData(item))).then(data => { const tmpDataSouce = {}; data.forEach(item => { From 5978c6f867e431afa5effbf8163b00acfac1bfab Mon Sep 17 00:00:00 2001 From: Baoyuan Date: Tue, 24 Aug 2021 23:39:07 +0800 Subject: [PATCH 4/6] docs: add Development doc --- README.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/README.md b/README.md index 131f793..320d11f 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,53 @@ We maintain a [list](api-go/config/multi-repo.md) which you could directly add y [![Monthly Active Contributors](https://contributor-overtime-api.apiseven.com/contributors-svg?chart=contributorMonthlyActivity&repo=apache/apisix&merge=true)](https://www.apiseven.com/en/contributor-graph?chart=contributorMonthlyActivity&repo=apache/apisix&merge=true) +## Development + +The current project uses Google Cloud deployment, to develop and debug locally, follow the steps below: + +This project depends on `Golang` and `Node.js`, please make sure you have the corresponding environment. + +Because the read and write functions of Google Clould DataStore and Google Clould DataStorage are used in the project, you need to obtain the corresponding permissions of this. + +It is recommended to download the key file of Google Clould in json format, and then set the environment variable locally: + +``` +GOOGLE_APPLICATION_CREDENTIALS=/The/path/of/json/key/file +``` + +1. Clone the project. + +``` +git clone https://github.com/api7/contributor-graph.git +``` + +2. Start the front end. + +``` +cd contributor-graph +yarn +yarn dev +``` + +3. Start API Server. + +``` +cd api-go +go run ./cmd/contributor/main.go +``` + +4. Start Google Cloud Function locally. + +This step depends on [@google-cloud/functions-framework](https://www.npmjs.com/package/@google-cloud/functions-framework) tool, please install the tool first. + +``` +cd ./api-go/tools/puppeteer +yarn +npx @google-cloud/functions-framework --target=png +``` + +Then config this address as Clould Function Trigger link in `api-go`. + ## Feature request If you have any requests, including but not limited to: From cb8b77b5a960ae8b19c3b0d3eba93f29f9c03195 Mon Sep 17 00:00:00 2001 From: Baoyuan Date: Sun, 29 Aug 2021 16:14:44 +0800 Subject: [PATCH 5/6] feat: support both contributorOverTime & contributorMonthlyActivity --- api-go/cmd/contributor/main.go | 30 ++++++++++------ api-go/tools/puppeteer/README.md | 13 +++++++ api-go/tools/puppeteer/fetch.js | 61 ++++++++++++++++++++++++++++++-- api-go/tools/puppeteer/index.js | 44 +++++++++++------------ 4 files changed, 114 insertions(+), 34 deletions(-) create mode 100644 api-go/tools/puppeteer/README.md diff --git a/api-go/cmd/contributor/main.go b/api-go/cmd/contributor/main.go index fe06d3a..38383db 100644 --- a/api-go/cmd/contributor/main.go +++ b/api-go/cmd/contributor/main.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "encoding/json" + "fmt" "log" "net/http" "strings" @@ -130,19 +131,28 @@ func getContributorSVG(w http.ResponseWriter, r *http.Request) { return } } - w.Header().Add("content-type", "image/png") - w.Header().Add("cache-control", "public, max-age=86400") - base64String := strings.Split(svg, "data:image/png;base64,")[1] - buffer, err := base64.StdEncoding.DecodeString(base64String) + if strings.Contains(svg, "svg") { + w.Header().Add("content-type", "image/svg+xml;charset=utf-8") + w.Header().Add("cache-control", "public, max-age=86400") - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - json.NewEncoder(w).Encode(err.Error()) - return - } + svg = strings.Replace(svg, "%", "%%", -1) + fmt.Fprintf(w, svg) + } else { + w.Header().Add("content-type", "image/png") + w.Header().Add("cache-control", "public, max-age=86400") + + base64String := strings.Split(svg, "data:image/png;base64,")[1] + buffer, err := base64.StdEncoding.DecodeString(base64String) - w.Write(buffer) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + json.NewEncoder(w).Encode(err.Error()) + return + } + + w.Write(buffer) + } } func getRepos(w http.ResponseWriter, r *http.Request) { diff --git a/api-go/tools/puppeteer/README.md b/api-go/tools/puppeteer/README.md new file mode 100644 index 0000000..0c90836 --- /dev/null +++ b/api-go/tools/puppeteer/README.md @@ -0,0 +1,13 @@ +# Google Clould Function + +Google Clould Function used to generate contributor statistics picture. + +## API + +The entry point of the function is `png` in `index.js`. + +| Parameter | Required | Type | Description | Example | +| ---- | ---- | ---- | ---- | ---- | +| repo | true | string | The name of repository | apache/apisix,apache/skywalking | +| merge | false | boolean | Whether to view all repos related to this repo, when chart is `contributorMonthlyActivity`, can not be set true | true | +| chart | false | contributorOverTime contributorMonthlyActivity | chart type | | diff --git a/api-go/tools/puppeteer/fetch.js b/api-go/tools/puppeteer/fetch.js index 28a92ef..94e113b 100644 --- a/api-go/tools/puppeteer/fetch.js +++ b/api-go/tools/puppeteer/fetch.js @@ -9,7 +9,7 @@ const isSameDay = (d1, d2) => { ); }; -const fetchData = (repo) => { +const fetchContributorsData = (repo) => { if (repo === "null" || repo === null) { repo = "apache/apisix"; } @@ -76,6 +76,63 @@ const fetchData = (repo) => { }) }; +const fetchMonthlyData = (repo) => { + if (repo === "null" || repo === null) { + repo = "apache/apisix"; + } + return new Promise((resolve, reject) => { + axios.get( + `https://contributor-overtime-api.apiseven.com/monthly-contributor?repo=${repo}` + ) + .then(response => { + return response.data; + }) + .then(myJson => { + resolve({ repo, ...myJson }); + }) + .catch(e => { + reject(e); + }); + }); +}; + +const fetchMergeContributor = (repo) => { + return new Promise((resolve, reject) => { + axios.get( + `https://contributor-overtime-api.apiseven.com/contributors-multi?repo=${repo.join( + "," + )}` + ) + .then(response => { + if (!response.ok) { + let message = ""; + switch (response.status) { + case 403: + message = "Hit rate limit"; + break; + case 404: + message = "Repo format error / Repo not found"; + break; + default: + message = "Request Error"; + break; + } + throw message; + } + return response.json(); + }) + .then(myJson => { + console.log('myJson: ', myJson); + resolve({ repo, ...myJson }); + }) + .catch(e => { + reject(); + }); + }); +}; + module.exports = { - fetchData, + fetchContributorsData, + fetchMonthlyData, + fetchMergeContributor, } diff --git a/api-go/tools/puppeteer/index.js b/api-go/tools/puppeteer/index.js index 46a3870..f65d916 100644 --- a/api-go/tools/puppeteer/index.js +++ b/api-go/tools/puppeteer/index.js @@ -1,11 +1,8 @@ const echarts = require("echarts"); const { createCanvas } = require('canvas'); -const { Storage } = require('@google-cloud/storage'); -const { fetchData } = require('./fetch'); +const { fetchContributorsData, fetchMonthlyData } = require('./fetch'); const { updateSeries } = require('./utils'); -const fs = require('fs'); - const config = { width: 896, height: 550, @@ -23,23 +20,35 @@ exports.png = (req, res) => { const merge = req.query.merge; const chartType = req.query.chart; - const storage = new Storage(); - const myBucket = storage.bucket('api7-301102.appspot.com'); - const ctx = createCanvas(config.width, config.height); echarts.setCanvasCreator(function () { return ctx; }); let repoList = repo.split(","); - Promise.all(repoList.map(item => fetchData(item))).then(data => { + Promise.all(repoList.map(item => { + if (chartType === "contributorMonthlyActivity") { + return fetchMonthlyData(item); + }; + return fetchContributorsData(item); + })).then(data => { const tmpDataSouce = {}; data.forEach(item => { const { Contributors = [], repo } = item; - const data = Contributors.map(item => ({ - repo, - contributorNum: item.idx, - date: item.date - })); + const data = Contributors.map(item => { + if (chartType === "contributorMonthlyActivity") { + return { + repo, + contributorNum: item.Num, + date: item.Month + } + } else { + return { + repo, + contributorNum: item.idx, + date: item.date + } + } + }); if (!tmpDataSouce[item.repo]) { tmpDataSouce[repo] = data; @@ -53,15 +62,6 @@ exports.png = (req, res) => { res.set('Content-Type', 'image/png'); res.send(base64); - // file.save(buffer, (err) => { - // if (err) { - // console.log('save file error'); - // throw err; - // } else { - // console.log(`return filename ${fileName} ok ~`); - // res.status(200).send(fileName); - // } - // }); }).catch(error => { console.log(`generate file of ${repo} error`, error); }); From 6e8b69ff282567086ac95c5b1b4c0295d79d4976 Mon Sep 17 00:00:00 2001 From: Baoyuan Date: Sun, 29 Aug 2021 22:11:33 +0800 Subject: [PATCH 6/6] feat: switch clould function url --- api-go/internal/graph/graph.go | 2 +- api-go/tools/puppeteer/fetch.js | 19 ++----------------- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/api-go/internal/graph/graph.go b/api-go/internal/graph/graph.go index 23acd34..3f62c8e 100644 --- a/api-go/internal/graph/graph.go +++ b/api-go/internal/graph/graph.go @@ -27,7 +27,7 @@ func GenerateAndSaveSVG(ctx context.Context, repo string, merge bool, chartType } defer client.Close() - graphFunctionUrl := "http://localhost:8081?repo=" + repo + graphFunctionUrl := "https://asia-east2-api7-301102.cloudfunctions.net/png" + repo if merge { graphFunctionUrl += "&merge=true" } diff --git a/api-go/tools/puppeteer/fetch.js b/api-go/tools/puppeteer/fetch.js index 94e113b..ede9244 100644 --- a/api-go/tools/puppeteer/fetch.js +++ b/api-go/tools/puppeteer/fetch.js @@ -104,29 +104,14 @@ const fetchMergeContributor = (repo) => { )}` ) .then(response => { - if (!response.ok) { - let message = ""; - switch (response.status) { - case 403: - message = "Hit rate limit"; - break; - case 404: - message = "Repo format error / Repo not found"; - break; - default: - message = "Request Error"; - break; - } - throw message; - } - return response.json(); + return response.data; }) .then(myJson => { console.log('myJson: ', myJson); resolve({ repo, ...myJson }); }) .catch(e => { - reject(); + reject(e); }); }); };