Skip to content

Commit

Permalink
Add author validation, fix validator early-exit (#176)
Browse files Browse the repository at this point in the history
  • Loading branch information
samtstern authored Aug 2, 2021
1 parent b8a139f commit fce8e16
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 31 deletions.
3 changes: 2 additions & 1 deletion shared/schema/BlogMetadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"type": "array",
"description": "The Dev Library ID of the author(s)",
"items": {
"type": "string"
"type": "string",
"pattern": "^[a-z0-9\\-_]+$"
}
},
"author": {
Expand Down
3 changes: 2 additions & 1 deletion shared/schema/RepoMetadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"type": "array",
"description": "The Dev Library ID of the author(s)",
"items": {
"type": "string"
"type": "string",
"pattern": "^[a-z0-9\\-_]+$"
}
},
"owner": {
Expand Down
12 changes: 2 additions & 10 deletions shared/scripts/addauthor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,8 @@ function authorFilePath(normalizedId: string) {
return path.join(getConfigDir(), "authors", `${normalizedId}.json`);
}

function authorExists(normalizedId: string) {
return fs.existsSync(authorFilePath(normalizedId));
}

export function githubAuthorExists(owner: string) {
return authorExists(normalizeAuthorId(owner));
}

export function mediumAuthorExists(username: string) {
return authorExists(normalizeAuthorId(username));
export function authorExists(owner: string) {
return fs.existsSync(authorFilePath(normalizeAuthorId(owner)));
}

export async function getMediumPostAuthor(url: string) {
Expand Down
9 changes: 4 additions & 5 deletions shared/scripts/addproject.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@ import {
addGithubAuthor,
addMediumAuthor,
getMediumPostAuthor,
mediumAuthorExists,
githubAuthorExists,
authorExists,
} from "./addauthor";
import { writeOrUpdateJSON, getConfigDir } from "./util";

Expand Down Expand Up @@ -123,7 +122,7 @@ export async function addMediumBlog(
// TODO: This doesn't work for proandroiodev, etc
const postAuthor = await getMediumPostAuthor(projectUrl);
if (postAuthor) {
if (!mediumAuthorExists(postAuthor)) {
if (!authorExists(postAuthor)) {
await addMediumAuthor(postAuthor);
}
}
Expand Down Expand Up @@ -195,12 +194,12 @@ export async function addRepo(
Object.assign(repoFileContent, overrides || {});

// Check if we have a matching author aready
if (!githubAuthorExists(owner)) {
if (!authorExists(owner)) {
await addGithubAuthor(owner);
}

// We check again to see if we skipped the author or not
if (githubAuthorExists(owner)) {
if (authorExists(owner)) {
repoFileContent.authorIds = [normalizeAuthorId(owner)];
} else {
repoFileContent.authorIds = [];
Expand Down
92 changes: 78 additions & 14 deletions shared/scripts/validate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { ProductConfig } from "../types";
import { AuthorMetadata } from "../types/AuthorMetadata";
import { BlogMetadata } from "../types/BlogMetadata";
import { RepoMetadata } from "../types/RepoMetadata";
import { authorExists } from "./addauthor";

const BlogMetadataSchema = require("../schema/BlogMetadata.json");
const RepoMetadataSchema = require("../schema/RepoMetadata.json");
Expand All @@ -33,6 +34,48 @@ v.addSchema(BlogMetadataSchema, "BlogMetadata");
v.addSchema(RepoMetadataSchema, "RepoMetadata");
v.addSchema(AuthorMetadataSchema, "AuthorMetadata");

class ErrorCollector {
constructor(private warnings: string[] = [], private errors: string[] = []) {}

public addWarning(message: string) {
this.warnings.push(message);
}

public addError(message: string) {
this.errors.push(message);
}

public hasErrors() {
return this.errors.length > 0;
}

public hasWarnings() {
return this.warnings.length > 0;
}

public printSummary() {
if (this.warnings.length > 0) {
console.log(`Warnings (${this.warnings.length}):`);
console.log("--------------------------");
for (const msg of this.warnings) {
console.log(` ⚠ ${msg}`);
}
console.log();
}

if (this.errors.length > 0) {
console.log(`Errors (${this.errors.length}):`);
console.log("--------------------------");
for (const msg of this.errors) {
console.log(` x ${msg}`);
}
console.log();
}
}
}

const collector = new ErrorCollector();

function validateTags(
fPath: string,
metadata: RepoMetadata | BlogMetadata,
Expand All @@ -43,51 +86,62 @@ function validateTags(

// If none of the tags are valid, we error out
if (invalid.length === metadata.tags.length) {
console.warn(
` x ${fPath} does not have any valid tags. Valid tags for ${
collector.addError(
`${fPath} does not have any valid tags. Valid tags for ${
product.key
} are: ${JSON.stringify(tags)}`
);
process.exit(1);
}

// If at least one tag is valid, we just warn
if (invalid.length > 0 && invalid.length < metadata.tags.length) {
console.warn(
` ! ${fPath} has some invalid tags :${JSON.stringify(
collector.addWarning(
`${fPath} has some invalid tags :${JSON.stringify(
metadata.tags
)}. Valid tags for ${product.key} are: ${JSON.stringify(tags)}`
);
}
}

function validateAuthor(fPath: string, metadata: RepoMetadata | BlogMetadata) {
if (!metadata.authorIds) {
return;
}

for (const author of metadata.authorIds) {
if (!authorExists(author)) {
collector.addError(
`${fPath} has invalid authorId "${author}", no such .json file exists`
);
}
}
}

function validateObj<T>(fPath: string, schema: any): T | undefined {
const fContent = fs.readFileSync(fPath).toString();
if (fContent.includes("TODO")) {
console.warn(
` x ${fPath} contains a 'TODO', did you forget to fill in the template?`
collector.addError(
`${fPath} contains a 'TODO', did you forget to fill in the template?`
);
process.exit(1);
}

let obj;
try {
obj = JSON.parse(fContent);
} catch (e) {
console.warn(` x ${fPath} is not valid JSON!`);
process.exit(1);
collector.addError(`${fPath} is not valid JSON!`);
}

const res = v.validate(obj, schema);
if (!res.valid) {
console.warn(` x ${fPath} is not valid!`);
let msg = `${fPath} is not valid!`;
for (const e of res.errors) {
console.warn(` ${e.property}: ${e.message}`);
msg += ` ${e.property}: ${e.message}`;
}
process.exit(1);
collector.addError(msg);
}

console.log(` ${fPath}`);
console.log(` - ${fPath}`);
return obj as T;
}

Expand Down Expand Up @@ -122,6 +176,7 @@ async function main() {
const metadata = validateObj<BlogMetadata>(fPath, BlogMetadataSchema);
if (metadata) {
validateTags(fPath, metadata, productConfig);
validateAuthor(fPath, metadata);
}
}
}
Expand All @@ -137,10 +192,19 @@ async function main() {
const metadata = validateObj<RepoMetadata>(fPath, RepoMetadataSchema);
if (metadata) {
validateTags(fPath, metadata, productConfig);
validateAuthor(fPath, metadata);
}
}
}
}

if (collector.hasWarnings() || collector.hasErrors()) {
collector.printSummary();
}

if (collector.hasErrors()) {
process.exit(1);
}
}

if (require.main === module) {
Expand Down

0 comments on commit fce8e16

Please sign in to comment.