Extract GitHubFeatureFlags to a separate class

Internal refactoring so that `GitHubFeatureFlags` is
private only. The public facing class is `Features`.
This commit is contained in:
Andrew Eisenberg
2022-10-06 14:42:57 -07:00
parent 5915e70486
commit b27aed78f5
15 changed files with 149 additions and 110 deletions

92
lib/feature-flags.js generated
View File

@@ -19,7 +19,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createFeatureFlags = exports.GitHubFeatureFlags = exports.featureConfig = exports.Feature = void 0;
exports.createFeatureFlags = exports.Features = exports.featureConfig = exports.Feature = void 0;
const api_client_1 = require("./api-client");
const util = __importStar(require("./util"));
var Feature;
@@ -52,12 +52,14 @@ exports.featureConfig = {
minimumVersion: "2.10.1",
},
};
class GitHubFeatureFlags {
/**
* Determines the enablement status of a number of features.
* If feature enablement is not able to be determined locally, a request to the
* github API is made to determine the enablement status.
*/
class Features {
constructor(gitHubVersion, apiDetails, repositoryNwo, logger) {
this.gitHubVersion = gitHubVersion;
this.apiDetails = apiDetails;
this.repositoryNwo = repositoryNwo;
this.logger = logger;
this.gitHubFeatureFlags = new GitHubFeatureFlags(gitHubVersion, apiDetails, repositoryNwo, logger);
}
/**
*
@@ -96,6 +98,19 @@ class GitHubFeatureFlags {
return true;
}
// Ask the GitHub API if the feature is enabled.
return await this.gitHubFeatureFlags.getValue(flag);
}
}
exports.Features = Features;
class GitHubFeatureFlags {
constructor(gitHubVersion, apiDetails, repositoryNwo, logger) {
this.gitHubVersion = gitHubVersion;
this.apiDetails = apiDetails;
this.repositoryNwo = repositoryNwo;
this.logger = logger;
/**/
}
async getValue(flag) {
const response = await this.getApiResponse();
if (response === undefined) {
this.logger.debug(`No feature flags API response for ${flag}, considering it disabled.`);
@@ -106,45 +121,44 @@ class GitHubFeatureFlags {
this.logger.debug(`Feature flag '${flag}' undefined in API response, considering it disabled.`);
return false;
}
return flagValue;
return flagValue || false;
}
async getApiResponse() {
const loadApiResponse = async () => {
// Do nothing when not running against github.com
if (this.gitHubVersion.type !== util.GitHubVariant.DOTCOM) {
this.logger.debug("Not running against github.com. Disabling all feature flags.");
return {};
}
const client = (0, api_client_1.getApiClient)(this.apiDetails);
try {
const response = await client.request("GET /repos/:owner/:repo/code-scanning/codeql-action/features", {
owner: this.repositoryNwo.owner,
repo: this.repositoryNwo.repo,
});
return response.data;
}
catch (e) {
if (util.isHTTPError(e) && e.status === 403) {
this.logger.warning("This run of the CodeQL Action does not have permission to access Code Scanning API endpoints. " +
"As a result, it will not be opted into any experimental features. " +
"This could be because the Action is running on a pull request from a fork. If not, " +
`please ensure the Action has the 'security-events: write' permission. Details: ${e}`);
}
else {
// Some feature flags, such as `ml_powered_queries_enabled` affect the produced alerts.
// Considering these feature flags disabled in the event of a transient error could
// therefore lead to alert churn. As a result, we crash if we cannot determine the value of
// the feature flags.
throw new Error(`Encountered an error while trying to load feature flags: ${e}`);
}
}
};
const apiResponse = this.cachedApiResponse || (await loadApiResponse());
const apiResponse = this.cachedApiResponse || (await this.loadApiResponse());
this.cachedApiResponse = apiResponse;
return apiResponse;
}
async loadApiResponse() {
// Do nothing when not running against github.com
if (this.gitHubVersion.type !== util.GitHubVariant.DOTCOM) {
this.logger.debug("Not running against github.com. Disabling all feature flags.");
return {};
}
const client = (0, api_client_1.getApiClient)(this.apiDetails);
try {
const response = await client.request("GET /repos/:owner/:repo/code-scanning/codeql-action/features", {
owner: this.repositoryNwo.owner,
repo: this.repositoryNwo.repo,
});
return response.data;
}
catch (e) {
if (util.isHTTPError(e) && e.status === 403) {
this.logger.warning("This run of the CodeQL Action does not have permission to access Code Scanning API endpoints. " +
"As a result, it will not be opted into any experimental features. " +
"This could be because the Action is running on a pull request from a fork. If not, " +
`please ensure the Action has the 'security-events: write' permission. Details: ${e}`);
}
else {
// Some feature flags, such as `ml_powered_queries_enabled` affect the produced alerts.
// Considering these feature flags disabled in the event of a transient error could
// therefore lead to alert churn. As a result, we crash if we cannot determine the value of
// the feature flags.
throw new Error(`Encountered an error while trying to load feature flags: ${e}`);
}
}
}
}
exports.GitHubFeatureFlags = GitHubFeatureFlags;
/**
* Create a feature flags instance with the specified set of enabled flags.
*