<html>
<head>
<style>
h1 {
text-align: center;
}
#welcomePanel {
display: none;
text-align: center;
}
#signInPanel h2 {
text-align: center;
border: 1px solid silver;
}
#google-auth-button {
-webkit-box-align: baseline;
align-items: baseline;
border-width: 0px;
border-radius: 3px;
box-sizing: border-box;
display: inline-flex;
font-size: inherit;
font-style: normal;
font-family: inherit;
max-width: 100%;
position: relative;
text-align: center;
text-decoration: none;
transition: background 0.1s ease-out 0s, box-shadow 0.15s cubic-bezier(0.47, 0.03, 0.49, 1.38) 0s;
white-space: nowrap;
cursor: pointer;
padding: 0px 10px;
vertical-align: middle;
width: 100%;
-webkit-box-pack: center;
justify-content: center;
font-weight: bold;
color: var(--ds-text,#42526E) !important;
height: 40px !important;
line-height: 40px !important;
background: rgb(255, 255, 255) !important;
box-shadow: rgb(0 0 0 / 20%) 1px 1px 5px 0px !important;
}
</style>
<!-- First we have to include Apple Sign-in script -->
<script
type="text/javascript"
src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
</head>
<body>
<script>
function parseJwt (token) {
var base64Url = token.split('.')[1];
var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
var jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function(c) {
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
}).join(''));
return JSON.parse(jsonPayload);
}
/**
* This function will initialize the `AppleID.auth` object with parameter we pass in.
*/
const initApple = () => {
window.AppleID.auth.init({
clientId: "com.toni-develops.sign-in-with-apple-identifier", // This is the service ID we created.
scope: "name email", // To tell apple we want the user name and emails fields in the response it sends us.
redirectURI: "https://www.toni-develops.com/external-files/examples/oauth-with-apple-using-js-library", // As registered along with our service ID
state: "origin:web", // Any string of your choice that you may use for some logic. It's optional and you may omit it.
usePopup: true, // Important if we want to capture the data apple sends on the client side.
});
};
/**
* This function is where the magic happens.
* This is a simple example, ideally you'll have catch block as well to handle authentication failure.
*/
const singInApple = async () => {
const response = await window.AppleID.auth.signIn();
return response;
};
initApple();
function signInWithApple() {
const userData = singInApple();
userData.then( (data) => {
console.dir(data, { depth: null });
const result = parseJwt(data.authorization.id_token)
console.dir(result, { depth: null });
document.querySelector('#signInPanel').innerHTML = '<h2>Welcome ' + result.email + '</h2>';
});
}
</script>
<h1>Sign-In with Apple using Java Script library example</h1>
<div id="signInPanel">
<button id="google-auth-button" class="css-11s2kpt" type="button" tabindex="0" onclick="signInWithApple()">
<span class="css-1ujqpe8">
<img class="appleLogo" src="https://www.toni-develops.com/external-files/examples/assets/apple-logo.svg" alt="">
</span>
<span class="css-19r5em7"><span>Continue with Apple</span>
</button>
</div>
</body>
</html>
Given two strings text1 and text2, return the length of their longest common subsequence. If there is no common subsequence, return 0.
A subsequence of a string is a new string generated from the original string with some characters (can be none) deleted without changing the relative order of the remaining characters.
For example, "ace" is a subsequence of "abcde".
A common subsequence of two strings is a subsequence that is common to both strings.
Example 1:
Input:
text1 = "abcde", text2 = "ace"
Output:
3
Explanation:
The longest common subsequence is "ace" and its length is 3.
Example 2:
Input:
text1 = "abc", text2 = "abc"
Output:
3
Explanation:
The longest common subsequence is "abc" and its length is 3.
Example 3:
Input:
text1 = "abc", text2 = "def"
Output:
0
Explanation:
There is no such common subsequence, so the result is 0.
Constraints:
1 <= text1.length, text2.length <= 1000
text1 and text2 consist of only lowercase English characters.
This problem was taken from Leetcode Longest Common Subsequence
Solution
Dynamic programming with memoization.
We create a matrix to store (memoize) the response from previous calculations so we won’t re-calculate them again.
We are starting from the first row comparing every character from column 1 with the first character from row one.
There are two cases:
either the character match, then we add 1 and add it with the value from the diagonal to the left of the cell where we are.
Longest Common Substring Step 1
the characters don’t match, then we get the MAX of the value above current cell and the value to the left of the current cell.
Longest Common Substring Step 2
Here again c int the column (first string) matches c in the row (the second string)
so we get the value of last comparison (to the left and up diagonal of the current cell) which is 1 and add 1 again since characters match.
Longest Common Substring 3
and keep going till we reach the end of both strings which is the answer.
Longest Common Substring 4
/**
* @param {string} text1
* @param {string} text2
* @return {number}
*/
var longestCommonSubsequence = function(text1, text2) {
var txt1 = text1.split('');
var txt2 = text2.split('');
txt1.unshift('0');
txt2.unshift('1');
var l1 = txt1.length;
var l2 = txt2.length;
var matrix = [];
for(var i = 0; i < l2; i ++) {
matrix[i] = new Array(l1).fill(0);
}
var maxCommon = 0;
for(var row = 0; row < l2; row ++) {
for(var col = 0; col < l1; col ++) {
var last = 0;
if(txt1[col] == txt2[row]) {
var previousDiagonalRowVal = row == 0 || col == 0 ? 0 : matrix[row - 1][col - 1];
last = 1 + previousDiagonalRowVal;
}
else {
var prevUp = row == 0 ? 0 : matrix[row - 1][col];
var prevLeft = col == 0 ? 0 : matrix[row][col - 1];
last = Math.max(prevUp, prevLeft);
}
matrix[row][col] = last;
maxCommon = last > maxCommon ? last : maxCommon;
}
}
return maxCommon;
};
var text1 = "abcde", text2 = "ace";
var r = longestCommonSubsequence(text1, text2);
console.log(">>", r);
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
image was borrowed from leetcode
The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!
The brute force approach: for each element we go to the right and find the maximum height of the bar, then we go to the left and do the same.
For any element the maximum amount of the water that could be trapped will be the minimum of left height and right height, minus the height of the bar.
So for the array [0,1,0,2,1,0,1,3,2,1,2,1] we go all the way to the right and calculate the max right value, starting from first element ‘0’ max right will be 0. ‘1’ – max right is ‘1’ and so on.
We repeat the same from last element ‘1’ to the first one.
Then the trapped water for the first column will be: min(maxRight, maxLeft) – theArrayElement[n]
the array
0
1
0
2
1
0
1
3
2
1
2
1
max right
0
1
1
2
2
2
2
3
3
3
3
3
max left
3
3
3
3
3
3
3
3
2
2
2
1
collected
water
0
0
1
0
1
2
1
0
0
1
0
0
The complexity will be O(n2)
/**
* @param {number[]} height
* @return {number}
*/
var trap = function(height) {
if(height.length < 2)
return 0;
let findMaxLeft = function(idx, height) {
let max = 0;
for(let i =idx;i >= 0; i --) {
max = Math.max(max, height[i]);
}
return max;
}
let findMaxRight = function(idx, height) {
let max = 0;
for(let i = idx;i < height.length; i ++) {
max = Math.max(max, height[i]);
}
return max;
}
let collectedWater = 0;
for(let i = 0;i < height.length; i ++) {
const maxLeft = findMaxLeft(i, height);
const maxRight = findMaxRight(i, height);
let min = Math.min(maxLeft, maxRight);
collectedWater += (min - height[i]);
}
return collectedWater;
};
The better solution: find all max left and max right with one loop, then do a second loop for each element in the array, and calculate trapped water.
/**
* @param {number[]} height
* @return {number}
*/
var trap = function(height) {
let maxLeftArray = [], maxRightArray = [];
let maxLeft = 0, maxRight = 0;
const ln = height.length;
let trappedWater = 0;
for(let i = 0;i < height.length; i ++) {
maxLeftArray[i] = Math.max(height[i], maxLeft);
maxLeft = maxLeftArray[i];
maxRightArray[ln - i - 1] = Math.max(height[ln - i - 1], maxRight);
maxRight = maxRightArray[ln - i - 1];
}
for(let i = 0;i < height.length; i ++) {
trappedWater += Math.min(maxLeftArray[i], maxRightArray[i]) - height[i];
}
return trappedWater;
};
what we just did:
– With one loop find the max left and right bar on each side.
– for any element the maximum amount of the water that could be trapped will be the minimum of left height and right height, minus the height of the bar.
The brute force solution could be to make two loops and iterate through each elements in the array and repeatedly swapping the adjacent elements if they are in wrong order. This approach is called Bubble sort.
In the worst case scenario the complexity will be O(n*n) since we have to iterate n*n times where n is the number of elements in the array.
A better performing solution is to keep splitting the array into two halves till we have single elements. Then we start merging the arrays and sorting at the same time. This is called merge sort algorithm.
The diagram is borrowed from Wikipedia.
Merge sort algorithm implementation:
/**
* @param {number[]} nums
* @return {number[]}
*/
var sortArray = function(nums) {
function mergeArray(nums, start, mid, end) {
var i = start, j = mid + 1;
var tempArr = [];
// compare till we reach either end of one of the halves
for(var k = start;(i < mid+1 && j < end+1); k++) {
if(nums[i] <= nums[j]) {
tempArr.push(nums[i]);
i++;
}
else {
tempArr.push(nums[j]);
j ++;
}
}
// add the rest from the first half
for(var k = j;k < end + 1; k++) {
tempArr.push(nums[k]);
}
// add the rest from the second half
for(var k = i;k < mid + 1; k++) {
tempArr.push(nums[k]);
}
// set up nums with sorted values
for(var k = start;k < end+1; k++) {
nums[k] = tempArr[k - start];
}
}
function mergeSort(nums, start, end) {
var mid = Math.floor((start + end) / 2);
if(start < end) {
mergeSort(nums, start, mid);
mergeSort(nums, mid + 1, end);
mergeArray(nums, start, mid, end);
}
}
mergeSort(nums, 0, nums.length - 1);
return nums;
}
var nums = [5,4,6,1,2,3];
var result = sortArray(nums);
console.log(result);
What we just did?
– started to split the array by half (line 39,40) which recursively calls mergeSort and keep splitting on smaller and smaller pieces till the array has only 1 element (line 37)
Let’s make the task more challenging and assume that we are going to have two different Brands (or apps)
one.localhost.com,
two.localhost.com,
for the most of the cases they will do the same thing but the styling will be different, so let’s add ability for our components to be styled in different ways, depending of the brand.
Passing the sub-domain to the PageLayout component
Let’s add new parameter to store the default brand.
what we just did: – (line 23) getting the sub domain, this time from the express request object, since this is happening on the server side.
– (line 30) passing extra parameter with the subDomain to the PageComponent
Now this will work fine for the production build, but if you run yarn start-api it will break with Invalid Host header message. This is a security feature and we need to tell Webpack dev server that we are going to have sub-domains.
This could be done by passing disableHostCheck: true and since we are using WebpackDevServer only for development this should be safe enough.
./server-api.js
import WebpackDevServer from 'webpack-dev-server';
import webpack from 'webpack';
import config from './webpack.api.config.js';
require('dotenv').config();
console.log(">>>" + process.env.GRAPHQL_URL);
const compiler = webpack(config);
const server = new WebpackDevServer(compiler, {
hot: true,
publicPath: config.output.publicPath,
historyApiFallback: true,
disableHostCheck: true,
});
server.listen(8080, 'localhost', function() {});
For the CLI config, if will look like this: --disable-host-check
This could be achieved in many different ways:
– we could either use redux and add the brand property there, exposing it to all connected components (which we will do later in this tutorial)
– or we could simply pass it as a property from the PageLayout component.
Let’s do the second one since it is straight forward.
Coming up with a good naming convention and folder structure is very important, but I will leave this to you to research.
I would propose the simplest one:
component
|- brands
| |_ one
| |- styles.scss
| |_ two
| |- styles.scss
|- index.js
./components/Home/index.js
import React from 'react';
const Home = ( {subDomain} ) => {
const styles = require(`./brands/${subDomain}/styles.scss`);
return (
<div>
<div className={styles.wrapper}>This is my home section!</div>
</div>
)
}
export default Home;
yarn start
hit one.localhost:3006/home and then two.localhost:3006/home and you will notice the difference right away!
But how this was achieved without re-rendering the component?
Let’s look at component’s css file (it might be 1.css or different number but look at the net tab and make sure that the CSS contains something like this):
– first brand has .one-wrapper{background:#8d8dac;color:#fff} and the second has two-wrapper{background:#fff;color:#000} and all CSS that is in common is added like this: .one-wrapper,.two-wrapper{text-align:center;font-family:MyFont} so no repeating CSS if it’s the same between brands.
Basically now the css file for this component will have all styling for both brands. This is nice but not perfect. We still load unnecessarily the styles for the brands that we are not in to. Let’s see how to fix this in the next chapter.
what we just did:
– we created a plain react component that will accept 3 parameters: – content [string]: rendered components returned from react-apollorenderToStringWithData(…) – cssBundles [array of CSS bundle objects]: – jsBundles [array of js bundle objects]:
– apolloClient [Object]: An instance of the Apollo client.
And now, let’s remove the HTML coder from ./server.js and add the new component instead, passing all necessary parameters.
./server-js
import React from 'react';
import express from 'express';
import App from './src/components/App/ssr-index';
import Loadable from 'react-loadable';
import manifest from './dist/loadable-manifest.json';
import { getDataFromTree } from "react-apollo";
import { ApolloClient } from 'apollo-client';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { renderToStringWithData } from "react-apollo"
import { createHttpLink } from 'apollo-link-http';
import { getBundles } from 'react-loadable/webpack';
import ReactDOMServer from 'react-dom/server';
import Html from './html.js';
const PORT = process.env.PROD_SERVER_PORT;
const app = express();
app.use('/server-build', express.static('./server-build'));
app.use('/dist', express.static('dist')); // to serve frontent prod static files
app.use('/favicon.ico', express.static('./src/images/favicon.ico'));
app.get('/*', (req, res) => {
const GRAPHQL_URL = process.env.GRAPHQL_URL;
const client = new ApolloClient({
ssrMode: true,
link: createHttpLink({
uri: GRAPHQL_URL,
fetch: fetch,
credentials: 'same-origin',
headers: {
cookie: req.header('Cookie'),
},
}),
cache: new InMemoryCache()
});
// Prepare to get list of all modules that have to be loaded for this route
const modules = [];
const mainApp = (
<Loadable.Capture report={moduleName => modules.push(moduleName)}>
<App req={req} client={client} />
</Loadable.Capture>
);
// Execute all queries and fetch the results before continue
getDataFromTree(mainApp).then(() => {
// Once we have the data back, this will render the components with the appropriate GraphQL data.
renderToStringWithData(<App req={req} client={client} />).then( (HTML_content) => {
// Extract CSS and JS bundles
const bundles = getBundles(manifest, modules);
const cssBundles = bundles.filter(bundle => bundle && bundle.file.split('.').pop() === 'css');
const jsBundles = bundles.filter(bundle => bundle && bundle.file.split('.').pop() === 'js');
const html = <Html content={HTML_content} cssBundles={cssBundles} jsBundles={jsBundles} apolloClient={client} />;
res.status(200);
res.send(`<!doctype html>\n${ReactDOMServer.renderToStaticMarkup(html)}`);
res.end();
});
}).catch( (error) => {
console.log("ERROR !!!!", error);
});
});
Loadable.preloadAll().then(() => {
app.listen(PORT, () => {
console.log(`? Server is listening on port ${PORT}`);
});
});
Starting with the first number 3 we assume that this is our smallest number since we don’t know the others.
Then we iterate through the whole array comparing 3 with other numbers.
If we find number that is smaller than 3 (in our case 1) this will become our smallest number, and we continue comparing it to the end of the array.
when we reach the end of the array we swap the first number, that we assumed that is the smallest one with the newly discovered smallest number (in our case number 1 at third position), and the array will look like this now: [1,15,3,5]
Now as we know for sure that the number at the first position (number 1) is the smallest one, we can start from position 2 and repeat steps 2 to 5 till we checked all numbers in the array.
function sortArray(list) {
var smallestIndex;
for(var i = 0; i < list.length; i ++) {
smallestIndex = i;
for(var ii = i + 1; ii < list.length;ii ++) {
smallestIndex = list[ii] < list[smallestIndex] ? ii : smallestIndex;
}
var larger = list[i];
list[i] = list[smallestIndex];
list[smallestIndex] = larger;
}
return list;
}
var listArray = [3,15,1,5];
var result = sortArray(listArray);
console.log(result);
Better approach:
Merge sort algorithm.
Let’s consider this example with adding more numbers: [3,15,1,5,4,9]
If you are not familiar with these tools please read the links first since we will focus mainly on installing and using the tools.
The basic idea is that we are going to create a test cases, that will render the component, and create a snapshot of it. Next time when we run the test we could compare with the snapshot, making sure that either the component is not changed, or it renders as expected.
Setting up Jest and Enzyme.
We will need some extra packages in order to make Jest work with Enzyme.
Installing the packages.
enzyme – the actual enzyme testing utility.
enzyme-adapter-react-16 – the react adapter for enzyme. There are different adapters for different React versions, and since we are using react 16 we will install this version. A full list of adapter packages could be found here
enzyme-to-json – a serializer for the components.
yarn add jest enzyme enzyme-adapter-react-16 enzyme-to-json --dev
In addition we have to also install:
yarn add babel-jest --dev
babel-jest – allows Jest to use babel to transpile JS.
Before using Enzyme we have to configure it first. Create src/setupTests.js file with the following contents:
src/setupTests.js
import { configure } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
This tells Enzyme, to run with React using the adaptor we just installed. Make sure this file is included before using Enzyme. Open package.json and add the following config:
Now we are almost ready to write our first test. Header component is perfect candidate since it is not connected to the redux store.
Create new file in the ./src/components/Header folder and let’s
Add a Snapshot Test
./src/components/Header/index.test.js
import React from 'react';
import { shallow } from 'enzyme';
import Header from './index';
import toJson from 'enzyme-to-json';
describe('Testing Header component', () => {
it('renders as expected!!!', () => {
const wrapper = shallow(
<Header title="Title test" />
);
expect(toJson(wrapper)).toMatchSnapshot();
});
});
unfortunately if we do yarn test it will fail with error code, since Jest doesn’t know how to deal with anything different than Java Script.
But there is an easy fix: we could map CSS to a JS file that Jest understands. Create empty file in
Now let’s run yarn test and we should see something like this:
yarn test
yarn run v1.12.3
$ jest
PASS src/components/Header/index.test.js
Testing Home component
✓ renders as expected!!! (15ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 1 passed, 1 total
Time: 2.194s
Ran all test suites.
✨ Done in 3.05s.
Perfect ! Our first snapshot test is done.
Testing renderer component
src/components/Home/renderer.test.js
import React from 'react';
import { shallow, mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import Component from './renderer';
describe('Testing Home component', () => {
it('renders as expected!!!', () => {
const wrapper = shallow(<Component styles={{}} title="MyTest" />);
expect(toJson(wrapper)).toMatchSnapshot();
expect(wrapper.contains(<div>MyTest</div>)).toBe(true);
});
});
Also we added expect(wrapper.contains(<div>MyTest</div>)).toBe(true); which looks for certain things into the component.
Testing Redux connected component.
./src/components/About/index.test.js
import React from 'react';
import { mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import { Provider } from 'react-redux';
import reducers from '../../reducers';
import { createStore} from 'redux';
import About from './index.js';
const store = createStore(reducers, {});
let wrapper;
describe('Testing About component', () => {
beforeEach(() => {
// Runs a function before each of the tests in this file runs
wrapper = mount(
<Provider store={store}>
<About userName="Toni" />
</Provider>
);
});
it('renders as expected', () => {
expect(toJson(wrapper)).toMatchSnapshot();
});
it('expect to have state.userName set to John', () => {
wrapper.setState({userName: "John"});
expect(wrapper.state('userName')).toEqual("John");
});
});
since we wrapped MyComponent with Provider component, we have to mount the component that will render it in full instead of doing shallow rendering (like in the previous example). Otherwise shallow will render only the Provider component.
Now the two tests could be executed together yarn test
PASS src/components/About/index.test.js
Testing About component
✓ renders as expected (56ms)
✓ expect to have state.userName set to John (14ms)
Test Suites: 1 passed, 1 total
Tests: 2 passed, 2 total
Snapshots: 1 passed, 1 total
Time: 3.102s
Ran all test suites matching /.\/node_modules\/jest\/bin\/jest.js|src\/components\/About\/index.test.js/i.
Adding more tests.
./src/components/Greetings/renderer.test.js
import React from 'react';
import { mount } from 'enzyme';
import toJson from 'enzyme-to-json';
import reducers from '../../reducers';
import { createStore} from 'redux';
import Component from './renderer';
const store = createStore(reducers, {});
let wrapper;
describe('Testing Greetings component', () => {
beforeEach(() => {
wrapper = mount(
<Component styles={{}} store={store} />
);
});
it('renders as expected', () => {
// to snapshot test
expect(toJson(wrapper)).toMatchSnapshot();
});
it('textbox does not exist', () => {
expect(wrapper.find('input').exists()).toBe(false);
});
it('textbox exists after h2 click', () => {
// simulate click on the h2 tag trigering props change and visualizing the input text box
wrapper.find('h2').simulate('click');
expect(wrapper.find('input').exists()).toBe(true);
});
it('textbox values change properly', () => {
// text box value tests
expect(wrapper.find('input').props().value).toBe('No Name');
wrapper.find('input').props().value = 'test';
expect(wrapper.find('input').props().value).toBe('test');
});
});
beforeEach – Runs a function before each of the tests in this file runs. If the function returns a promise or is a generator, Jest waits for that promise to resolve before running the test.
here is another way of passing the store directly to the component.
Middleware is a good way to pre-process the request before handing it out to GraphQL.
We could add authentication that could check the auth token in the header or middleware that will log the request IPs. Let’s do the second one.
Adding middleware could be done the same way as we are doing it in Express app. Add the highlighted lines, and let’s see what they do.
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
var dogs = require('./src/models/mock_data/dogs.js');
// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
type Mutation {
setNewDog(id: String, breed: String, displayImage: String): String
}
type dog {
id: String,
breed: String,
displayImage: String
}
type dogsCatalog {
getDogByBreed(breed: String): dog
}
type Query {
queryDogs: dogsCatalog
}
`);
class dogsCatalog {
getDogByBreed({breed}) {
var result = dogs.find(function(dog){
return breed == dog.breed ? dog : null;
});
return result;
}
}
// The root provides a resolver function for each API endpoint
var root = {
queryDogs: () => {
return new dogsCatalog();
},
setNewDog: ({id, breed, displayImage}) => {
var result = dogs.find(function(dog){
return breed == dog.breed ? dog : null;
});
if(result != null) {
return 'dog already exists!';
}
dogs.push({
"breed": breed,
"displayImage": displayImage,
"id": id
});
return 'dog added successfully !';
}
};
// Logger middleware
var logger = function(req, res, next) {
console.log("GOT REQUEST >", req.ip);
next(); // Passing the request to the next handler in the stack.
}
var app = express();
app.use(logger);
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4001);
console.log('Running a GraphQL API server at localhost:4000/graphql');
what we just did: – we created the middleware object to log every request ip (lines 52-56)
– in the middleware we added the next() function to proceed to the next middleware none we are done with the request processing.
– we told Express to use our new middleware app.use(logger);
And as simple as that we created our own logging class.