Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
CarsonF committed Sep 28, 2023
2 parents decd790 + 943d254 commit a564e4c
Show file tree
Hide file tree
Showing 39 changed files with 2,000 additions and 1,086 deletions.
873 changes: 0 additions & 873 deletions .yarn/releases/yarn-3.4.1.cjs

This file was deleted.

874 changes: 874 additions & 0 deletions .yarn/releases/yarn-3.6.3.cjs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins:
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"

yarnPath: .yarn/releases/yarn-3.4.1.cjs
yarnPath: .yarn/releases/yarn-3.6.3.cjs

logFilters:
# discard these messages that flood the build log
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"engines": {
"node": ">=16"
},
"packageManager": "yarn@3.4.1",
"packageManager": "yarn@3.6.3",
"dependencies": {
"@apollo/client": "^3.7.6",
"@babel/runtime": "^7.20.13",
Expand Down
9 changes: 6 additions & 3 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@ import { ThemeProvider } from '@mui/material/styles';
import { LocalizationProvider } from '@mui/x-date-pickers';
import LogRocket from 'logrocket';
import setupLogRocketReact from 'logrocket-react';
import { ApolloProvider } from './api';
import { ApolloProvider, GqlSensitiveOperations } from './api';
import { LuxonCalenderDateUtils } from './common/LuxonCalenderDateUtils';
import { ConfettiProvider } from './components/Confetti';
import { Nest } from './components/Nest';
import { SnackbarProvider } from './components/Snackbar';
import { UploadProvider as FileUploadProvider } from './components/Upload';
import { SensitiveOperations } from './scenes/Authentication';
import { Root } from './scenes/Root';
import { createTheme } from './theme';

Expand All @@ -19,7 +18,11 @@ if (logRocketAppId) {
network: {
requestSanitizer(request) {
// Relies on operation name suffix which do in Apollo HttpLink config
if (SensitiveOperations.some((op) => request.url.endsWith('/' + op))) {
if (
[...GqlSensitiveOperations].some((op) =>
request.url.endsWith(`/${op}`)
)
) {
request.body = undefined;
}
return request;
Expand Down
28 changes: 18 additions & 10 deletions src/api/client/links/impersonation.link.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
import { setContext } from '@apollo/client/link/context';
import { pickBy } from 'lodash';
import { RefObject } from 'react';
import { GqlSensitiveOperations } from '../../operationsList';
import { Impersonation } from '../ImpersonationContext';

export const createImpersonationLink = (
ref?: RefObject<Impersonation | undefined>
) =>
setContext((req, prev) => ({
...prev,
headers: {
...prev.headers,
...pickBy({
'x-cord-impersonate-user': ref?.current?.user,
'x-cord-impersonate-roles': ref?.current?.roles?.join(','),
}),
},
}));
setContext((req, prev) => {
const isSensitiveOp = req.operationName
? GqlSensitiveOperations.has(req.operationName)
: false;
const impersonation =
ref?.current && !isSensitiveOp ? ref.current : undefined;
return {
...prev,
headers: {
...prev.headers,
...pickBy({
'x-cord-impersonate-user': impersonation?.user,
'x-cord-impersonate-role': impersonation?.roles?.join(','),
}),
},
};
});
46 changes: 44 additions & 2 deletions src/api/operationsList/operations.codegen.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,49 @@
import { plugin as basePlugin } from '@graphql-codegen/named-operations-object';
import type { PluginFunction } from '@graphql-codegen/plugin-helpers';
import { concatAST, FieldNode, visit } from 'graphql';

export const plugin: PluginFunction = async (...args) => {
const result = await basePlugin(...args);
return result ? result + ' as const' : result;
let result = await basePlugin(...args);
result = result ? result + ' as const;' : result;

const [schema, documents] = args;

const queries = Object.values(schema.getQueryType()?.getFields() ?? {});
const mutations = Object.values(schema.getMutationType()?.getFields() ?? {});
const sensitiveQueries = new Set(
[...queries, ...mutations]
.filter(
(f) => f.description && /^\s*@sensitive-secrets$/m.test(f.description)
)
.map((f) => f.name)
);
const sensitiveOperationsNames = new Set();
const allAst = concatAST(documents.flatMap((v) => v.document ?? []));
visit(allAst, {
OperationDefinition: (node) => {
if (
!node.name ||
(node.operation !== 'query' && node.operation !== 'mutation')
) {
return;
}
const fields = node.selectionSet.selections
.filter((s): s is FieldNode => s.kind === 'Field')
.map((s) => s.name.value);
for (const field of fields) {
if (sensitiveQueries.has(field)) {
sensitiveOperationsNames.add(node.name.value);
}
}
},
});

result += `
export const GqlSensitiveOperations: ReadonlySet<string> = new Set([
${[...sensitiveOperationsNames].map((n) => ` '${n}'`).join(',\n')}
]);
`;

return result;
};
17 changes: 17 additions & 0 deletions src/api/schema/typePolicies/lists/page-limit-pagination.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
ValueIteratee,
} from 'lodash';
import { isListNotEmpty, Nullable, splice, unwrapSecured } from '~/common';
import { VariantFragment } from '../../../../common/fragments';
import {
InputArg,
PaginatedListInput,
Expand Down Expand Up @@ -126,6 +127,22 @@ const mergeList = (
const readSecuredField = (field: string) => (ref: Reference) => {
const secured = readField(field, ref);
const fieldVal = unwrapSecured(secured);

// Hack to handle sorting with variants,
// whose sort keys do not match a field name that's a primitive.
// This logic needs to be exposed to pageLimitPagination
// Something like pageLimitPagination({
// getSortValue: {
// variant: (variant) => variant.key,
if (
fieldVal &&
typeof fieldVal === 'object' &&
'__typename' in fieldVal &&
fieldVal.__typename === 'Variant'
) {
return (fieldVal as VariantFragment).key;
}

return fieldVal;
};

Expand Down
14 changes: 8 additions & 6 deletions src/components/PeriodicReports/ReportLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CalendarDate, isSecured, Nullable, SecuredProp } from '~/common';
import { isSecured, Nullable, SecuredProp } from '~/common';
import { Redacted } from '../Redacted';
import { PeriodicReportFragment } from './PeriodicReport.graphql';

Expand All @@ -22,14 +22,16 @@ export const ReportLabel = ({
if (!rep) {
return null;
}
return (
<span css={{ whiteSpace: 'nowrap' }}>{getLabel(rep.start, rep.end)}</span>
);
return <span css={{ whiteSpace: 'nowrap' }}>{getReportLabel(rep)}</span>;
};

const getLabel = (start: CalendarDate, end: CalendarDate) =>
+start === +end
export const getReportLabel = (report?: Report) => {
const { start, end } = report ?? {};

if (!start || !end) return null;
return +start === +end
? 'Final'
: start.hasSame(end, 'month')
? start.toFormat('LLLL yyyy')
: `Q${start.fiscalQuarter} FY${start.fiscalYear}`;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fragment ProgressReportBreadcrumb on ProgressReport {
id
start
end
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Skeleton } from '@mui/material';
import { Nullable } from '~/common';
import { Breadcrumb } from '../Breadcrumb';
import { ReportLabel } from '../PeriodicReports/ReportLabel';
import { ProgressReportBreadcrumbFragment } from './ProgressReportBreadcrumb.graphql';

interface ProgressReportBreadcrumbProps {
data?: Nullable<ProgressReportBreadcrumbFragment>;
}

export const ProgressReportBreadcrumb = ({
data,
}: ProgressReportBreadcrumbProps) => (
<Breadcrumb to={data ? `/progress-reports/${data.id}` : undefined}>
{!data ? <Skeleton width={200} /> : <ReportLabel report={data} />}
</Breadcrumb>
);
1 change: 1 addition & 0 deletions src/components/ProgressReportBreadcrumb/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ProgressReportBreadcrumb';
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Skeleton } from '@mui/material';
import { IdFragment } from '~/common';
import { Breadcrumb, BreadcrumbProps } from '../Breadcrumb';
import { idForUrl } from '../Changeset';

interface ProgressReportListBreadcrumbProps extends BreadcrumbProps {
engagement?: IdFragment;
}

export const ProgressReportListBreadcrumb = ({
engagement,
...rest
}: ProgressReportListBreadcrumbProps) => (
<Breadcrumb
to={
engagement
? `/engagements/${idForUrl(engagement)}/reports/progress`
: undefined
}
{...rest}
>
{engagement ? 'Quarterly Reports' : <Skeleton width={200} />}
</Breadcrumb>
);
1 change: 1 addition & 0 deletions src/components/ProgressReportListBreadcrumb/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './ProgressReportListBreadcrumb';
29 changes: 15 additions & 14 deletions src/components/form/Dropzone.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Clear as ClearIcon } from '@mui/icons-material';
import {
Box,
IconButton,
List,
ListItem,
Expand All @@ -12,20 +13,17 @@ import { ReactNode } from 'react';
import { useDropzone } from 'react-dropzone';
import { makeStyles } from 'tss-react/mui';
import { Except } from 'type-fest';
import { extendSx, StyleProps } from '~/common';
import { fileIcon } from '../files/fileTypes';
import { FieldConfig, useField } from './useField';

const useStyles = makeStyles()(({ palette, spacing }) => {
const useStyles = makeStyles()(({ palette, spacing, shape }) => {
const dropzoneHoverStyle = {
backgroundColor: palette.grey[200],
borderColor: palette.primary.main,
};
return {
root: {
marginBottom: spacing(2),
},
dropzone: {
backgroundColor: palette.grey[300],
borderRadius: shape.borderRadius,
border: `2px dashed ${palette.divider}`,
cursor: 'pointer',
padding: spacing(3),
Expand All @@ -42,17 +40,20 @@ const useStyles = makeStyles()(({ palette, spacing }) => {
};
});

export type DropzoneFieldProps = Except<FieldConfig<File, true>, 'multiple'> & {
label?: ReactNode;
multiple?: boolean;
className?: string;
};
export type DropzoneFieldProps = Except<FieldConfig<File, true>, 'multiple'> &
StyleProps & {
label?: ReactNode;
multiple?: boolean;
disableFileList?: boolean;
};

export function DropzoneField({
multiple = false,
label = 'Click or drag files here',
name: nameProp,
className,
sx,
disableFileList,
}: DropzoneFieldProps) {
const { classes, cx } = useStyles();

Expand Down Expand Up @@ -91,7 +92,7 @@ export function DropzoneField({
});

return (
<div className={cx(classes.root, className)}>
<Box sx={[{ mb: 2 }, ...extendSx(sx)]} className={className}>
<div
className={cx(classes.dropzone, {
[classes.active]: isDragActive,
Expand All @@ -103,7 +104,7 @@ export function DropzoneField({
{label}
</Typography>
</div>
{currentFiles.length > 0 && (
{!disableFileList && currentFiles.length > 0 && (
<List dense className={classes.files}>
{currentFiles.map((file, index) => {
const { name, type } = file;
Expand All @@ -129,6 +130,6 @@ export function DropzoneField({
})}
</List>
)}
</div>
</Box>
);
}
4 changes: 3 additions & 1 deletion src/components/form/SelectField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ export function SelectField<T, Multiple extends boolean | undefined>({
</MenuItem>
))}
</Select>
<FormHelperText>{getHelperText(meta, helperText)}</FormHelperText>
{helperText !== false && (
<FormHelperText>{getHelperText(meta, helperText)}</FormHelperText>
)}
</FormControl>
);
}
Loading

0 comments on commit a564e4c

Please sign in to comment.