Skip to content

Release 2021/04/09 (v1.8.5) #5472

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 20 commits into from
Apr 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -276,36 +276,35 @@ workflows:
branches:
only:
- develop
- feature/recommender-sync-develop
# This is alternate dev env for parallel testing
- "build-test":
context : org-global
filters:
branches:
only:
- FAQ-theme
only:
- free

# This is alternate dev env for parallel testing
- "build-qa":
context : org-global
filters:
branches:
only:
- bug-bash
- free
# This is beta env for production soft releases
- "build-prod-beta":
context : org-global
filters:
branches:
only:
- bug-bash
- free
# This is stage env for production QA releases
- "build-prod-staging":
context : org-global
filters:
branches:
only:
- develop
- feature/recommender-sync-develop
- "approve-smoke-test-on-staging":
type: approval
requires:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ exports[`Matches shallow shapshot 1 shapshot 1 1`] = `
<Connect(Container)
setFilterState={[MockFunction]}
/>
<div
className="src-shared-components-challenge-listing-___style__outage-banner___JSVnq"
>
<strong>
Heads up!
</strong>
We will be doing testing work on our Community Platform on April 12, 2021 between 7:30-9:30 UTC-4. We will do our best to ensure a smooth experience for our users but please be aware there may be unforeseen issues during that time. Thank you!
</div>
<div
className="src-shared-components-challenge-listing-___style__tc-content-wrapper___1MqlF"
>
Expand Down Expand Up @@ -64,6 +72,14 @@ exports[`Matches shallow shapshot 2 shapshot 2 1`] = `
<Connect(Container)
setFilterState={[MockFunction]}
/>
<div
className="src-shared-components-challenge-listing-___style__outage-banner___JSVnq"
>
<strong>
Heads up!
</strong>
We will be doing testing work on our Community Platform on April 12, 2021 between 7:30-9:30 UTC-4. We will do our best to ensure a smooth experience for our users but please be aware there may be unforeseen issues during that time. Thank you!
</div>
<div
className="src-shared-components-challenge-listing-___style__tc-content-wrapper___1MqlF"
>
Expand Down
433 changes: 433 additions & 0 deletions src/assets/images/thinking-face-laptop-tablet.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
433 changes: 433 additions & 0 deletions src/assets/images/thinking-face-mobile.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
124 changes: 103 additions & 21 deletions src/server/services/recruitCRM.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,62 @@ import fetch from 'isomorphic-fetch';
import config from 'config';
import qs from 'qs';
import _ from 'lodash';
import { logger } from 'topcoder-react-lib';
import GrowsurfService from './growsurf';
import { sendEmailDirect } from './sendGrid';

const FormData = require('form-data');

const JOB_FIELDS_RESPONSE = [
'id',
'slug',
'country',
'locality',
'city',
'name',
'custom_fields',
'enable_job_application_form',
'created_on',
'updated_on',
'min_annual_salary',
'salary_type',
'max_annual_salary',
'job_description_text',
];
const CANDIDATE_FIELDS_RESPONSE = [
'id',
'slug',
'first_name',
'last_name',
'email',
'contact_number',
'skill',
'resume',
'locality',
'salary_expectation',
'custom_fields',
];

/**
* Send email to Kiril/Nick for debuging gig application errors
* @param {Object} error the error
*/
function notifyKirilAndNick(error) {
logger.error(error);
sendEmailDirect({
personalizations: [
{
to: [{ email: '[email protected]' }, { email: '[email protected]' }],
subject: 'Gig application error alert',
},
],
from: { email: '[email protected]' },
content: [{
type: 'text/plain', value: `The error occured as JSON string:\n\n ${JSON.stringify(error)}`,
}],
});
}

