Skip to content

Commit bcd1eab

Browse files
committed
Fetch Microsoft Learn Certifications
1 parent 6c0f6d0 commit bcd1eab

7 files changed

Lines changed: 344 additions & 3 deletions

File tree

api/mslearn.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const renderMSLearnCertificationCard = require("../src/cards/mslearn-certification-card");
2+
const renderError = require("../src/cards/error-card");
3+
const fetchMSLearnCertificationData = require("../src/fetch/mslearn-fetcher");
4+
5+
export default async function handler(req, res) {
6+
const {
7+
transcriptId,
8+
theme
9+
} = req.query;
10+
11+
try {
12+
// console.log(`index.js: BEGIN`)
13+
14+
// HTTP GET
15+
const badges = await fetchMSLearnCertificationData(
16+
transcriptId
17+
);
18+
19+
// Send Response
20+
// console.log(`index.js: SUCCESS`)
21+
res.setHeader("Content-Type", "image/svg+xml");
22+
res.setHeader("Vary", "Accept-Encoding");
23+
return res.send(renderMSLearnCertificationCard(badges, theme));
24+
25+
} catch (err) {
26+
// console.log(`index.js: ERROR: ${err.message}`)
27+
// Send Error Response
28+
return res.send(renderError(err.message, err.secondaryMessage));
29+
}
30+
};

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
2-
"name": "credly-readme-widget",
2+
"name": "github-readme-widget",
33
"version": "0.0.1",
4-
"description": "Dynamically generate credly badges for your github readme",
4+
"description": "Dynamically generate widgets for your github readme",
55
"main": "index.js",
66
"author": "Ashish Singh Baghel",
77
"license": "MIT",
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
const themes = require("../../themes");
2+
3+
function isValidHexColor(hexColor) {
4+
return new RegExp(
5+
/^([A-Fa-f0-9]{8}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4})$/,
6+
).test(hexColor);
7+
}
8+
9+
function isValidGradient(colors) {
10+
return isValidHexColor(colors[1]) && isValidHexColor(colors[2]);
11+
}
12+
13+
function fallbackColor(color, fallbackColor) {
14+
let colors = color.split(",");
15+
let gradient = null;
16+
17+
if (colors.length > 1 && isValidGradient(colors)) {
18+
gradient = colors;
19+
}
20+
21+
return (
22+
(gradient ? gradient : isValidHexColor(color) && `#${color}`) ||
23+
fallbackColor
24+
);
25+
}
26+
27+
function getCardColors({
28+
title_color,
29+
text_color,
30+
icon_color,
31+
bg_color,
32+
border_color,
33+
theme,
34+
fallbackTheme = "default",
35+
}) {
36+
const defaultTheme = themes[fallbackTheme];
37+
const selectedTheme = themes[theme] || defaultTheme;
38+
const defaultBorderColor =
39+
selectedTheme.border_color || defaultTheme.border_color;
40+
41+
// get the color provided by the user else the theme color
42+
// finally if both colors are invalid fallback to default theme
43+
const titleColor = fallbackColor(
44+
title_color || selectedTheme.title_color,
45+
"#" + defaultTheme.title_color,
46+
);
47+
const iconColor = fallbackColor(
48+
icon_color || selectedTheme.icon_color,
49+
"#" + defaultTheme.icon_color,
50+
);
51+
const textColor = fallbackColor(
52+
text_color || selectedTheme.text_color,
53+
"#" + defaultTheme.text_color,
54+
);
55+
const bgColor = fallbackColor(
56+
bg_color || selectedTheme.bg_color,
57+
"#" + defaultTheme.bg_color,
58+
);
59+
60+
const borderColor = fallbackColor(
61+
border_color || defaultBorderColor,
62+
"#" + defaultBorderColor,
63+
);
64+
65+
return { titleColor, iconColor, textColor, bgColor, borderColor };
66+
}
67+
68+
const renderBadgesCard = (badges, theme="default") => {
69+
// console.log(`badges-card: BEGIN`);
70+
// const theme = "default";
71+
72+
// returns theme based colors with proper overrides and defaults
73+
const title = `Microsoft Learn Active Certifications`;
74+
const totalCount = badges.certificationData.totalActiveCertifications;
75+
76+
// console.log(theme);
77+
78+
// titleColor = `#white`;
79+
// textColor = `#black`;
80+
// bgColor = `#000000`;
81+
// borderColor = `#ffffff`;
82+
83+
// returns theme based colors with proper overrides and defaults
84+
const { titleColor, textColor, iconColor, bgColor, borderColor } =
85+
getCardColors({
86+
// title_color,
87+
// icon_color,
88+
// text_color,
89+
// bg_color,
90+
// border_color,
91+
theme
92+
});
93+
94+
let certItems = ` `;
95+
let certItemPropY = 0;
96+
97+
// badges.data.forEach(function (item, i ) {
98+
// for (const item of badges.data) {
99+
for (let i = 0; i < 5; i++) {
100+
certItems += `<text x="0" y="${certItemPropY}" class="cert-text">${badges.certificationData.activeCertifications[i].name}</text>`;
101+
certItemPropY += 20;
102+
}
103+
104+
let badgesCard = `
105+
<svg version="1.1"
106+
width="595"
107+
height="195"
108+
viewBox="0 0 595 195"
109+
fill="#ffffff"
110+
xmlns="http://www.w3.org/2000/svg"
111+
xmlns:xlink="http://www.w3.org/1999/xlink" >
112+
113+
<style>
114+
.header {
115+
font: 600 18px 'Segoe UI', Ubuntu, Sans-Serif;
116+
fill: ${titleColor};
117+
animation: fadeInAnimation 0.8s ease-in-out forwards;
118+
}
119+
@supports(-moz-appearance: auto) {
120+
/* Selector detects Firefox */
121+
.header { font-size: 15.5px; }
122+
}
123+
.cert-text {
124+
font: 600 14px 'Segoe UI', Ubuntu, "Helvetica Neue", Sans-Serif;
125+
fill: ${textColor};
126+
}
127+
</style>
128+
129+
<rect data-id="card-bg"
130+
x="2"
131+
y="2"
132+
rx="5"
133+
width="99%"
134+
height="99%"
135+
stroke="${borderColor}"
136+
fill="${bgColor}"
137+
stroke-width="2"
138+
stroke-opacity="1"
139+
/>
140+
141+
<g data-id="card-title"
142+
transform="translate(25, 35)">
143+
144+
<g transform="translate(0, 0)">
145+
<text
146+
x="0"
147+
y="0"
148+
class="header"
149+
data-testid="header">
150+
${title}
151+
</text>
152+
</g>
153+
</g>
154+
155+
<g data-id="main-card-body"
156+
transform="translate(25, 75)">
157+
${certItems}
158+
</g>
159+
</svg>
160+
`
161+
// console.log(`badges-card: ${badgesCard}`);
162+
// console.log(`badges-card: END`);
163+
return `${badgesCard}`;
164+
165+
};
166+
167+
module.exports = renderBadgesCard

