import { InsightDatasetKind, InsightError, ResultTooLargeError } from "../controller/IInsightFacade"; import QueryValidatorWhere from "./QueryValidatorWhere"; import QueryValidatorOptions from "./QueryValidatorOptions"; import { ValidatorInfo } from "./QueryProcessor"; import QueryValidatorTransformations from "./QueryValidatorTransformations"; import QueryPersister from "./QueryPersister"; export default class QueryValidator { constructor() {} private static mfieldsSections: string[] = ["avg", "pass", "fail", "audit", "year"]; private static sfieldsSections: string[] = ["dept", "id", "instructor", "title", "uuid"]; private static mfieldsRooms: string[] = ["lat", "lon", "seats"]; private static sfieldsRooms: string[] = [ "fullname", "shortname", "number", "address", "name", "type", "furniture", "href", ]; private static max = 3; public static validateQuery(query: any, validateInfo: ValidatorInfo): boolean { if (query === null || Array.isArray(query) || typeof query !== "object") { throw new InsightError("Query is not an object!"); } if ("WHERE" in query && "OPTIONS" in query && Object.keys(query).length === 2) { QueryValidatorWhere.validateQueryWhere(query.WHERE, validateInfo); QueryValidatorOptions.validateQueryOptions(query.OPTIONS, validateInfo); return true; } else if ( "TRANSFORMATIONS" in query && "WHERE" in query && "OPTIONS" in query && Object.keys(query).length === this.max ) { QueryValidatorWhere.validateQueryWhere(query.WHERE, validateInfo); QueryValidatorTransformations.validateTransformations(query.TRANSFORMATIONS, validateInfo); QueryValidatorOptions.validateQueryOptions(query.OPTIONS, validateInfo); return true; } throw new InsightError("Wrong object!"); } public static validateMKey(mkey: any, validateInfo: ValidatorInfo): boolean { if (typeof mkey === "string" && mkey.includes("_")) { const index = mkey.indexOf("_"); const idstring = mkey.substring(0, index); const key = mkey.substring(index + 1); this.validateIdString(idstring, validateInfo); if (validateInfo.type === "") { if (this.mfieldsSections.includes(key)) { validateInfo.type = "sections"; return true; } else if (this.mfieldsRooms.includes(key)) { validateInfo.type = "rooms"; return true; } } else { if (validateInfo.type === "sections" && this.mfieldsSections.includes(key)) { return true; } else if (validateInfo.type === "rooms" && this.mfieldsRooms.includes(key)) { return true; } } } throw new InsightError("Invalid mkey!"); } public static validateSKey(skey: any, validateInfo: ValidatorInfo): boolean { if (typeof skey === "string" && skey.includes("_")) { const index = skey.indexOf("_"); const idstring = skey.substring(0, index); const key = skey.substring(index + 1); this.validateIdString(idstring, validateInfo); if (validateInfo.type === "") { if (this.sfieldsSections.includes(key)) { validateInfo.type = "sections"; return true; } else if (this.sfieldsRooms.includes(key)) { validateInfo.type = "rooms"; return true; } } else { if (validateInfo.type === "sections" && this.sfieldsSections.includes(key)) { return true; } else if (validateInfo.type === "rooms" && this.sfieldsRooms.includes(key)) { return true; } } } throw new InsightError("Invalid skey!"); } public static validateKey(key: any, validateInfo: ValidatorInfo): string { if (typeof key === "string" && key.includes("_")) { const index = key.indexOf("_"); const idstring = key.substring(0, index); const akey = key.substring(index + 1); this.validateIdString(idstring, validateInfo); if (validateInfo.type === "") { if (this.sfieldsSections.includes(akey) || this.mfieldsSections.includes(akey)) { validateInfo.type = "sections"; return key; } else if (this.sfieldsRooms.includes(akey) || this.mfieldsRooms.includes(akey)) { validateInfo.type = "rooms"; return key; } } else { if ( validateInfo.type === "sections" && (this.sfieldsSections.includes(akey) || this.mfieldsSections.includes(akey)) ) { return key; } else if ( validateInfo.type === "rooms" && (this.sfieldsRooms.includes(akey) || this.mfieldsRooms.includes(akey)) ) { return key; } } } throw new InsightError("Invalid key!"); } public static validateIdString(idstring: any, validateInfo: ValidatorInfo): boolean { if (idstring.length > 0 && !idstring.includes("_")) { if (validateInfo.id === "") { validateInfo.id = idstring; } else if (validateInfo.id !== idstring) { throw new InsightError("Can only query one dataset!"); } return true; } throw new InsightError("Invalid id string!"); } public static validateAnykey(anykey: any, validateInfo: ValidatorInfo): string { if (typeof anykey === "string") { if (anykey.includes("_")) { return this.validateKey(anykey, validateInfo); } else if (anykey.length > 0) { return anykey; } } throw new InsightError("anykey is not string!"); } public static validateSize(size: number): void { const MAXSIZE = 5000; if (size > MAXSIZE) { throw new ResultTooLargeError(`InsightResult is over 5000, ${size}`); } } public static async validateKind(id: string, kind: InsightDatasetKind): Promise { const insights = await new QueryPersister().persistQuery("data/_insightDatasets.json"); try { const insight = insights.find((dataset: any) => dataset.id === id); if (insight ? insight.kind === kind : false) { return; } } catch (_e) { throw new InsightError("validateKind! no id or wrong dataset kind"); } } }