mirror of
https://github.com/github/codeql-action.git
synced 2026-04-27 01:08:46 +00:00
Validate value types returned by API against expectations
This commit is contained in:
@@ -38,7 +38,7 @@ test.serial(
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"loadPropertiesFromApi throws if response data contains unexpected objects",
|
||||
"loadPropertiesFromApi throws if response data contains objects without `property_name`",
|
||||
async (t) => {
|
||||
sinon.stub(api, "getRepositoryProperties").resolves({
|
||||
headers: {},
|
||||
@@ -197,7 +197,7 @@ test.serial(
|
||||
);
|
||||
|
||||
test.serial(
|
||||
"loadPropertiesFromApi throws if property value is not a string",
|
||||
"loadPropertiesFromApi throws if known property value is not a string",
|
||||
async (t) => {
|
||||
sinon.stub(api, "getRepositoryProperties").resolves({
|
||||
headers: {},
|
||||
@@ -217,7 +217,7 @@ test.serial(
|
||||
),
|
||||
{
|
||||
message:
|
||||
/Expected repository property 'github-codeql-extra-queries' to have a string value/,
|
||||
/Unexpected value for repository property 'github-codeql-extra-queries', got: 123/,
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -20,16 +20,56 @@ type AllRepositoryProperties = {
|
||||
/** Parsed repository properties. */
|
||||
export type RepositoryProperties = Partial<AllRepositoryProperties>;
|
||||
|
||||
/** Maps known repository properties to the type we expect to get from the API. */
|
||||
type RepositoryPropertyApiType = {
|
||||
[RepositoryPropertyName.DISABLE_OVERLAY]: string;
|
||||
[RepositoryPropertyName.EXTRA_QUERIES]: string;
|
||||
};
|
||||
|
||||
/** The type of functions which take the `value` from the API and try to convert it to the type we want. */
|
||||
export type PropertyParser<K extends RepositoryPropertyName> = (
|
||||
name: K,
|
||||
value: RepositoryPropertyApiType[K],
|
||||
logger: Logger,
|
||||
) => AllRepositoryProperties[K];
|
||||
|
||||
/** Possible types of `value`s we get from the API. */
|
||||
export type RepositoryPropertyValue = string | string[];
|
||||
|
||||
/** The type of repository property configurations. */
|
||||
export type PropertyInfo<K extends RepositoryPropertyName> = {
|
||||
/** A validator which checks that the value received from the API is what we expect. */
|
||||
validate: (
|
||||
value: RepositoryPropertyValue,
|
||||
) => value is RepositoryPropertyApiType[K];
|
||||
/** A `PropertyParser` for the property. */
|
||||
parse: PropertyParser<K>;
|
||||
};
|
||||
|
||||
/** Determines whether a value from the API is a string or not. */
|
||||
function isString(value: RepositoryPropertyValue): value is string {
|
||||
return typeof value === "string";
|
||||
}
|
||||
|
||||
/** A repository property that we expect to contain a string value. */
|
||||
const stringProperty = {
|
||||
validate: isString,
|
||||
parse: parseStringRepositoryProperty,
|
||||
};
|
||||
|
||||
/** A repository property that we expect to contain a boolean value. */
|
||||
const booleanProperty = {
|
||||
// The value from the API should come as a string, which we then parse into a boolean.
|
||||
validate: isString,
|
||||
parse: parseBooleanRepositoryProperty,
|
||||
};
|
||||
|
||||
/** Parsers that transform repository properties from the API response into typed values. */
|
||||
const repositoryPropertyParsers: {
|
||||
[K in RepositoryPropertyName]: (
|
||||
name: K,
|
||||
value: string,
|
||||
logger: Logger,
|
||||
) => AllRepositoryProperties[K];
|
||||
[K in RepositoryPropertyName]: PropertyInfo<K>;
|
||||
} = {
|
||||
[RepositoryPropertyName.DISABLE_OVERLAY]: parseBooleanRepositoryProperty,
|
||||
[RepositoryPropertyName.EXTRA_QUERIES]: parseStringRepositoryProperty,
|
||||
[RepositoryPropertyName.DISABLE_OVERLAY]: booleanProperty,
|
||||
[RepositoryPropertyName.EXTRA_QUERIES]: stringProperty,
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -37,7 +77,7 @@ const repositoryPropertyParsers: {
|
||||
*/
|
||||
export interface GitHubRepositoryProperty {
|
||||
property_name: string;
|
||||
value: string | string[];
|
||||
value: RepositoryPropertyValue;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,14 +126,6 @@ export async function loadPropertiesFromApi(
|
||||
}
|
||||
|
||||
if (isKnownPropertyName(property.property_name)) {
|
||||
// Only validate the type of `value` if this is a property we care about, to avoid throwing
|
||||
// on unrelated properties that may use representations we do not support.
|
||||
if (typeof property.value !== "string") {
|
||||
throw new Error(
|
||||
`Expected repository property '${property.property_name}' to have a string value, but got: ${JSON.stringify(property)}`,
|
||||
);
|
||||
}
|
||||
|
||||
setProperty(properties, property.property_name, property.value, logger);
|
||||
}
|
||||
}
|
||||
@@ -119,14 +151,30 @@ export async function loadPropertiesFromApi(
|
||||
}
|
||||
}
|
||||
|
||||
/** Update the partial set of repository properties with the parsed value of the specified property. */
|
||||
/**
|
||||
* Validate that `value` has the correct type for `K` and, if so, update the partial set of repository
|
||||
* properties with the parsed value of the specified property.
|
||||
*/
|
||||
function setProperty<K extends RepositoryPropertyName>(
|
||||
properties: RepositoryProperties,
|
||||
name: K,
|
||||
value: string,
|
||||
value: RepositoryPropertyValue,
|
||||
logger: Logger,
|
||||
): void {
|
||||
properties[name] = repositoryPropertyParsers[name](name, value, logger);
|
||||
const propertyOptions = repositoryPropertyParsers[name];
|
||||
|
||||
// We perform the validation here for two reasons:
|
||||
// 1. This function is only called if `name` is a property we care about, to avoid throwing
|
||||
// on unrelated properties that may use representations we do not support.
|
||||
// 2. The `propertyOptions.validate` function checks that the type of `value` we received from
|
||||
// the API is what expect and narrows the type accordingly, allowing us to call `parse`.
|
||||
if (propertyOptions.validate(value)) {
|
||||
properties[name] = propertyOptions.parse(name, value, logger);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unexpected value for repository property '${name}', got: ${JSON.stringify(value)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse a boolean repository property. */
|
||||
|
||||
Reference in New Issue
Block a user