src/common/utils.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ function encodeHTML(str) {
1414
const SECONDARY_ERROR_MESSAGES = {
1515
MAX_RETRY: "Max retries reached, unable to get data ! Please retry after sometime !! ",
1616
USER_NOT_FOUND: "Please make sure the provided username is correct !",
17+
TRANSCRIPT_NOT_FOUND: "Please ensure that provided transcript id is correct !"
1718
};
1819

1920
class CustomError extends Error {

src/fetch/mslearn-fetcher.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
const axios = require("axios").default;
2+
3+
const {
4+
CustomError,
5+
MissingParamError
6+
} = require("../common/utils");
7+
8+
const fetchMSLearnTranscript = (transcriptId) => {
9+
return axios({
10+
method: "get",
11+
// url: `https://www.credly.com/users/${username}/badges?sort=most_popular`,
12+
url: `https://learn.microsoft.com/api/profiles/transcript/share/${transcriptId}?locale=en-us`,
13+
headers: {
14+
Accept: "application/json",
15+
},
16+
});
17+
};
18+
19+
async function fetchMSLearnCertificationData(transcriptId){
20+
21+
if (!transcriptId) throw new MissingParamError(["transcriptId"]);
22+
// console.log(username);
23+
24+
try {
25+
let res = await fetchMSLearnTranscript(transcriptId);
26+
// console.log(res.data);
27+
return res.data;
28+
} catch (error) {
29+
if (error.response) {
30+
// console.log(error.response.data);
31+
// console.log(error.response.status);
32+
// console.log(error.response.headers);
33+
throw new CustomError(
34+
error.message || "Could not fetch data",
35+
CustomError.TRANSCRIPT_NOT_FOUND,
36+
);
37+
}
38+
// }else if (error.request) {
39+
// console.log(error.request);
40+
// }else {
41+
// console.log('Error', error.message);
42+
// }
43+
console.log(error.config);
44+
}
45+
}
46+
47+
module.exports = fetchMSLearnCertificationData

templates/mslearn-widget.svg

Lines changed: 96 additions & 0 deletions
Loading

varcel.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
}
77
},
88
"redirects": [
9-
{ "source": "/", "destination": "https://github.com/mechdeveloper/credly-readme-widget" }
9+
{ "source": "/", "destination": "https://github.com/mechdeveloper/github-readme-widgets" }
1010
]
1111
}

0 commit comments

Comments
 (0)