Skip to content

Commit 435fdb4

Browse files
authored
Merge pull request #41 from topcoder-platform/dev
PROD-2741 Topcoder Academy MVP -> prod
2 parents 4a1f7db + b34b5a6 commit 435fdb4

File tree

59 files changed

+3560
-343
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+3560
-343
lines changed

Jenkinsfile

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
// Define Application name
2-
def APPNAME = "freecodecamp-mfe"
2+
def APPNAME = "freecodecamp"
33

44
// Define which branch build and deploy need to run in the below array
5-
65
def branchfilter = ['dev', 'prod']
76

87
if (!branchfilter.contains(env.BRANCH_NAME)) {
@@ -13,7 +12,7 @@ if (!branchfilter.contains(env.BRANCH_NAME)) {
1312
return
1413
}
1514

16-
//Define branch specific var
15+
// Define branch specific vars
1716
if (env.BRANCH_NAME == 'dev') {
1817
DEPLOY_ENV = 'DEV'
1918
LOGICAL_ENV = 'dev'
@@ -24,7 +23,6 @@ if (env.BRANCH_NAME == 'dev') {
2423
ENABLE_CACHE = false
2524
}
2625

27-
// NOTE: main/prod is not supported yet
2826
if (env.BRANCH_NAME == 'prod') {
2927
DEPLOY_ENV = 'PROD'
3028
LOGICAL_ENV = 'prod'
@@ -126,7 +124,7 @@ pipeline {
126124
"""
127125
}
128126
}
129-
stage('appdeploy')
127+
stage('appdeploy')
130128
{
131129
//Deploying app
132130
when { expression { IS_APP_DEPLOY } }
@@ -137,10 +135,10 @@ pipeline {
137135
sh """
138136
#!/bin/bash
139137
./master_deploy.sh -d CFRONT -e $DEPLOY_ENV -c $ENABLE_CACHE
140-
"""
138+
"""
141139
}
142140
}
143-
stage('apideploy')
141+
stage('apideploy')
144142
{
145143
//Deploying app
146144
when { expression { IS_API_DEPLOY } }
@@ -153,7 +151,7 @@ pipeline {
153151
sed -i '/node_modules/d' ./.dockerignore
154152
docker build -f docker/api/ECSDockerfile -t $APPNAME-api:latest .
155153
./master_deploy.sh -d ECS -e $DEPLOY_ENV -t latest -s ${LOGICAL_ENV}-${APPNAME}-appvar -i ${APPNAME}-api
156-
"""
154+
"""
157155
}
158156
}
159157
}

README-TOPCODER.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
## Topcoder freeCodeCamp modifications README
2+
3+
This file describes the modifications Topcoder made to the freeCodeCamp.org application to support providing FCC course content within the Topcoder Academy and Learning Paths effort. It also provides details about how the application is deployed and hosted in our AWS cloud environment.
4+
5+
## Table of Contents
6+
7+
- [Learning Platform](#the-learning-platform)
8+
- [Learning Platform API](#the-learning-platform-api)
9+
- [MongoDB Configuration](#mongodb-configuration)
10+
- [CI/CD Configuration](#cicd-configuration)
11+
12+
### The Learning Platform
13+
14+
The freeCodeCamp application is hosted in the [platform-ui](https://github.com/topcoder-platform/platform-ui) application, within the [Learn tool](https://github.com/topcoder-platform/platform-ui/tree/dev/src-ts/tools/learn). The Learn application uses the [learning-paths-api](https://github.com/topcoder-platform/learning-paths-api) to manage and display course metadata, such as the certifications and courses available, and to track user course progress independently of the progress tracking performed by the hosted freeCodeCamp application.
15+
16+
The freeCodeCamp code editors and course material viewing tools are hosted inside an iframe and communicate with the parent platform-ui application via messages, which in turn trigger calls to the learning paths API. For example, when a user completes a course, a course completion message is sent to from the freeCodeCamp application to the parent platform-ui app, which then makes an API call to record the event in the Topcoder Academy datastore.
17+
18+
#### Build and Deploy Process
19+
20+
The freeCodeCamp application is statically built during deployment and hosted in AWS S3 via a Cloudfront distribution. The S3 bucket had to be specially configured to support this type of deployment, in particular to allow the correct URLs to be used by the platform-ui and the embedded freeCodeCamp application.
21+
22+
#### Authentication
23+
24+
The application natively uses Auth0 authentication, which we had to modify to use Topcoder's login and authentication process. (details)
25+
26+
(TODO - more detail here on the major things we did)
27+
28+
### The Learning Platform API
29+
30+
The freeCodeCamp API, which is responsible for managing user progress and sessions in MongoDB, was modified to support Topcoder Auth0 authentication and to reflect the path at which the API was deployed in AWS.
31+
32+
#### Build and Deploy Process
33+
34+
The API is deployed via AWS Elastic Container Service. See the [api/ECSDockerfile](./docker/api/ECSDockerfile) for details of this configuration. A key thing to note is that the freeCodeCamp application and API rely in the presence of a .env file that contains all of the required environment variables.
35+
36+
Topcoder's standard deployment process is to store sensitive deployment variables (include API keys, secrets, tokens, etc.) in an S3 bucket and to programmatically retrieve them and inject those values as environment variables into the Docker container at build time. However, this approach does not work for freeCodeCamp due to the reliance on the existence of the .env file in the project root. We modified the build and deploy process to include a copy of the .env file in S3 that is copied into the project root and available for the application when it is started.
37+
38+
### MongoDB Configuration
39+
40+
The freeCodeCamp application natively uses MongoDB to track user sessions and course progress. To support our deployment of freeCodeCamp, an instance of MongoDB's cloud [Atlas](https://www.mongodb.com/atlas) service was stood-up. Key things to note about this:
41+
42+
- This instance was configured via the [AWS Marketplace](https://aws.amazon.com/marketplace/pp/prodview-pp445qepfdy34?ref_=aws-mp-console-subscription-detail) so that the Atlas instance billing would flow to the associated AWS account.
43+
- An initial attempt was made to use an Atlas "pay as you go" serverless instance of MongoDB since costs would be very low. However, two issues arose that prevented this:
44+
- The MongoDB version that Atlas serverless uses was incompatible with freeCodeCamp's NodeJS mongodb driver package
45+
- AWS VPC peering cannot be used with a serverless instance
46+
- As a result of these issues we settled on an M10 clustered instance of MongoDB.
47+
48+
In order to connect the [freeCodeCamp API](#learning-platform-api) to the MongoDB Atlas instance, VPC peering was established between the AWS VPC into which the freeCodeCamp API was deployed and the Atlas instance's VPC. The [VPC peering documentation](https://www.mongodb.com/docs/atlas/security-vpc-peering/) on their website is comprehensive and accurate. We followed this process to establish a secure connection from the API to the database.
49+
50+
### CI/CD Configuration
51+
52+
We initially attempted to deploy the freeCodeCamp application and API via our standard CircleCI system, however we were never able to get this to work properly. The application build failed repeatedly at various points and was never successful.
53+
54+
Our DevOps team stood-up a new Jenkins CI/CD system in an AWS EC2 instance (see the [Jenkinsfile](./Jenkinsfile) in the project root for details) and this was successful. However, the build and deployment of the application and API currently takes over 20 minutes. We have an outstanding request with the DevOps team to address this.

api-server/src/server/middlewares/request-authorization.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ export default function getRequestAuthorisation({
7373
const { origin } = getRedirectParams(req);
7474
const { path } = req;
7575
if (!isAllowedPath(path)) {
76-
console.log('##### path:', path);
7776
const { accessToken, error, jwt } = getAccessTokenFromRequest(
7877
req,
7978
jwtSecret

client/gatsby-config.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ module.exports = {
123123
icon: 'src/assets/images/square_puck.png'
124124
}
125125
},
126-
'gatsby-plugin-remove-serviceworker'
126+
'gatsby-plugin-remove-serviceworker',
127+
{
128+
resolve: `gatsby-plugin-s3`,
129+
options: {
130+
bucketName: 'freecodecamp-mfe.topcoder-dev.com',
131+
hostname: 'freecodecamp-mfe.topcoder-dev.com',
132+
protocol: 'https'
133+
}
134+
}
127135
]
128136
};

client/i18n/locales/english/intro.json

Lines changed: 65 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -80,31 +80,36 @@
8080
"intro": [
8181
"This is one of the required projects to earn your certification.",
8282
"For this project, you will build a tribute page for a subject of your choosing, fictional or real."
83-
] },
83+
]
84+
},
8485
"build-a-personal-portfolio-webpage-project": {
8586
"title": "Personal Portfolio Webpage",
8687
"intro": [
8788
"This is one of the required projects to earn your certification.",
8889
"For this project, you will build your own personal portfolio page."
89-
] },
90+
]
91+
},
9092
"build-a-product-landing-page-project": {
9193
"title": "Product Landing Page",
9294
"intro": [
9395
"This is one of the required projects to earn your certification.",
9496
"For this project, you will build a product landing page to market a product of your choice."
95-
] },
97+
]
98+
},
9699
"build-a-survey-form-project": {
97100
"title": "Survey Form",
98101
"intro": [
99102
"This is one of the required projects to earn your certification.",
100103
"For this project, you will build a survey form to collect data from your users."
101-
] },
104+
]
105+
},
102106
"build-a-technical-documentation-page-project": {
103107
"title": "Technical Documentation Page",
104108
"intro": [
105109
"This is one of the required projects to earn your certification.",
106110
"For this project, you will build a technical documentation page to serve as instruction or reference for a topic."
107-
] },
111+
]
112+
},
108113
"learn-html-by-building-a-cat-photo-app": {
109114
"title": "Learn HTML by Building a Cat Photo App",
110115
"intro": [
@@ -181,24 +186,66 @@
181186
"In this course, you'll use typography to build a nutrition label webpage. You'll learn how to style text, adjust line height, and position your text using CSS."
182187
]
183188
},
184-
"learn-css-transforms-by-building-a-penguin": { "title": "Learn CSS Transforms by Building a Penguin", "intro": [
185-
"You can transform HTML elements to create appealing designs that draw your reader's eye. You can use transforms to rotate elements, scale them, and more.",
186-
"In this course, you'll build a penguin. You'll use CSS transforms to position and resize the parts of your penguin, create a background, and animate your work."
187-
] },
188-
"learn-css-animation-by-building-a-ferris-wheel": { "title": "Learn CSS Animation by Building a Ferris Wheel", "intro": [
189-
"You can use CSS animation to draw attention to specific sections of your webpage and make it more engaging.",
190-
"In this course, you'll build a Ferris wheel. You'll learn how to use CSS to animate elements, transform them, and adjust their speed."
191-
] },
192-
"learn-more-about-css-pseudo-selectors-by-building-a-balance-sheet": { "title": "Learn More About CSS Pseudo Selectors By Building A Balance Sheet", "intro": [
193-
"You can use CSS pseudo selectors to change specific HTML elements.",
194-
"In this course, you'll build a balance sheet using pseudo selectors. You'll learn how to change the style of an element when you hover over it with your mouse, and trigger other events on your webpage."
195-
] },
189+
"learn-css-transforms-by-building-a-penguin": {
190+
"title": "Learn CSS Transforms by Building a Penguin",
191+
"intro": [
192+
"You can transform HTML elements to create appealing designs that draw your reader's eye. You can use transforms to rotate elements, scale them, and more.",
193+
"In this course, you'll build a penguin. You'll use CSS transforms to position and resize the parts of your penguin, create a background, and animate your work."
194+
]
195+
},
196+
"learn-css-animation-by-building-a-ferris-wheel": {
197+
"title": "Learn CSS Animation by Building a Ferris Wheel",
198+
"intro": [
199+
"You can use CSS animation to draw attention to specific sections of your webpage and make it more engaging.",
200+
"In this course, you'll build a Ferris wheel. You'll learn how to use CSS to animate elements, transform them, and adjust their speed."
201+
]
202+
},
203+
"learn-more-about-css-pseudo-selectors-by-building-a-balance-sheet": {
204+
"title": "Learn More About CSS Pseudo Selectors By Building A Balance Sheet",
205+
"intro": [
206+
"You can use CSS pseudo selectors to change specific HTML elements.",
207+
"In this course, you'll build a balance sheet using pseudo selectors. You'll learn how to change the style of an element when you hover over it with your mouse, and trigger other events on your webpage."
208+
]
209+
},
196210
"learn-css-colors-by-building-a-set-of-colored-markers": {
197211
"title": "Learn CSS Colors by Building a Set of Colored Markers",
198212
"intro": [
199213
"Selecting the correct colors for your webpage can greatly improve the aesthetic appeal to your readers.",
200214
"In this course, you'll build a set of colored markers. You'll learn different ways to set color values and how to pair colors with each other."
201-
] }
215+
]
216+
}
217+
}
218+
},
219+
"2022/responsive-web-design-qa": {
220+
"title": "(New) Responsive Web Design QA",
221+
"intro": [
222+
"In this Responsive Web Design Certification, you'll learn the languages that developers use to build webpages: HTML (Hypertext Markup Language) for content, and CSS (Cascading Style Sheets) for design.",
223+
"First, you'll build a cat photo app to learn the basics of HTML and CSS. Later, you'll learn modern techniques like CSS variables by building a penguin, and best practices for accessibility by building a web form.",
224+
"Finally, you'll learn how to make webpages that respond to different screen sizes by building a Twitter card with Flexbox, and a complex blog layout with CSS Grid."
225+
],
226+
"note": "Note: Some browser extensions, such as ad-blockers and dark mode extensions can interfere with the tests. If you face issues, we recommend disabling extensions that modify the content or layout of pages, while taking the course.",
227+
"blocks": {
228+
"build-a-personal-portfolio-webpage-project-qa": {
229+
"title": "Personal Portfolio Webpage QA",
230+
"intro": [
231+
"This is one of the required projects to earn your certification.",
232+
"For this project, you will build your own personal portfolio page."
233+
]
234+
},
235+
"learn-html-by-building-a-cat-photo-app-qa": {
236+
"title": "Learn HTML by Building a Cat Photo App QA",
237+
"intro": [
238+
"HTML tags give a webpage its structure. You can use HTML tags to add photos, buttons, and other elements to your webpage.",
239+
"In this course, you'll learn the most common HTML tags by building your own cat photo app."
240+
]
241+
},
242+
"learn-basic-css-by-building-a-cafe-menu-qa": {
243+
"title": "Learn Basic CSS by Building a Cafe Menu QA",
244+
"intro": [
245+
"CSS tells the browser how to display your webpage. You can use CSS to set the color, font, size, and other aspects of HTML elements.",
246+
"In this course, you'll learn CSS by designing a menu page for a cafe webpage."
247+
]
248+
}
202249
}
203250
},
204251
"javascript-algorithms-and-data-structures": {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import { useTranslation } from 'react-i18next';
3+
4+
function ExternalLink(
5+
props: JSX.IntrinsicAttributes & React.SVGProps<SVGSVGElement>
6+
): JSX.Element {
7+
const { t } = useTranslation();
8+
9+
return (
10+
<svg
11+
width='16'
12+
height='10'
13+
viewBox='0 0 16 10'
14+
xmlns='http://www.w3.org/2000/svg'
15+
{...props}
16+
>
17+
<title>{t('icons.chevron-down')}</title>
18+
<path
19+
fillRule='evenodd'
20+
clipRule='evenodd'
21+
d={
22+
'M0.292893 0.792893C0.683417 0.402369 1.31658 0.402369 1.70711 0.792893L8 ' +
23+
'7.08579L14.2929 0.792893C14.6834 0.402369 15.3166 0.402369 15.7071 ' +
24+
'0.792893C16.0976 1.18342 16.0976 1.81658 15.7071 2.20711L8.70711 ' +
25+
'9.20711C8.31658 9.59763 7.68342 9.59763 7.29289 9.20711L0.292893 ' +
26+
'2.20711C-0.0976311 1.81658 -0.0976311 1.18342 0.292893 0.792893Z'
27+
}
28+
fill='#137D60'
29+
/>
30+
</svg>
31+
);
32+
}
33+
34+
ExternalLink.displayName = 'ExternalLink';
35+
36+
export default ExternalLink;

client/src/assets/icons/index.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ const generateIconComponent = (
3535
className: string
3636
): JSX.Element => {
3737
// fallback in case super block doesn't exist and for tests
38-
const Icon = iconMap[superBlock] ? iconMap[superBlock] : ResponsiveDesign;
38+
const Icon = iconMap[superBlock as keyof typeof iconMap]
39+
? iconMap[superBlock as keyof typeof iconMap]
40+
: ResponsiveDesign;
3941

4042
return <Icon className={className} />;
4143
};

client/src/components/layouts/global.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ input[type='submit']:hover,
304304
.btn-primary.focus {
305305
background-color: var(--tc-turq-140);
306306
outline: 0 none;
307+
color: var(--tc-white);
307308
}
308309

309310
.btn-tab-primary:focus,

client/src/components/layouts/learn.tsx

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,8 @@ import { Loader } from '../../components/helpers';
66
import {
77
userSelector,
88
userFetchStateSelector,
9-
isSignedInSelector,
10-
tryToShowDonationModal
9+
isSignedInSelector
1110
} from '../../redux';
12-
import DonateModal from '../Donation/donation-modal';
13-
import createRedirect from '../create-redirect';
1411

1512
import './prism.css';
1613
import './prism-night.css';
@@ -38,32 +35,16 @@ const mapStateToProps = createSelector(
3835
})
3936
);
4037

