diff --git a/app/components/github/contributorslist.js b/app/components/github/contributorslist.js index fc4b18a5..a7556506 100644 --- a/app/components/github/contributorslist.js +++ b/app/components/github/contributorslist.js @@ -1,8 +1,14 @@ -import Image from 'next/image'; +import Image from "next/image"; const ContributorsList = (props) => { - - let contributors = props.data.contributors.Contributors; + let contributors = []; + if ( + props.data && + props.data.contributors && + Array.isArray(props.data.contributors.Contributors) + ) { + contributors = props.data.contributors.Contributors; + } return (
diff --git a/app/components/github/githubissueslist.js b/app/components/github/githubissueslist.js index b1bcce51..c7da1613 100644 --- a/app/components/github/githubissueslist.js +++ b/app/components/github/githubissueslist.js @@ -1,8 +1,8 @@ -import { Col, NavLink, Row } from 'react-bootstrap'; -import LikeIcon from '../../public/svg/like'; -import CommentIcon from '../../public/svg/comment'; -import IssueIcon from '../../public/svg/issue'; -import styles from '../../styles/GithubIssuesList.module.css'; +import { Col, NavLink, Row } from "react-bootstrap"; +import LikeIcon from "../../public/svg/like"; +import CommentIcon from "../../public/svg/comment"; +import IssueIcon from "../../public/svg/issue"; +import styles from "../../styles/GithubIssuesList.module.css"; const GithubIssue = ({ issue }) => { return ( @@ -17,12 +17,14 @@ const GithubIssue = ({ issue }) => { {issue.state} - #{issue.number} + + #{issue.number} + - {issue.reactions['+1']} + {issue.reactions["+1"]} @@ -36,10 +38,17 @@ const GithubIssue = ({ issue }) => { }; const GithubIssuesList = (props) => { - const data = - props.data.issues.Issues.length > 10 - ? props.data.issues.Issues.slice(0, 10) - : props.data.issues.Issues; + let data = []; + if ( + props.data && + props.data.issues && + Array.isArray(props.data.issues.Issues) + ) { + data = + props.data.issues.Issues.length > 10 + ? props.data.issues.Issues.slice(0, 10) + : props.data.issues.Issues; + } return (
{ return ( @@ -41,10 +41,13 @@ const GithubPullReqeust = ({ pull }) => { ); }; const GithubPullRequestsList = (props) => { - const data = - props.data.pulls.pulls.length > 6 - ? props.data.pulls.pulls.slice(0, 6) - : props.data.pulls.pulls; + let data = []; + if (props.data && props.data.pulls && Array.isArray(props.data.pulls.pulls)) { + data = + props.data.pulls.pulls.length > 6 + ? props.data.pulls.pulls.slice(0, 6) + : props.data.pulls.pulls; + } return (
{ +const GithubRepo = ({ data }) => { return ( -
- - - {data.full_name} - - - - - - - {data.open_issues_count} - - - - - - {data.stargazers_count} - - - - - - {data.forks_count} - - - { - (Array.isArray(data.topics) && (data.topics.length > 0 )) && - ( - + {data && ( + + + {data.full_name} + + + + + + + {data.open_issues_count} + + + + + + {data.stargazers_count} + + + + + + {data.forks_count} + + + {Array.isArray(data.topics) && data.topics.length > 0 && ( + {data.topics.map((topic) => { - return ( - - {topic} - - ) + return ( + + + {topic} + + + ); })} - ) - } - - - {data.description} - - - + )} + + {data.description} + + + )}
); }; diff --git a/app/components/gitlab/gitlabIssuesList.js b/app/components/gitlab/gitlabIssuesList.js new file mode 100644 index 00000000..bb94ca9d --- /dev/null +++ b/app/components/gitlab/gitlabIssuesList.js @@ -0,0 +1,66 @@ +import { Col, NavLink, Row } from "react-bootstrap"; +import LikeIcon from "../../public/svg/like"; +import DislikeIcon from "../../public/svg/dislike"; +import IssueIcon from "../../public/svg/issue"; +import styles from "../../styles/GitlabIssuesList.module.css"; + +const GitLabIssue = ({ issue }) => { + return ( + + + {issue.title} + + + + + + + {issue.state} + + + #{issue.iid} + + + + + + {issue.upvotes} + + + + + + {issue.downvotes} + + + + ); +}; + +const GitLabIssuesList = (props) => { + let data = []; + if ( + props.data && + props.data.issues && + Array.isArray(props.data.issues.issues) + ) { + data = + props.data.issues.issues.length > 10 + ? props.data.issues.issues.slice(0, 10) + : props.data.issues.issues; + } + + return ( +
+ {Array.isArray(data) ? ( + data.map((issue) => ) + ) : ( +

ERROR

+ )} +
+ ); +}; + +export default GitLabIssuesList; diff --git a/app/components/gitlab/gitlabMembersList.js b/app/components/gitlab/gitlabMembersList.js new file mode 100644 index 00000000..68e5ddc6 --- /dev/null +++ b/app/components/gitlab/gitlabMembersList.js @@ -0,0 +1,30 @@ +import Image from "next/image"; + +const MembersList = (props) => { + let contributors = []; + if ( + props.data && + props.data.members && + Array.isArray(props.data.members.members) + ) { + contributors = props.data.members.members; + } + + return ( +
+ {Array.isArray(contributors) && + contributors.map((contributor) => ( + + + + ))} +
+ ); +}; + +export default MembersList; diff --git a/app/components/gitlab/gitlabMergeRequestList.js b/app/components/gitlab/gitlabMergeRequestList.js new file mode 100644 index 00000000..4a08e2d8 --- /dev/null +++ b/app/components/gitlab/gitlabMergeRequestList.js @@ -0,0 +1,76 @@ +import { Col, NavLink, Row } from "react-bootstrap"; +import styles from "../../styles/GitlabMergeList.module.css"; +import Image from "next/image"; +import PullsIcon from "../../public/svg/pull"; + +const GitlabMergeReqeust = ({ mergeRequest }) => { + return ( + + + + + + + + + + {mergeRequest.author.name} + {" | "} + {`@${mergeRequest.author.username}`} + + + + + {mergeRequest.title} + + + + + + + {mergeRequest.state} + + + #{mergeRequest.iid} + + + + ); +}; +const GitlabMergeRequestsList = (props) => { + let data = []; + if ( + props.data && + props.data.merges && + Array.isArray(props.data.merges.merges) + ) { + data = + props.data.merges.merges.length > 6 + ? props.data.merges.merges.slice(0, 6) + : props.data.merges.merges; + } + + return ( +
+ {Array.isArray(data) ? ( + data.map((mergeRequest) => ( + + )) + ) : ( +

ERROR

+ )} +
+ ); +}; + +export default GitlabMergeRequestsList; diff --git a/app/components/gitlab/gitlabProject.js b/app/components/gitlab/gitlabProject.js new file mode 100644 index 00000000..59c96eff --- /dev/null +++ b/app/components/gitlab/gitlabProject.js @@ -0,0 +1,74 @@ +import { Col, Row, Badge } from "react-bootstrap"; +import Image from "next/image"; +import IssueIcon from "../../public/svg/issue"; +import StarIcon from "../../public/svg/star"; +import ForkIcon from "../../public/svg/forks"; +import styles from "../../styles/GitlabProject.module.css"; + +const GitlabProject = ({ data }) => { + return ( +
+ {data && ( + + + + + + + + + {data.full_name} + + + + + + + + {data.open_issues_count} + + + + + + {data.star_count} + + + + + + {data.forks_count} + + + {Array.isArray(data.topics) && data.topics.length > 0 && ( + + {data.topics.map((topic) => { + return ( + + + {topic} + + + ); + })} + + )} + + {data.description} + + + )} +
+ ); +}; + +export default GitlabProject; diff --git a/app/components/gitlab/index.js b/app/components/gitlab/index.js new file mode 100644 index 00000000..23a8241f --- /dev/null +++ b/app/components/gitlab/index.js @@ -0,0 +1,17 @@ +import GitlabIssuesList from "./gitlabIssuesList"; +import MembersList from "./gitlabMembersList"; +import GitlabMergeList from "./gitlabMergeRequestList"; +import GitlabProject from "./gitlabProject"; + +const Gitlab = (props) => { + if(props.type === 'issues'){ + return (); + }else if(props.type === 'merges'){ + return() + }else if(props.type === 'members'){ + return (); + } + return (); +}; + +export default Gitlab; \ No newline at end of file diff --git a/app/lib/gitlab.js b/app/lib/gitlab.js new file mode 100644 index 00000000..4c3207c1 --- /dev/null +++ b/app/lib/gitlab.js @@ -0,0 +1,30 @@ +import { fetchAPI } from "./api"; + +export const gitlabKitData = async (project_id , needed) => { + try { + const res = await fetchAPI("/gitlab-repositories"); + const databaseMap = { + "issues" : "gitlab_issue", + "merges" : "gitlab_merge_request", + "members" : "gitlab_member" + }; + let neededRepository = new Object(); + res.forEach((repo) => { + if (repo.project_id === project_id) { + neededRepository["id"] = repo.id; + neededRepository["project_id"] = repo.project_id; + neededRepository["project_data"] = repo.project_data; + if(Array.isArray(needed)){ + needed.forEach((neededData) => { + neededRepository[neededData] = repo[databaseMap[neededData]]; + }); + }else if(typeof needed !== 'undefined'){ + neededRepository[needed] = repo[needed]; + } + } + }); + return neededRepository; + } catch (error) { + console.log(error); + } +}; \ No newline at end of file diff --git a/app/next.config.js b/app/next.config.js index 5c53b9fb..5ea9ac00 100644 --- a/app/next.config.js +++ b/app/next.config.js @@ -6,7 +6,7 @@ module.exports = { includePaths: [path.join(__dirname, 'styles')], }, images: { - domains: ['global-uploads.webflow.com', 'avatars.githubusercontent.com', 'open.rocket.chat', 'media-exp1.licdn.com'] + domains: ['global-uploads.webflow.com', 'avatars.githubusercontent.com', 'open.rocket.chat', 'media-exp1.licdn.com', 'secure.gravatar.com', 'gitlab.com'] }, webpack: (config, { isServer }) => { // Fixes npm packages that depend on `fs` module @@ -17,4 +17,4 @@ module.exports = { } return config } -} +} \ No newline at end of file diff --git a/app/pages/gitlab/index.js b/app/pages/gitlab/index.js new file mode 100644 index 00000000..0e0a9eaa --- /dev/null +++ b/app/pages/gitlab/index.js @@ -0,0 +1,85 @@ +import Head from "next/head"; +import styles from "../../styles/GitlabPage.module.css"; +import { Container, Col } from "react-bootstrap"; +import { getNavItems } from "../../lib/navbar"; +import GitlabLogo from '../../public/svg/gitlablogo'; +import Gitlab from "../../components/gitlab"; +import { gitlabKitData } from "../../lib/gitlab"; + +export default function GitlabComponentKitPage(props) { + return ( +
+ + Gitab Components Kit + + + + + + +

+
+ Gitlab Components Kit +

+

+ Showcase your Gitlab Projects +

+ +
+

+ Project Overview +

+ +
+ +
+

+ Gitlab Issues +

+ +
+ +
+

+ Gitlab Merge Requests +

+ +
+ +
+

+ Project Members ✨ +

+ +
+
+
+ ); +} + +export async function getStaticProps({ params }) { + const topNavItems = await getNavItems(); + const gitlabData = await gitlabKitData( "3472737" , [ + "issues", + "members", + "merges", + ]); + + return { + props: { + topNavItems, + gitlabData, + }, + revalidate: 120, + }; +} \ No newline at end of file diff --git a/app/public/svg/dislike.js b/app/public/svg/dislike.js new file mode 100644 index 00000000..77e95228 --- /dev/null +++ b/app/public/svg/dislike.js @@ -0,0 +1,15 @@ +export default function dislike() { + return ( + + + + ); + } + \ No newline at end of file diff --git a/app/public/svg/gitlablogo.js b/app/public/svg/gitlablogo.js new file mode 100644 index 00000000..fa319b9c --- /dev/null +++ b/app/public/svg/gitlablogo.js @@ -0,0 +1,36 @@ +export default function gitlabLogo() { + return ( + + + + + + + + + ); + } + \ No newline at end of file diff --git a/app/public/svg/star.js b/app/public/svg/star.js index 1ae46960..7859d4de 100644 --- a/app/public/svg/star.js +++ b/app/public/svg/star.js @@ -45,5 +45,4 @@ export default function star() { ); -} - \ No newline at end of file +} \ No newline at end of file diff --git a/app/styles/GitlabIssuesList.module.css b/app/styles/GitlabIssuesList.module.css new file mode 100644 index 00000000..d66a5dce --- /dev/null +++ b/app/styles/GitlabIssuesList.module.css @@ -0,0 +1,26 @@ +.container { + max-width: 90rem; + } + .item_container { + width: 80vw; + } + .numbers { + font-size: clamp(8px, 3.6vw, 16px); + } + .column { + background: transparent; + border: 1px solid #dedede; + max-width: 40rem; + } + .md_container { + width: 80vw; + font-size: 0.87rem; + } + @media (min-width: 768px) { + .item_container { + width: clamp(18rem, 36vw, 36rem); + } + .md_container { + width: clamp(18rem, 36vw, 36rem); + } + } \ No newline at end of file diff --git a/app/styles/GitlabMergeList.module.css b/app/styles/GitlabMergeList.module.css new file mode 100644 index 00000000..beaeeb47 --- /dev/null +++ b/app/styles/GitlabMergeList.module.css @@ -0,0 +1,30 @@ +.container { + max-width: 90rem; + } + .item_container { + width: 80vw; + } + .numbers { + font-size: clamp(8px, 3.6vw, 16px); + } + .column { + background: transparent; + border: 1px solid #dedede; + max-width: 40rem; + } + .md_container { + width: 80vw; + font-size: 0.87rem; + } + @media (min-width: 768px) { + .item_container { + width: clamp(18rem, 36vw, 36rem); + } + .md_container { + width: clamp(18rem, 36vw, 36rem); + } + } + + .username a{ + text-decoration: none; + } \ No newline at end of file diff --git a/app/styles/GitlabPage.module.css b/app/styles/GitlabPage.module.css new file mode 100644 index 00000000..8d300786 --- /dev/null +++ b/app/styles/GitlabPage.module.css @@ -0,0 +1,34 @@ +.redText { + color: #f5455c; + } + + .infotiles { + display: flex; + flex-wrap: wrap; + align-items: center; + justify-content: center; + max-width: 38rem; + } + + .hero_heading { + color: #030c1a; + } + + .hero_subheading { + color: #87898d; + font-size: clamp(16px, 4.5vw, 20px); + } + + .community_news { + background-color: #f7f8fa; + width: 98vw; + overflow: hidden; + } + + .title { + font-size: clamp(22px, 4.6vw, 30px); + } + + .form { + width: 90%; + } \ No newline at end of file diff --git a/app/styles/GitlabProject.module.css b/app/styles/GitlabProject.module.css new file mode 100644 index 00000000..d462e59a --- /dev/null +++ b/app/styles/GitlabProject.module.css @@ -0,0 +1,28 @@ +.container { + max-width: 90rem; + } + .item_container { + width: 80vw; + } + .numbers { + font-size: clamp(8px, 3.6vw, 16px); + } + .column { + border: 1px solid #dedede; + max-width: 40rem; + } + .md_container { + width: 80vw; + font-size: 0.87rem; + } + @media (min-width: 768px) { + .item_container { + width: clamp(18rem, 36vw, 36rem); + } + .md_container { + width: clamp(18rem, 36vw, 36rem); + } + } + .project_name a{ + text-decoration: none; + } \ No newline at end of file diff --git a/cms/.env.example b/cms/.env.example index b667b6c4..371c3f74 100644 --- a/cms/.env.example +++ b/cms/.env.example @@ -1,2 +1,3 @@ HOST=0.0.0.0 PORT=1337 +GITLAB_TOKEN='gitlab_personal_access_token' \ No newline at end of file diff --git a/cms/api/gitlab-issues/config/routes.json b/cms/api/gitlab-issues/config/routes.json new file mode 100644 index 00000000..8877cf63 --- /dev/null +++ b/cms/api/gitlab-issues/config/routes.json @@ -0,0 +1,52 @@ +{ + "routes": [ + { + "method": "GET", + "path": "/gitlab-issues", + "handler": "gitlab-issues.find", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/gitlab-issues/count", + "handler": "gitlab-issues.count", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/gitlab-issues/:id", + "handler": "gitlab-issues.findOne", + "config": { + "policies": [] + } + }, + { + "method": "POST", + "path": "/gitlab-issues", + "handler": "gitlab-issues.create", + "config": { + "policies": [] + } + }, + { + "method": "PUT", + "path": "/gitlab-issues/:id", + "handler": "gitlab-issues.update", + "config": { + "policies": [] + } + }, + { + "method": "DELETE", + "path": "/gitlab-issues/:id", + "handler": "gitlab-issues.delete", + "config": { + "policies": [] + } + } + ] +} diff --git a/cms/api/gitlab-issues/controllers/gitlab-issues.js b/cms/api/gitlab-issues/controllers/gitlab-issues.js new file mode 100644 index 00000000..e8608953 --- /dev/null +++ b/cms/api/gitlab-issues/controllers/gitlab-issues.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers) + * to customize this controller + */ + +module.exports = {}; diff --git a/cms/api/gitlab-issues/models/gitlab-issues.js b/cms/api/gitlab-issues/models/gitlab-issues.js new file mode 100644 index 00000000..0054d33c --- /dev/null +++ b/cms/api/gitlab-issues/models/gitlab-issues.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#lifecycle-hooks) + * to customize this model + */ + +module.exports = {}; diff --git a/cms/api/gitlab-issues/models/gitlab-issues.settings.json b/cms/api/gitlab-issues/models/gitlab-issues.settings.json new file mode 100644 index 00000000..2df83084 --- /dev/null +++ b/cms/api/gitlab-issues/models/gitlab-issues.settings.json @@ -0,0 +1,22 @@ +{ + "kind": "collectionType", + "collectionName": "gitlab_issues", + "info": { + "name": "GitlabIssues" + }, + "options": { + "increments": true, + "timestamps": true, + "draftAndPublish": true + }, + "pluginOptions": {}, + "attributes": { + "gitlab_repository": { + "model": "gitlab-repositories", + "via": "gitlab_issue" + }, + "issues": { + "type": "json" + } + } +} diff --git a/cms/api/gitlab-issues/services/gitlab-issues.js b/cms/api/gitlab-issues/services/gitlab-issues.js new file mode 100644 index 00000000..6538a8c8 --- /dev/null +++ b/cms/api/gitlab-issues/services/gitlab-issues.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services) + * to customize this service + */ + +module.exports = {}; diff --git a/cms/api/gitlab-members/config/routes.json b/cms/api/gitlab-members/config/routes.json new file mode 100644 index 00000000..b830ef23 --- /dev/null +++ b/cms/api/gitlab-members/config/routes.json @@ -0,0 +1,52 @@ +{ + "routes": [ + { + "method": "GET", + "path": "/gitlab-members", + "handler": "gitlab-members.find", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/gitlab-members/count", + "handler": "gitlab-members.count", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/gitlab-members/:id", + "handler": "gitlab-members.findOne", + "config": { + "policies": [] + } + }, + { + "method": "POST", + "path": "/gitlab-members", + "handler": "gitlab-members.create", + "config": { + "policies": [] + } + }, + { + "method": "PUT", + "path": "/gitlab-members/:id", + "handler": "gitlab-members.update", + "config": { + "policies": [] + } + }, + { + "method": "DELETE", + "path": "/gitlab-members/:id", + "handler": "gitlab-members.delete", + "config": { + "policies": [] + } + } + ] +} diff --git a/cms/api/gitlab-members/controllers/gitlab-members.js b/cms/api/gitlab-members/controllers/gitlab-members.js new file mode 100644 index 00000000..e8608953 --- /dev/null +++ b/cms/api/gitlab-members/controllers/gitlab-members.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers) + * to customize this controller + */ + +module.exports = {}; diff --git a/cms/api/gitlab-members/models/gitlab-members.js b/cms/api/gitlab-members/models/gitlab-members.js new file mode 100644 index 00000000..0054d33c --- /dev/null +++ b/cms/api/gitlab-members/models/gitlab-members.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#lifecycle-hooks) + * to customize this model + */ + +module.exports = {}; diff --git a/cms/api/gitlab-members/models/gitlab-members.settings.json b/cms/api/gitlab-members/models/gitlab-members.settings.json new file mode 100644 index 00000000..83a82e95 --- /dev/null +++ b/cms/api/gitlab-members/models/gitlab-members.settings.json @@ -0,0 +1,22 @@ +{ + "kind": "collectionType", + "collectionName": "gitlab_members", + "info": { + "name": "GitlabMembers" + }, + "options": { + "increments": true, + "timestamps": true, + "draftAndPublish": true + }, + "pluginOptions": {}, + "attributes": { + "members": { + "type": "json" + }, + "gitlab_repository": { + "model": "gitlab-repositories", + "via": "gitlab_member" + } + } +} diff --git a/cms/api/gitlab-members/services/gitlab-members.js b/cms/api/gitlab-members/services/gitlab-members.js new file mode 100644 index 00000000..6538a8c8 --- /dev/null +++ b/cms/api/gitlab-members/services/gitlab-members.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services) + * to customize this service + */ + +module.exports = {}; diff --git a/cms/api/gitlab-merge-requests/config/routes.json b/cms/api/gitlab-merge-requests/config/routes.json new file mode 100644 index 00000000..12c1f9bc --- /dev/null +++ b/cms/api/gitlab-merge-requests/config/routes.json @@ -0,0 +1,52 @@ +{ + "routes": [ + { + "method": "GET", + "path": "/gitlab-merge-requests", + "handler": "gitlab-merge-requests.find", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/gitlab-merge-requests/count", + "handler": "gitlab-merge-requests.count", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/gitlab-merge-requests/:id", + "handler": "gitlab-merge-requests.findOne", + "config": { + "policies": [] + } + }, + { + "method": "POST", + "path": "/gitlab-merge-requests", + "handler": "gitlab-merge-requests.create", + "config": { + "policies": [] + } + }, + { + "method": "PUT", + "path": "/gitlab-merge-requests/:id", + "handler": "gitlab-merge-requests.update", + "config": { + "policies": [] + } + }, + { + "method": "DELETE", + "path": "/gitlab-merge-requests/:id", + "handler": "gitlab-merge-requests.delete", + "config": { + "policies": [] + } + } + ] +} diff --git a/cms/api/gitlab-merge-requests/controllers/gitlab-merge-requests.js b/cms/api/gitlab-merge-requests/controllers/gitlab-merge-requests.js new file mode 100644 index 00000000..e8608953 --- /dev/null +++ b/cms/api/gitlab-merge-requests/controllers/gitlab-merge-requests.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers) + * to customize this controller + */ + +module.exports = {}; diff --git a/cms/api/gitlab-merge-requests/models/gitlab-merge-requests.js b/cms/api/gitlab-merge-requests/models/gitlab-merge-requests.js new file mode 100644 index 00000000..0054d33c --- /dev/null +++ b/cms/api/gitlab-merge-requests/models/gitlab-merge-requests.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#lifecycle-hooks) + * to customize this model + */ + +module.exports = {}; diff --git a/cms/api/gitlab-merge-requests/models/gitlab-merge-requests.settings.json b/cms/api/gitlab-merge-requests/models/gitlab-merge-requests.settings.json new file mode 100644 index 00000000..f71c7696 --- /dev/null +++ b/cms/api/gitlab-merge-requests/models/gitlab-merge-requests.settings.json @@ -0,0 +1,22 @@ +{ + "kind": "collectionType", + "collectionName": "gitlab_merge_requests", + "info": { + "name": "GitlabMergeRequests" + }, + "options": { + "increments": true, + "timestamps": true, + "draftAndPublish": true + }, + "pluginOptions": {}, + "attributes": { + "merges": { + "type": "json" + }, + "gitlab_repository": { + "model": "gitlab-repositories", + "via": "gitlab_merge_request" + } + } +} diff --git a/cms/api/gitlab-merge-requests/services/gitlab-merge-requests.js b/cms/api/gitlab-merge-requests/services/gitlab-merge-requests.js new file mode 100644 index 00000000..6538a8c8 --- /dev/null +++ b/cms/api/gitlab-merge-requests/services/gitlab-merge-requests.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services) + * to customize this service + */ + +module.exports = {}; diff --git a/cms/api/gitlab-repositories/config/routes.json b/cms/api/gitlab-repositories/config/routes.json new file mode 100644 index 00000000..addb3365 --- /dev/null +++ b/cms/api/gitlab-repositories/config/routes.json @@ -0,0 +1,52 @@ +{ + "routes": [ + { + "method": "GET", + "path": "/gitlab-repositories", + "handler": "gitlab-repositories.find", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/gitlab-repositories/count", + "handler": "gitlab-repositories.count", + "config": { + "policies": [] + } + }, + { + "method": "GET", + "path": "/gitlab-repositories/:id", + "handler": "gitlab-repositories.findOne", + "config": { + "policies": [] + } + }, + { + "method": "POST", + "path": "/gitlab-repositories", + "handler": "gitlab-repositories.create", + "config": { + "policies": [] + } + }, + { + "method": "PUT", + "path": "/gitlab-repositories/:id", + "handler": "gitlab-repositories.update", + "config": { + "policies": [] + } + }, + { + "method": "DELETE", + "path": "/gitlab-repositories/:id", + "handler": "gitlab-repositories.delete", + "config": { + "policies": [] + } + } + ] +} diff --git a/cms/api/gitlab-repositories/controllers/gitlab-repositories.js b/cms/api/gitlab-repositories/controllers/gitlab-repositories.js new file mode 100644 index 00000000..e8608953 --- /dev/null +++ b/cms/api/gitlab-repositories/controllers/gitlab-repositories.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-controllers) + * to customize this controller + */ + +module.exports = {}; diff --git a/cms/api/gitlab-repositories/models/gitlab-repositories.js b/cms/api/gitlab-repositories/models/gitlab-repositories.js new file mode 100644 index 00000000..0054d33c --- /dev/null +++ b/cms/api/gitlab-repositories/models/gitlab-repositories.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#lifecycle-hooks) + * to customize this model + */ + +module.exports = {}; diff --git a/cms/api/gitlab-repositories/models/gitlab-repositories.settings.json b/cms/api/gitlab-repositories/models/gitlab-repositories.settings.json new file mode 100644 index 00000000..5883b328 --- /dev/null +++ b/cms/api/gitlab-repositories/models/gitlab-repositories.settings.json @@ -0,0 +1,33 @@ +{ + "kind": "collectionType", + "collectionName": "gitlab_repositories", + "info": { + "name": "GitlabRepositories" + }, + "options": { + "increments": true, + "timestamps": true, + "draftAndPublish": true + }, + "pluginOptions": {}, + "attributes": { + "project_id": { + "type": "string" + }, + "project_data": { + "type": "json" + }, + "gitlab_issue": { + "via": "gitlab_repository", + "model": "gitlab-issues" + }, + "gitlab_member": { + "via": "gitlab_repository", + "model": "gitlab-members" + }, + "gitlab_merge_request": { + "via": "gitlab_repository", + "model": "gitlab-merge-requests" + } + } +} diff --git a/cms/api/gitlab-repositories/services/gitlab-repositories.js b/cms/api/gitlab-repositories/services/gitlab-repositories.js new file mode 100644 index 00000000..6538a8c8 --- /dev/null +++ b/cms/api/gitlab-repositories/services/gitlab-repositories.js @@ -0,0 +1,8 @@ +'use strict'; + +/** + * Read the documentation (https://strapi.io/documentation/developer-docs/latest/development/backend-customization.html#core-services) + * to customize this service + */ + +module.exports = {}; diff --git a/cms/config/functions/cron.js b/cms/config/functions/cron.js index 7cc9c418..6e57ec9f 100644 --- a/cms/config/functions/cron.js +++ b/cms/config/functions/cron.js @@ -3,6 +3,8 @@ const { getLatestCommunityActivity } = require("./fetchTopPosts"); const { getCommunityContributors } = require("./fetchContributors") const { githubKit } = require("./github"); const { updateSpeakerData } = require('./speaker') +const { gitlabKit } = require("./gitlab") + /** * Cron config that gives you an opportunity * to run scheduled jobs. @@ -28,7 +30,10 @@ module.exports = { getCommunityContributors('https://gsoc.rocket.chat/api/data','rocketChat','Rocket.Chat'); updateSpeakerData(); }, - '*/* 10 * * * *': () => { + '* */10 * * * *': () => { githubKit('RocketChat','RC4Community',['issues','contributors','pulls']); + }, + '* */30 * * * *': () => { + gitlabKit(3472737,['issues','merges','members']); } }; \ No newline at end of file diff --git a/cms/config/functions/fetchData.js b/cms/config/functions/fetchData.js index 78a09064..f52adc78 100644 --- a/cms/config/functions/fetchData.js +++ b/cms/config/functions/fetchData.js @@ -8,6 +8,7 @@ const { carousels, speakers, forms } = require('../initialData'); const { githubKit } = require('./github'); +const { gitlabKit } = require('./gitlab') module.exports = async () => { @@ -22,7 +23,8 @@ module.exports = async () => { var formCount = await strapi.query("form").count(); var ghrepos = await strapi.query("github-repositories").count({}); var speakersCount = await strapi.query("speaker").count({}); - + var gitlabprojects = await strapi.query("gitlab-repositories").count({}); + // initial fetch speakers.map(async (speaker, index) => { if (index <= speakersCount - 1) { @@ -62,6 +64,10 @@ module.exports = async () => { githubKit('RocketChat', 'RC4Community', ['issues', 'contributors', 'pulls']); } + if (!gitlabprojects) { + gitlabKit(3472737,['issues','merges','members']); + } + forms.map(async (form, index) => { if (index <= formCount - 1) { await strapi.query("form").update( diff --git a/cms/config/functions/gitlab.js b/cms/config/functions/gitlab.js new file mode 100644 index 00000000..35660278 --- /dev/null +++ b/cms/config/functions/gitlab.js @@ -0,0 +1,300 @@ +const axios = require("axios"); + +const getRepoData = async function (project_id, token) { + try { + let returnedData = await axios({ + method: "GET", + url: `https://gitlab.com/api/v4/projects/${project_id}`, + headers: { Authorization: `Bearer ${token}` }, + }); + + const { data } = returnedData; + const { + id, + name, + namespace, + name_with_namespace, + http_url_to_repo, + description, + star_count, + forks_count, + open_issues_count, + topics, + avatar_url, + } = data; + const html_url = http_url_to_repo; + const ownerName = namespace.name; + const full_name = name_with_namespace; + const compactData = { + id, + full_name, + name, + ownerName, + avatar_url, + html_url, + description, + star_count, + forks_count, + open_issues_count, + topics, + }; + return { + success: true, + data: compactData, + }; + } catch (err) { + return { + error_message: err, + success: false, + message: "Internal Server Error!", + }; + } +}; + +const getRepoIssues = async function (project_id, token) { + try { + let returnedData = await axios({ + method: "GET", + url: `https://gitlab.com/api/v4/projects/${project_id}/issues`, + headers: { Authorization: `Bearer ${token}` }, + }); + let data = returnedData.data; + let issueList = []; + + data.forEach((issue) => { + let newIssue = new Object(); + if (issue.state === "opened") { + newIssue["id"] = issue.id; + newIssue["author"] = issue.author; + newIssue["title"] = issue.title; + newIssue["iid"] = issue.iid; + newIssue["state"] = issue.state; + newIssue["web_url"] = issue.web_url; + newIssue["created_at"] = issue.created_at; + newIssue["upvotes"] = issue.upvotes; + newIssue["downvotes"] = issue.downvotes; + issueList.push(newIssue); + } + }); + + return { + success: true, + data: issueList, + }; + } catch (err) { + return { + error_message: err, + success: false, + message: "Internal Server Error!", + }; + } +}; + +const getRepoMerges = async function (project_id, token) { + try { + let returnedData = await axios({ + method: "GET", + url: `https://gitlab.com/api/v4/projects/${project_id}/merge_requests`, + headers: { Authorization: `Bearer ${token}` }, + }); + let data = returnedData.data; + + let mergeRequestList = []; + data.forEach((mergeRequest) => { + let newMergeRequest = new Object(); + if (mergeRequest.closed_at === null) { + newMergeRequest["id"] = mergeRequest.id; + newMergeRequest["author"] = mergeRequest.author; + newMergeRequest["title"] = mergeRequest.title; + newMergeRequest["iid"] = mergeRequest.iid; + newMergeRequest["state"] = mergeRequest.state; + newMergeRequest["web_url"] = mergeRequest.web_url; + newMergeRequest["created_at"] = mergeRequest.created_at; + newMergeRequest["upvotes"] = mergeRequest.upvotes; + newMergeRequest["downvotes"] = mergeRequest.downvotes; + mergeRequestList.push(newMergeRequest); + } + }); + return { + success: true, + data: mergeRequestList, + }; + } catch (err) { + return { + error_message: err, + success: false, + message: "Internal Server Error!", + }; + } +}; + +const getProjectMembers = async function (project_id, token) { + try { + let returnedData = await axios({ + method: "GET", + url: `https://gitlab.com/api/v4/projects/${project_id}/members`, + headers: { Authorization: `Bearer ${token}` }, + }); + + const { data } = returnedData; + + let contributorList = []; + data.forEach((contributor) => { + let newContributor = new Object(); + newContributor["id"] = contributor.id; + newContributor["web_url"] = contributor.web_url; + newContributor["name"] = contributor.name; + newContributor["username"] = contributor.username; + newContributor["avatar_url"] = contributor.avatar_url; + contributorList.push(newContributor); + }); + + return { + success: true, + data: contributorList, + }; + } catch (err) { + return { + error_message: err, + success: false, + message: "Internal Server Error!", + }; + } +}; + +module.exports.gitlabKit = async function (project_id, needed) { + try { + let getIssues = false; + let getMerges = false; + let getMembers = false; + if (Array.isArray(needed)) { + needed.forEach((need) => { + if (need === "issues") { + getIssues = true; + } else if (need === "merges") { + getMerges = true; + } else if (need === "members") { + getMembers = true; + } + }); + } else { + if (needed === "issues") { + getIssues = true; + } else if (needed === "merges") { + getMerges = true; + } else if (needed === "members") { + getMembers = true; + } + } + const token = process.env.GITLAB_TOKEN; + let repoData = await getRepoData(project_id, token); + let gitlabRepositoryCount = await strapi + .query("gitlab-repositories") + .count({ + project_id: project_id, + }); + let gitlabRepository = await strapi.query("gitlab-repositories").findOne({ + project_id: project_id, + }); + + if (repoData.success) { + if (gitlabRepositoryCount === 0) { + gitlabRepository = await strapi.query("gitlab-repositories").create({ + project_id: repoData.data.id, + project_data: repoData.data, + }); + } else { + gitlabRepository.project_data = repoData.data; + gitlabRepository.project_id = repoData.data.id; + await strapi.query("gitlab-repositories").update( + { + id: gitlabRepository.id, + }, + gitlabRepository + ); + } + } + + if (getIssues) { + const issuesData = await getRepoIssues(project_id, token); + + if (issuesData.success) { + const issueCount = await strapi.query("gitlab-issues").count({ + gitlab_repository: gitlabRepository.id, + }); + if (issueCount === 0) { + let newissueData = await strapi.query("gitlab-issues").create({ + gitlab_repository: gitlabRepository.id, + issues: issuesData.data, + }); + issuesId = newissueData.id; + } else { + await strapi.query("gitlab-issues").update( + { + gitlab_repository: gitlabRepository.id, + }, + { + gitlab_repository: gitlabRepository.id, + issues: issuesData.data, + } + ); + } + } + } + + if (getMerges) { + const mergeData = await getRepoMerges(project_id, token); + if (mergeData.success) { + const mergeDataCount = await strapi + .query("gitlab-merge-requests") + .count({ + gitlab_repository: gitlabRepository.id, + }); + if (mergeDataCount === 0) { + await strapi.query("gitlab-merge-requests").create({ + gitlab_repository: gitlabRepository.id, + merges: mergeData.data, + }); + } else { + await strapi.query("gitlab-merge-requests").update( + { + gitlab_repository: gitlabRepository.id, + }, + { + gitlab_repository: gitlabRepository.id, + merges: mergeData.data, + } + ); + } + } + } + + if (getMembers && typeof token !== "undefined") { + const memberData = await getProjectMembers(project_id, token); + if (memberData.success) { + const membersDataCount = await strapi.query("gitlab-members").count({ + gitlab_repository: gitlabRepository.id, + }); + + if (membersDataCount === 0) { + await strapi.query("gitlab-members").create({ + gitlab_repository: gitlabRepository.id, + members: memberData.data, + }); + } else { + await strapi.query("gitlab-members").update( + { + gitlab_repository: gitlabRepository.id, + }, + { + gitlab_repository: gitlabRepository.id, + members: memberData.data, + } + ); + } + } + } + } catch (err) { + console.log(err); + } +}; diff --git a/docs/components/README.md b/docs/components/README.md index 8a780586..ab5d4354 100644 --- a/docs/components/README.md +++ b/docs/components/README.md @@ -13,6 +13,8 @@ We at Rocket.Chat believe in building susatinable online communities which can 4. InAppChat Component +5. Gitlab Components Kit + --- diff --git a/docs/components/gitlab/README.MD b/docs/components/gitlab/README.MD new file mode 100644 index 00000000..5c5be5c2 --- /dev/null +++ b/docs/components/gitlab/README.MD @@ -0,0 +1,240 @@ +# Gitlab Component Kit + +The Gitlab Component kit cane be used by communtiy builders to showcase the progress of their projects. The current gitlab component kit can be used to showcase the following details of a project : + +### 1. Project Overview + +

+ overview +

+ + +### 2. Issues + +

+ issues-example +

+ +### 3. Project Members + +

+ members-example +

+ +### 4. Merge Requests + +

+ merge-example +

+ + +## Component Details + +All the componets use the same `` tag and the same data fetchig library with additional paramters. +Some components require an [access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token) to be set as environment variable in our cms '.env' file . Create a .env and set `GITLAB_TOKEN` variable as your personal access token. Checkout `/cms/.env.example` for reference. + +| Serial Number | Component Name | Usage | Access Token Required | +| ------------- |------------------------- |-------------------------- | -----| +| 1 | Project Overview | `` | No | +| 2 | Project Issues | ` type={"issues"}` | No | +| 3 | Project Merge Requests | ` type={"merges"}` | No | +| 4 | Project Members | ` type={"members"}` | Yes | + + +## Gitlab Tag Props + +We use our helper function `gitlabKitData(project_id,[... needs]);` to fetch the data for the component. The returned object can be directly passed to the component and it will render data based on the passed paramters + +| Prop Name | Description | Type | +| ------------- |------------------------- | -----| +| type | This specifies the `type` of gitlab kit components we wish to use. If `type` is not specified, by default the `project overview` component is rendered. Type can be set to : `issues` , `merges` or `members` | string | +| gitlabData | This will contain the data which will be rendered by the component | json | + + +# Usage Examples + +## #Example 1 : Using Gitlab Project Overview , Issues, Members and Merge Request all at once. + +### Using the component + +``` +import Head from "next/head"; +import { Gitlab } from '../components/gitlab'; +import { gitlabKitData } from '../lib/gitlab'; + +export default function GitlabPage(props){ + return ( +
+ + Gitlab Example 1 + +
+

+ Project Overview +

+ +
+
+

+ Gitlab Issues +

+ +
+ +
+

+ Gitlab Pull Requests +

+ +
+ +
+

+ Project Members ✨ +

+ +
+
+ ); +} + +export async function getStaticProps(){ + + const gitlabData = await gitlabKitData(3472737,['issues','merges','members']); + + return { + props: { + gitlabData + }, + revalidate: 30, + }; +} +``` + +### Setting up component data in CMS + +1. Open the your cron.js ( This can be located in the following path : `RC4Community/cms/config/functions/cron.js` ). +2. We need to add cron jobs to handle the fetch requests to Gitlab, as frequent fetch request will forbid the IP address to fetch data. + This example means after each 30 minutes we will re-fetch and update our data. + + ![image](https://user-images.githubusercontent.com/70485812/160277772-265d6371-699d-405a-89fd-6909f9f12125.png) + +3. The `gitlabKit()` function takes two arguments: + 1. The gitlab `Project ID` of the project. + 2. The components we wish to use, as it fetches data accordigly. This will be an array of strinng and can take a combinationof values : `issues`, `members`,`merges` depending on our usecase. + +4. Some components require an [access token](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token) to be set as environment variable in our cms `.env` file . Create a .env and set `GITLAB_TOKEN` variable as your personal access token. Checkout `/cms/.env.example` for reference. ![image](https://user-images.githubusercontent.com/70485812/160278557-7e596b2b-7f28-484e-b20b-b8cba8de08e1.png) + + +5. Just one more change and we are good to go! As the cron job will populate and re-populate data after some time interval. We can't just wait for the first one hour to work with the data right? So we need an initial fetch! + +6. Go to `fetchData.js` file which is also in the same functions directory and change the `Project ID` here so that when we first call `INITIALIZE_DATA=true npm run develop` it will call these functions and populate the data for us! + +![image](https://user-images.githubusercontent.com/70485812/160277935-375534a0-41ce-4357-8123-b653d704fed3.png) + +## #Example 2 : Using Project Members and Merge Request components for the [Inkscape Project](https://gitlab.com/inkscape/inkscape) by Inkscape. + +### Using the component + +``` +import Head from "next/head"; +import { Gitlab } from '../components/gitlab'; +import { gitlabKitData } from '../lib/gitlab'; + +export default function GitlabPage(props){ + return ( +
+ + GSOC2022 Gitlab + +
+

+ Gitlab Merge Requests +

+ +
+ +
+

+ Project Members ✨ +

+ +
+
+ ); +} + +export async function getStaticProps(){ + + const gitlabData = await gitlabKitData(3472737,['members','merges']); + + return { + props: { + gitlabData + }, + revalidate: 30, + }; +} +``` + +### Setting up component data in CMS + + +1. Cron Jobs ![image](https://user-images.githubusercontent.com/70485812/160278155-d48975a5-474c-4307-92c8-d2c168d9db1c.png) + +2. Initial Fetch ![image](https://user-images.githubusercontent.com/70485812/160278175-f7845508-4edd-4bf3-ac00-cb7a188cfc9b.png) + + +## #Example 3 : Using Project Overview Component only. + +Note : the Project overview component can be used by default. We do not need to specify any `type` in Gitlab component or add anything to `needed` in `gitlabKitData` +or in `gitlabKit` in the cron job. + +### Using the component + +``` +import Head from "next/head"; +import { Gitlab } from '../components/gitlab'; +import { gitlabKitData } from '../lib/gitlab'; + +export default function GitlabPage(props){ + return ( +
+ + Gitlab Page + +
+

+ Project Overview +

+ +
+
+ ); +} + +export async function getStaticProps(){ + + const gitlabData = await gitlabKitData(3472737); + + return { + props: { + gitlabData + }, + revalidate: 30, + }; +} +``` + +### Setting up component data in CMS + + +1. Cron Jobs ![image](https://user-images.githubusercontent.com/70485812/160278317-481ef952-e039-4d1d-ad00-a3cb4a179701.png) + +2. Initial Fetch ![image](https://user-images.githubusercontent.com/70485812/160278332-fffb4261-c693-49bc-b496-352463173683.png) + + + + + +### :arrow_left: Explore More Components \ No newline at end of file