/**
* Auxiliary class that handles communication with recruitCRM
*/
Expand Down Expand Up @@ -44,13 +96,17 @@ export default class RecruitCRMService {
return this.getJobs(req, res, next);
}
if (response.status >= 400) {
return res.send({
const error = {
error: true,
status: response.status,
url: `${this.private.baseUrl}/v1/jobs/search?${qs.stringify(req.query)}`,
});
errObj: await response.json(),
};
logger.error(error);
return res.send(error);
}
const data = await response.json();
data.data = _.map(data.data, j => _.pick(j, JOB_FIELDS_RESPONSE));
return res.send(data);
} catch (err) {
return next(err);
Expand All @@ -76,14 +132,17 @@ export default class RecruitCRMService {
return this.getJob(req, res, next);
}
if (response.status >= 400) {
return res.send({
const error = {
error: true,
status: response.status,
url: `${this.private.baseUrl}/v1/jobs/${req.params.id}`,
});
errObj: await response.json(),
};
logger.error(error);
return res.send(error);
}
const data = await response.json();
return res.send(data);
return res.send(_.pick(data, JOB_FIELDS_RESPONSE));
} catch (err) {
return next(err);
}
Expand All @@ -108,11 +167,14 @@ export default class RecruitCRMService {
return this.getJobs(req, res, next);
}
if (response.status >= 400) {
return res.send({
const error = {
error: true,
status: response.status,
url: `${this.private.baseUrl}/v1/jobs/search?${qs.stringify(req.query)}`,
});
errObj: await response.json(),
};
logger.error(error);
return res.send(error);
}
const data = await response.json();
if (data.current_page < data.last_page) {
Expand All @@ -133,13 +195,17 @@ export default class RecruitCRMService {
const pageData = await pageDataRsp.json();
data.data = _.flatten(data.data.concat(pageData.data));
}
return res.send(data.data);
return res.send(
_.map(data.data, j => _.pick(j, JOB_FIELDS_RESPONSE)),
);
})
.catch(e => res.send({
error: e,
}));
}
return res.send(data.data);
return res.send(
_.map(data.data, j => _.pick(j, JOB_FIELDS_RESPONSE)),
);
} catch (err) {
return next(err);
}
Expand All @@ -164,13 +230,17 @@ export default class RecruitCRMService {
return this.searchCandidates(req, res, next);
}
if (response.status >= 400) {
return res.send({
const error = {
error: true,
status: response.status,
url: `${this.private.baseUrl}/v1/candidates/search?${qs.stringify(req.query)}`,
});
errObj: await response.json(),
};
logger.error(error);
return res.send(error);
}
const data = await response.json();
data.data = _.map(data.data, j => _.pick(j, CANDIDATE_FIELDS_RESPONSE));
return res.send(data);
} catch (err) {
return next(err);
Expand Down Expand Up @@ -215,6 +285,8 @@ export default class RecruitCRMService {
form.custom_fields.push({
field_id: 6, value: `https://app.growsurf.com/dashboard/campaign/${config.GROWSURF_CAMPAIGN_ID}/participant/${growRes.id}`,
});
} else {
notifyKirilAndNick(growRes);
}
// clear the cookie
res.cookie(config.GROWSURF_COOKIE, '', {
Expand All @@ -231,12 +303,14 @@ export default class RecruitCRMService {
},
});
if (candidateResponse.status >= 300) {
return res.send({
const error = {
error: true,
status: candidateResponse.status,
url: `${this.private.baseUrl}/v1/candidates/search?email=${form.email}`,
errObj: await candidateResponse.json(),
});
};
notifyKirilAndNick(error);
return res.send(error);
}
let candidateData = await candidateResponse.json();
if (candidateData.data) {
Expand Down Expand Up @@ -265,13 +339,15 @@ export default class RecruitCRMService {
body: JSON.stringify(form),
});
if (workCandidateResponse.status >= 300) {
return res.send({
const error = {
error: true,
status: workCandidateResponse.status,
url: `${this.private.baseUrl}/v1/candidates${candidateSlug ? `/${candidateSlug}` : ''}`,
form,
errObj: await workCandidateResponse.json(),
});
};
notifyKirilAndNick(error);
return res.send(error);
}
candidateData = await workCandidateResponse.json();
// Attach resume to candidate if uploaded
Expand All @@ -286,7 +362,7 @@ export default class RecruitCRMService {
body: fileData,
});
if (fileCandidateResponse.status >= 300) {
return res.send({
const error = {
error: true,
status: fileCandidateResponse.status,
url: `${this.private.baseUrl}/v1/candidates/${candidateData.slug}`,
Expand All @@ -295,7 +371,9 @@ export default class RecruitCRMService {
file,
formHeaders,
errObj: await fileCandidateResponse.json(),
});
};
notifyKirilAndNick(error);
return res.send(error);
}
candidateData = await fileCandidateResponse.json();
}
Expand All @@ -314,14 +392,16 @@ export default class RecruitCRMService {
success: true,
});
}
return res.send({
const error = {
error: true,
status: applyResponse.status,
url: `${this.private.baseUrl}/v1/candidates/${candidateData.slug}/assign?job_slug=${id}`,
form,
candidateData,
errObj,
});
};
notifyKirilAndNick(error);
return res.send(error);
}
// Set hired-stage
const hireStageResponse = await fetch(`${this.private.baseUrl}/v1/candidates/${candidateData.slug}/hiring-stages/${id}`, {
Expand All @@ -337,13 +417,15 @@ export default class RecruitCRMService {
}),
});
if (hireStageResponse.status >= 300) {
return res.send({
const error = {
error: true,
status: hireStageResponse.status,
url: `$${this.private.baseUrl}/v1/candidates/${candidateData.slug}/hiring-stages/${id}`,
form,
errObj: await hireStageResponse.json(),
});
};
notifyKirilAndNick(error);
return res.send(error);
}
// respond to API call
const data = await applyResponse.json();
Expand Down
29 changes: 29 additions & 0 deletions src/server/services/sendGrid.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,33 @@ export const sendEmail = async (req, res) => {
}
};

