groq-builder
Version:
A **schema-aware**, strongly-typed GROQ query builder. It enables you to build GROQ queries using **auto-completion**, **type-checking**, and **runtime validation**.
103 lines • 4.48 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("../types/utils");
const groq_builder_1 = require("../groq-builder");
const validate_utils_1 = require("./validate-utils");
const conditional_types_1 = require("./conditional-types");
const simple_validation_1 = require("../validation/simple-validation");
groq_builder_1.GroqBuilder.implement({
project(projectionMapArg, ...__projectionMapTypeMismatchErrors) {
// Retrieve the projectionMap:
let projectionMap;
if (typeof projectionMapArg === "function") {
projectionMap = projectionMapArg(this.root);
}
else {
projectionMap = projectionMapArg;
}
const keys = Object.keys(projectionMap);
// Compile query from projection values:
const fields = keys
.map((key) => {
const fieldConfig = projectionMap[key];
return normalizeProjectionField(key, fieldConfig);
})
.filter(utils_1.notNull);
if (this.internal.options.validationRequired) {
// Validate that we have provided validation functions for all fields:
const invalidFields = fields.filter((f) => !f.parser);
if (invalidFields.length) {
throw new TypeError("[groq-builder] Because 'validationRequired' is enabled, " +
"every field must have validation (like `q.string()`), " +
"but the following fields are missing it: " +
`${invalidFields.map((f) => `"${f.key}"`)}`);
}
}
const queries = fields.map((v) => v.query);
const { newLine, space } = this.indentation;
const newQuery = ` {${newLine}${space}${queries.join(`,${newLine}${space}`)}${newLine}}`;
// Create a combined parser:
const projectionParser = createProjectionParser(fields);
return this.chain(newQuery, projectionParser);
},
});
function normalizeProjectionField(key, fieldConfig) {
// Analyze the field configuration:
const value = fieldConfig;
if (value instanceof groq_builder_1.GroqBuilder) {
const query = (0, conditional_types_1.isConditional)(key) // Conditionals can ignore the key
? value.query
: key === value.query // Use shorthand syntax
? key
: `"${key}": ${value.query}`;
return { key, query, parser: value.parser };
}
else if (typeof value === "string") {
const query = key === value ? key : `"${key}": ${value}`;
return { key, query, parser: null };
}
else if (typeof value === "boolean") {
if (value === false)
return null; // 'false' will be excluded from the results
return { key, query: key, parser: null };
}
else if (Array.isArray(value)) {
const [projectionKey, parser] = value;
const query = key === projectionKey ? key : `"${key}": ${projectionKey}`;
return {
key,
query,
parser: (0, validate_utils_1.normalizeValidationFunction)(parser),
};
}
else if ((0, validate_utils_1.isParser)(value)) {
return {
key,
query: key,
parser: (0, validate_utils_1.normalizeValidationFunction)(value),
};
}
else {
throw new Error(`Unexpected value for projection key "${key}": "${typeof value}"`);
}
}
function createProjectionParser(fields) {
if (!fields.some((f) => f.parser)) {
// No nested parsers!
return null;
}
// Parse all normal fields:
const normalFields = fields.filter((f) => !(0, conditional_types_1.isConditional)(f.key));
const objectShape = Object.fromEntries(normalFields.map((f) => [f.key, f.parser]));
const objectParser = (0, simple_validation_1.simpleObjectParser)(objectShape);
// Parse all conditional fields:
const conditionalFields = fields.filter((f) => (0, conditional_types_1.isConditional)(f.key));
const conditionalParsers = conditionalFields
.map((f) => f.parser)
.filter(utils_1.notNull);
// Combine normal and conditional parsers:
const combinedParser = (0, simple_validation_1.combineObjectParsers)(objectParser, ...conditionalParsers);
// Finally, transparently handle arrays or objects:
return (0, simple_validation_1.maybeArrayParser)(combinedParser);
}
//# sourceMappingURL=project.js.map