Skip to content

Commit

Permalink
[#1756] Add JSON validation and migrate api.js to TypeScript (#1903)
Browse files Browse the repository at this point in the history
Currently, the objects produced by reading from summary.json,
authorship.json and commits.json are not validated against any schema.
This results in these objects having an implicit any type, which might
lead to type errors during runtime.

Let's add support for JSON validation with the zod library and migrate
the file where the JSON files are parsed, api.js, to TypeScript. This
will increase type safety and enable the migration of other files.
  • Loading branch information
vvidday authored Feb 21, 2023
1 parent dd6e0e9 commit ebd6f81
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 35 deletions.
5 changes: 5 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
"vue": "^3.2.47",
"vue-loading-overlay": "^5.0.3",
"vue-observe-visibility": "^1.0.0",
"vuex": "^4.0.2"
"vuex": "^4.0.2",
"zod": "^3.20.6"
},
"devDependencies": {
"@babel/eslint-parser": "^7.17.0",
Expand Down
47 changes: 47 additions & 0 deletions frontend/src/types/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { FileResult } from './zod/authorship-type';
import {
AuthorFileTypeContributions,
CommitResultRaw,
Commits,
} from './zod/commits-type';
import { RepoRaw } from './zod/summary-type';

// We add these three fields in setContributionOfCommitResultsAndInsertRepoId of utils/api.ts
export interface CommitResult extends CommitResultRaw {
repoId: string;
insertions: number;
deletions: number;
}

// Similar to AuthorDailyContributions, but uses the updated CommitResult with the three new fields
export interface DailyCommit {
commitResults: CommitResult[];
date: string;
}

// Similar to DailyCommit, but contains the total insertions and deletions for all CommitResults
export interface Commit extends DailyCommit {
deletions: number;
insertions: number;
}

export interface User {
checkedFileTypeContribution: number;
commits?: Commit[];
dailyCommits: DailyCommit[];
displayName: string;
fileTypeContribution: AuthorFileTypeContributions;
location: string;
name: string;
repoId: string;
repoName: string;
searchPath: string;
variance: number;
}

// We add these three fields in loadCommits and loadAuthorship of utils/api.ts
export interface Repo extends RepoRaw {
commits?: Commits;
files?: FileResult[];
users?: User[];
}
72 changes: 72 additions & 0 deletions frontend/src/types/window.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import JSZip from 'jszip';
import User from '../utils/user';
import { Repo, User as UserType } from './types';
import { AuthorshipSchema } from './zod/authorship-type';
import { AuthorDailyContributions } from './zod/commits-type';
import { DomainUrlMap, ErrorMessage } from './zod/summary-type';

// Declares the types for all the global variables under the window object
export {};

interface comparatorFunction {
(a: any, b: any): -1 | 0 | 1;
}

interface sortingFunction {
(item: any, sortingOption?: string): any;
}

interface api {
loadJSON: (fname: string) => Promise<any>;
loadSummary: () => Promise<{
creationDate: string,
reportGenerationTime: string,
errorMessages: { [key: string]: ErrorMessage },
names: string[],
} | null>;
loadCommits: (repoName: string) => Promise<User[]>;
loadAuthorship: (repoName: string) => Promise<AuthorshipSchema>;
setContributionOfCommitResultsAndInsertRepoId: (dailyCommits: AuthorDailyContributions[], repoId: string) => void;
}

declare global {
interface Window {
$: (id: string) => HTMLElement | null;
enquery: (key: string, val: string) => string;
REPOSENSE_REPO_URL: string;
HOME_PAGE_URL: string;
UNSUPPORTED_INDICATOR: string;
DAY_IN_MS: number;
HASH_DELIMITER: string;
REPOS: { [key: string]: Repo };
hashParams: { [key: string]: string };
isMacintosh: boolean;
REPORT_ZIP: JSZip | null;
deactivateAllOverlays: () => void;
getDateStr: (date: Date) => string;
getHexToRGB: (color: string) => number[];
getFontColor: (color: string) => string;
addHash: (newKey: string, newVal: string) => void;
removeHash: (key: string) => void;
encodeHash: () => void;
decodeHash: () => void;
comparator: (fn: sortingFunction, sortingOption: string) => comparatorFunction;
filterUnsupported: (string: string) => string | undefined;
getAuthorLink: (repoId: string, author: string) => string | undefined;
getRepoLinkUnfiltered: (repoId: string) => string;
getRepoLink: (repoId: string) => string | undefined;
getBranchLink: (repoId: string, branch: string) => string | undefined;
getCommitLink: (repoId: string, commitHash: string) => string | undefined;
getBlameLink: (repoId: string, branch: string, filepath: string) => string | undefined;
getHistoryLink: (repoId: string, branch: string, filepath: string) => string | undefined;
getGroupName: (group: UserType[], filterGroupSelection: string) => string;
getAuthorDisplayName: (authorRepos: Repo[]) => string;
api: api;
sinceDate: string;
untilDate: string;
repoSenseVersion: string;
isSinceDateProvided: boolean;
isUntilDateProvided: boolean;
DOMAIN_URL_MAP: DomainUrlMap;
}
}
22 changes: 22 additions & 0 deletions frontend/src/types/zod/authorship-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { z } from 'zod';

const lineSchema = z.object({
lineNumber: z.number(),
author: z.object({ gitId: z.string() }),
content: z.string(),
});

const fileResult = z.object({
path: z.string(),
fileType: z.string(),
lines: z.array(lineSchema),
authorContributionMap: z.record(z.number()),
});

// Contains the zod validation schema for the authorship.json file

export const authorshipSchema = z.array(fileResult);

// Export typescript types
export type AuthorshipSchema = z.infer<typeof authorshipSchema>;
export type FileResult = z.infer<typeof fileResult>;
36 changes: 36 additions & 0 deletions frontend/src/types/zod/commits-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { z } from 'zod';

const fileTypesAndContributionSchema = z.object({
insertions: z.number(),
deletions: z.number(),
});

const commitResult = z.object({
hash: z.string(),
messageTitle: z.string(),
messageBody: z.string(),
tags: z.array(z.string()).optional(),
fileTypesAndContributionMap: z.record(fileTypesAndContributionSchema),
});

const authorDailyContributionsSchema = z.object({
date: z.string(),
commitResults: z.array(commitResult),
});

const authorFileTypeContributionsSchema = z.record(z.number());

// Contains the zod validation schema for the commits.json file

export const commitsSchema = z.object({
authorDailyContributionsMap: z.record(z.array(authorDailyContributionsSchema)),
authorFileTypeContributionMap: z.record(authorFileTypeContributionsSchema),
authorContributionVariance: z.record(z.number()),
authorDisplayNameMap: z.record(z.string()),
});

// Export typescript types
export type Commits = z.infer<typeof commitsSchema>;
export type CommitResultRaw = z.infer<typeof commitResult>;
export type AuthorDailyContributions = z.infer<typeof authorDailyContributionsSchema>;
export type AuthorFileTypeContributions = z.infer<typeof authorFileTypeContributionsSchema>;
53 changes: 53 additions & 0 deletions frontend/src/types/zod/summary-type.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { z } from 'zod';

const locationSchema = z.object({
location: z.string(),
repoName: z.string(),
organization: z.string(),
domainName: z.string(),
});

const repoSchema = z.object({
location: locationSchema,
branch: z.string(),
displayName: z.string(),
outputFolderName: z.string(),
});

const errorSchema = z.object({
repoName: z.string(),
errorMessage: z.string(),
});

const urlSchema = z.object({
BASE_URL: z.string(),
BLAME_PATH: z.string(),
BRANCH: z.string(),
COMMIT_PATH: z.string(),
HISTORY_PATH: z.string(),
REPO_URL: z.string(),
});

const supportedDomainUrlMapSchema = z.record(urlSchema);

// Contains the zod validation schema for the summary.json file

export const summarySchema = z.object({
repoSenseVersion: z.string(),
reportGeneratedTime: z.string(),
reportGenerationTime: z.string(),
zoneId: z.string(),
reportTitle: z.string(),
repos: z.array(repoSchema),
errorSet: z.array(errorSchema),
sinceDate: z.string(),
untilDate: z.string(),
isSinceDateProvided: z.boolean(),
isUntilDateProvided: z.boolean(),
supportedDomainUrlMap: supportedDomainUrlMapSchema,
});

// Export typescript types
export type DomainUrlMap = z.infer<typeof supportedDomainUrlMapSchema>;
export type RepoRaw = z.infer<typeof repoSchema>;
export type ErrorMessage = z.infer<typeof errorSchema>;
Loading

0 comments on commit ebd6f81

Please sign in to comment.