/**
* Send email directly via the SendGrid API
* @param {Object} msg the payload
* @returns Promise
*/
export const sendEmailDirect = async (msg) => {
try {
const response = await fetch('https://api.sendgrid.com/v3/mail/send', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${config.SECRET.SENDGRID_API_KEY}`,
},
body: JSON.stringify(msg),
});
return response;
} catch (error) {
logger.error(error);
const { message, code, response } = error;
if (error.response) {
const { headers, body } = response;
return {
code, message, headers, body,
};
}
return { message };
}
};

export default undefined;
5 changes: 4 additions & 1 deletion src/shared/components/Contentful/Route.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { Route, Switch, Redirect } from 'react-router-dom';
import Viewport from 'components/Contentful/Viewport';
import PasswordScreen from 'components/Contentful/PasswordScreen';
import { isomorphy } from 'topcoder-react-utils';
import { removeTrailingSlash } from 'utils/url';

// Concatenates a base and segment and handles optional trailing slashes
const buildUrl = (base, segment) => `${_.trimEnd(base, '/')}/${_.trim(segment, '/')}`;
Expand Down Expand Up @@ -169,8 +170,10 @@ export default function ContentfulRoute(props) {
render={(data) => {
const { fields } = Object.values(data.entries.items)[0];
const url = path || buildUrl(baseUrl, fields.url);
// eslint-disable-next-line no-restricted-globals
const currentPathname = typeof location === 'undefined' ? '' : removeTrailingSlash(location.pathname);
const redirectToUrl = _.trim(fields.redirectToUrl);
return redirectToUrl ? (
return redirectToUrl && currentPathname === url ? (
<RedirectWithStatus status={301} from={url} to={redirectToUrl} />
) : (
<Route
Expand Down
2 changes: 1 addition & 1 deletion src/shared/components/GUIKit/JobListCard/index.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable max-len */
/**
* SearchCombo component.
* Job card component.
*/
import React from 'react';
import PT from 'prop-types';
Expand Down
Loading