41-
const mapDispatchToProps = {
42-
tryToShowDonationModal
43-
};
44-
45-
const RedirectEmailSignUp = createRedirect('/email-sign-up');
38+
const mapDispatchToProps = {};
4639

4740
type LearnLayoutProps = {
4841
isSignedIn?: boolean;
4942
fetchState: FetchState;
5043
user: User;
51-
tryToShowDonationModal: () => void;
5244
children?: React.ReactNode;
5345
};
5446

55-
function LearnLayout({
56-
isSignedIn,
57-
fetchState,
58-
user,
59-
tryToShowDonationModal,
60-
children
61-
}: LearnLayoutProps): JSX.Element {
62-
useEffect(() => {
63-
tryToShowDonationModal();
64-
// eslint-disable-next-line react-hooks/exhaustive-deps
65-
}, []);
66-
47+
function LearnLayout({ fetchState, children }: LearnLayoutProps): JSX.Element {
6748
useEffect(() => {
6849
return () => {
6950
const metaTag = document.querySelector(`meta[name="robots"]`);
@@ -77,19 +58,12 @@ function LearnLayout({
7758
return <Loader fullScreen={true} />;
7859
}
7960

80-
if (isSignedIn && !user.acceptedPrivacyTerms) {
81-
return <RedirectEmailSignUp />;
82-
}
83-
8461
return (
8562
<>
8663
<Helmet>
8764
<meta content='noindex' name='robots' />
8865
</Helmet>
8966
<main id='learn-app-wrapper'>{children}</main>
90-
{/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */
91-
/* @ts-ignore */}
92-
<DonateModal />
9367
</>
9468
);
9569
}

client/src/components/layouts/variables.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,12 @@
3737
--tc-link-blue-light: #5fb7ee;
3838
--tc-black: #000;
3939
--tc-black-100: #2a2a2a;
40+
--tc-black-20: #d4d4d4;
4041
--tc-black-10: #e9e9e9;
4142
--tc-black-5: #f4f4f4;
4243
--tc-blue-140: #16679a;
44+
--tc-blue-25: #bae1f9;
45+
--tc-blue-10: #eaf6fd;
4346
}
4447

4548
.dark-palette {

0 commit comments

Comments
 (0)