Convert sync.js to TypeScript (#58009)
This commit is contained in:
@@ -15,30 +15,124 @@ const ENABLED_APPS_DIR = 'src/github-apps/data'
|
|||||||
const CONFIG_FILE = 'src/github-apps/lib/config.json'
|
const CONFIG_FILE = 'src/github-apps/lib/config.json'
|
||||||
|
|
||||||
// Actor type mapping from generic names to actual YAML values
|
// Actor type mapping from generic names to actual YAML values
|
||||||
export const actorTypeMap = {
|
export const actorTypeMap: Record<string, string> = {
|
||||||
fine_grained_pat: 'fine_grained_personal_access_token',
|
fine_grained_pat: 'fine_grained_personal_access_token',
|
||||||
server_to_server: 'github_app',
|
server_to_server: 'github_app',
|
||||||
user_to_server: 'user_access_token',
|
user_to_server: 'user_access_token',
|
||||||
}
|
}
|
||||||
|
|
||||||
// Also need to handle the actual values that come from the source data
|
interface AppDataOperation {
|
||||||
// UserProgrammaticAccess maps to fine_grained_pat functionality
|
slug: string
|
||||||
const sourceDataActorMap = {
|
subcategory: string
|
||||||
UserProgrammaticAccess: 'fine_grained_pat',
|
verb: string
|
||||||
|
requestPath: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function syncGitHubAppsData(openApiSource, sourceSchemas, progAccessSource) {
|
interface AppDataOperationWithCategory extends AppDataOperation {
|
||||||
|
category: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PermissionData {
|
||||||
|
title: string
|
||||||
|
displayTitle: string
|
||||||
|
permissions: Array<
|
||||||
|
AppDataOperationWithCategory & {
|
||||||
|
access: string
|
||||||
|
'user-to-server'?: boolean
|
||||||
|
'server-to-server'?: boolean
|
||||||
|
'additional-permissions'?: boolean
|
||||||
|
}
|
||||||
|
>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface GitHubAppsData {
|
||||||
|
[pageType: string]: {
|
||||||
|
[category: string]: AppDataOperation[] | PermissionData
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProgAccessOperationData {
|
||||||
|
userToServerRest: boolean
|
||||||
|
serverToServer: boolean
|
||||||
|
fineGrainedPat: boolean
|
||||||
|
permissions: Array<Record<string, string>>
|
||||||
|
allowPermissionlessAccess?: boolean
|
||||||
|
allowsPublicRead?: boolean
|
||||||
|
basicAuth?: boolean
|
||||||
|
disabledForPatV2?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProgAccessData {
|
||||||
|
[operationId: string]: ProgAccessOperationData
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProgActorResource {
|
||||||
|
title?: string
|
||||||
|
resource_group?: string
|
||||||
|
visibility?: string
|
||||||
|
excluded_actors?: string[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProgActorResources {
|
||||||
|
[key: string]: ProgActorResource
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OpenApiOperation {
|
||||||
|
operationId: string
|
||||||
|
summary: string
|
||||||
|
'x-github': {
|
||||||
|
category: string
|
||||||
|
subcategory: string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OpenApiData {
|
||||||
|
paths: {
|
||||||
|
[path: string]: {
|
||||||
|
[verb: string]: OpenApiOperation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppsDataConfig {
|
||||||
|
pages: {
|
||||||
|
[pageType: string]: unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProgAccessRawOperation {
|
||||||
|
operation_ids: string
|
||||||
|
user_to_server: {
|
||||||
|
enabled: boolean
|
||||||
|
}
|
||||||
|
server_to_server: {
|
||||||
|
enabled: boolean
|
||||||
|
}
|
||||||
|
disabled_for_patv2?: boolean
|
||||||
|
permission_sets?: Array<Record<string, string>>
|
||||||
|
allows_permissionless_access?: boolean
|
||||||
|
allows_public_read?: boolean
|
||||||
|
basic_auth?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function syncGitHubAppsData(
|
||||||
|
openApiSource: string,
|
||||||
|
sourceSchemas: string[],
|
||||||
|
progAccessSource: string,
|
||||||
|
): Promise<void> {
|
||||||
console.log(
|
console.log(
|
||||||
`Generating GitHub Apps data from ${openApiSource}, ${sourceSchemas} and ${progAccessSource}`,
|
`Generating GitHub Apps data from ${openApiSource}, ${sourceSchemas} and ${progAccessSource}`,
|
||||||
)
|
)
|
||||||
const { progAccessData, progActorResources } = await getProgAccessData(progAccessSource)
|
const { progAccessData, progActorResources } = await getProgAccessData(progAccessSource)
|
||||||
|
|
||||||
for (const schemaName of sourceSchemas) {
|
for (const schemaName of sourceSchemas) {
|
||||||
const data = JSON.parse(await readFile(path.join(openApiSource, schemaName), 'utf8'))
|
const data = JSON.parse(
|
||||||
const appsDataConfig = JSON.parse(await readFile(CONFIG_FILE, 'utf8'))
|
await readFile(path.join(openApiSource, schemaName), 'utf8'),
|
||||||
|
) as OpenApiData
|
||||||
|
const appsDataConfig = JSON.parse(await readFile(CONFIG_FILE, 'utf8')) as AppsDataConfig
|
||||||
|
|
||||||
// Initialize the data structure with keys for each page type
|
// Initialize the data structure with keys for each page type
|
||||||
const githubAppsData = {}
|
const githubAppsData: GitHubAppsData = {}
|
||||||
for (const pageType of Object.keys(appsDataConfig.pages)) {
|
for (const pageType of Object.keys(appsDataConfig.pages)) {
|
||||||
githubAppsData[pageType] = {}
|
githubAppsData[pageType] = {}
|
||||||
}
|
}
|
||||||
@@ -54,13 +148,16 @@ export async function syncGitHubAppsData(openApiSource, sourceSchemas, progAcces
|
|||||||
const isFineGrainedPat =
|
const isFineGrainedPat =
|
||||||
isUserAccessToken && !progAccessData[operation.operationId].disabledForPatV2
|
isUserAccessToken && !progAccessData[operation.operationId].disabledForPatV2
|
||||||
const { category, subcategory } = operation['x-github']
|
const { category, subcategory } = operation['x-github']
|
||||||
const appDataOperation = {
|
const appDataOperation: AppDataOperation = {
|
||||||
slug: slug(operation.summary),
|
slug: slug(operation.summary),
|
||||||
subcategory,
|
subcategory,
|
||||||
verb,
|
verb,
|
||||||
requestPath,
|
requestPath,
|
||||||
}
|
}
|
||||||
const appDataOperationWithCategory = Object.assign({ category }, appDataOperation)
|
const appDataOperationWithCategory: AppDataOperationWithCategory = Object.assign(
|
||||||
|
{ category },
|
||||||
|
appDataOperation,
|
||||||
|
)
|
||||||
// server-to-server
|
// server-to-server
|
||||||
if (isInstallationAccessToken) {
|
if (isInstallationAccessToken) {
|
||||||
addAppData(githubAppsData['server-to-server-rest'], category, appDataOperation)
|
addAppData(githubAppsData['server-to-server-rest'], category, appDataOperation)
|
||||||
@@ -85,11 +182,6 @@ export async function syncGitHubAppsData(openApiSource, sourceSchemas, progAcces
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
// Debug logging for checks-related operations
|
|
||||||
const hasChecksPermission = progAccessData[operation.operationId].permissions.some(
|
|
||||||
(permissionSet) => permissionSet.checks,
|
|
||||||
)
|
|
||||||
|
|
||||||
if (!allPermissionSetsExcluded) {
|
if (!allPermissionSetsExcluded) {
|
||||||
addAppData(githubAppsData['fine-grained-pat'], category, appDataOperation)
|
addAppData(githubAppsData['fine-grained-pat'], category, appDataOperation)
|
||||||
}
|
}
|
||||||
@@ -99,9 +191,9 @@ export async function syncGitHubAppsData(openApiSource, sourceSchemas, progAcces
|
|||||||
for (const permissionSet of progAccessData[operation.operationId].permissions) {
|
for (const permissionSet of progAccessData[operation.operationId].permissions) {
|
||||||
for (const [permissionName, readOrWrite] of Object.entries(permissionSet)) {
|
for (const [permissionName, readOrWrite] of Object.entries(permissionSet)) {
|
||||||
const { title, displayTitle } = getDisplayTitle(permissionName, progActorResources)
|
const { title, displayTitle } = getDisplayTitle(permissionName, progActorResources)
|
||||||
if (progActorResources[permissionName]['visibility'] === 'private') continue
|
if (progActorResources[permissionName]?.['visibility'] === 'private') continue
|
||||||
|
|
||||||
const excludedActors = progActorResources[permissionName]['excluded_actors']
|
const excludedActors = progActorResources[permissionName]?.['excluded_actors']
|
||||||
|
|
||||||
const additionalPermissions = calculateAdditionalPermissions(
|
const additionalPermissions = calculateAdditionalPermissions(
|
||||||
progAccessData[operation.operationId].permissions,
|
progAccessData[operation.operationId].permissions,
|
||||||
@@ -143,7 +235,9 @@ export async function syncGitHubAppsData(openApiSource, sourceSchemas, progAcces
|
|||||||
),
|
),
|
||||||
'additional-permissions': additionalPermissions,
|
'additional-permissions': additionalPermissions,
|
||||||
}
|
}
|
||||||
serverToServerPermissions[permissionName].permissions.push(
|
const permissionsArray = (serverToServerPermissions[permissionName] as PermissionData)
|
||||||
|
.permissions
|
||||||
|
permissionsArray.push(
|
||||||
Object.assign(
|
Object.assign(
|
||||||
{},
|
{},
|
||||||
appDataOperationWithCategory,
|
appDataOperationWithCategory,
|
||||||
@@ -174,7 +268,9 @@ export async function syncGitHubAppsData(openApiSource, sourceSchemas, progAcces
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
findGrainedPatPermissions[permissionName].permissions.push(
|
const permissionsArray = (findGrainedPatPermissions[permissionName] as PermissionData)
|
||||||
|
.permissions
|
||||||
|
permissionsArray.push(
|
||||||
Object.assign({}, appDataOperationWithCategory, {
|
Object.assign({}, appDataOperationWithCategory, {
|
||||||
'additional-permissions': additionalPermissions,
|
'additional-permissions': additionalPermissions,
|
||||||
access: readOrWrite,
|
access: readOrWrite,
|
||||||
@@ -214,7 +310,10 @@ export async function syncGitHubAppsData(openApiSource, sourceSchemas, progAcces
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getProgAccessData(progAccessSource, isRest = false) {
|
export async function getProgAccessData(
|
||||||
|
progAccessSource: string,
|
||||||
|
isRest = false,
|
||||||
|
): Promise<{ progAccessData: ProgAccessData; progActorResources: ProgActorResources }> {
|
||||||
const useRemoteGitHubFiles = progAccessSource === 'rest-api-description'
|
const useRemoteGitHubFiles = progAccessSource === 'rest-api-description'
|
||||||
// check for required PAT
|
// check for required PAT
|
||||||
if (useRemoteGitHubFiles && !process.env.GITHUB_TOKEN) {
|
if (useRemoteGitHubFiles && !process.env.GITHUB_TOKEN) {
|
||||||
@@ -223,8 +322,8 @@ export async function getProgAccessData(progAccessSource, isRest = false) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
let progAccessDataRaw
|
let progAccessDataRaw: ProgAccessRawOperation[]
|
||||||
let progActorResources
|
let progActorResources: ProgActorResources
|
||||||
const progAccessFilepath = 'config/access_control/programmatic_access.yaml'
|
const progAccessFilepath = 'config/access_control/programmatic_access.yaml'
|
||||||
const progActorDirectory =
|
const progActorDirectory =
|
||||||
'config/access_control/fine_grained_permissions/programmatic_actor_fine_grained_resources'
|
'config/access_control/fine_grained_permissions/programmatic_actor_fine_grained_resources'
|
||||||
@@ -232,14 +331,14 @@ export async function getProgAccessData(progAccessSource, isRest = false) {
|
|||||||
if (!useRemoteGitHubFiles) {
|
if (!useRemoteGitHubFiles) {
|
||||||
progAccessDataRaw = yaml.load(
|
progAccessDataRaw = yaml.load(
|
||||||
await readFile(path.join(progAccessSource, progAccessFilepath), 'utf8'),
|
await readFile(path.join(progAccessSource, progAccessFilepath), 'utf8'),
|
||||||
)
|
) as ProgAccessRawOperation[]
|
||||||
progActorResources = await getProgActorResourceContent({
|
progActorResources = await getProgActorResourceContent({
|
||||||
gitHubSourceDirectory: path.join(progAccessSource, progActorDirectory),
|
gitHubSourceDirectory: path.join(progAccessSource, progActorDirectory),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
progAccessDataRaw = yaml.load(
|
progAccessDataRaw = yaml.load(
|
||||||
await getContents('github', 'github', 'master', progAccessFilepath),
|
await getContents('github', 'github', 'master', progAccessFilepath),
|
||||||
)
|
) as ProgAccessRawOperation[]
|
||||||
progActorResources = await getProgActorResourceContent({
|
progActorResources = await getProgActorResourceContent({
|
||||||
owner: 'github',
|
owner: 'github',
|
||||||
repo: 'github',
|
repo: 'github',
|
||||||
@@ -248,9 +347,9 @@ export async function getProgAccessData(progAccessSource, isRest = false) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const progAccessData = {}
|
const progAccessData: ProgAccessData = {}
|
||||||
for (const operation of progAccessDataRaw) {
|
for (const operation of progAccessDataRaw) {
|
||||||
const operationData = {
|
const operationData: ProgAccessOperationData = {
|
||||||
userToServerRest: operation.user_to_server.enabled,
|
userToServerRest: operation.user_to_server.enabled,
|
||||||
serverToServer: operation.server_to_server.enabled,
|
serverToServer: operation.server_to_server.enabled,
|
||||||
fineGrainedPat: operation.user_to_server.enabled && !operation.disabled_for_patv2,
|
fineGrainedPat: operation.user_to_server.enabled && !operation.disabled_for_patv2,
|
||||||
@@ -260,6 +359,7 @@ export async function getProgAccessData(progAccessSource, isRest = false) {
|
|||||||
allowPermissionlessAccess: operation.allows_permissionless_access,
|
allowPermissionlessAccess: operation.allows_permissionless_access,
|
||||||
allowsPublicRead: operation.allows_public_read,
|
allowsPublicRead: operation.allows_public_read,
|
||||||
basicAuth: operation.basic_auth,
|
basicAuth: operation.basic_auth,
|
||||||
|
disabledForPatV2: operation.disabled_for_patv2,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle comma-separated operation IDs
|
// Handle comma-separated operation IDs
|
||||||
@@ -272,9 +372,12 @@ export async function getProgAccessData(progAccessSource, isRest = false) {
|
|||||||
return { progAccessData, progActorResources }
|
return { progAccessData, progActorResources }
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDisplayPermissions(permissionSets, progActorResources) {
|
function getDisplayPermissions(
|
||||||
|
permissionSets: Array<Record<string, string>>,
|
||||||
|
progActorResources: ProgActorResources,
|
||||||
|
): Array<Record<string, string>> {
|
||||||
const displayPermissions = permissionSets.map((permissionSet) => {
|
const displayPermissions = permissionSets.map((permissionSet) => {
|
||||||
const displayPermissionSet = {}
|
const displayPermissionSet: Record<string, string> = {}
|
||||||
Object.entries(permissionSet).forEach(([key, value]) => {
|
Object.entries(permissionSet).forEach(([key, value]) => {
|
||||||
const { displayTitle } = getDisplayTitle(key, progActorResources, true)
|
const { displayTitle } = getDisplayTitle(key, progActorResources, true)
|
||||||
displayPermissionSet[displayTitle] = value
|
displayPermissionSet[displayTitle] = value
|
||||||
@@ -286,33 +389,45 @@ function getDisplayPermissions(permissionSets, progActorResources) {
|
|||||||
return displayPermissions
|
return displayPermissions
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortObjectByKeys(obj) {
|
function sortObjectByKeys<T>(obj: Record<string, T>): Record<string, T> {
|
||||||
return Object.keys(obj)
|
return Object.keys(obj)
|
||||||
.sort()
|
.sort()
|
||||||
.reduce((acc, key) => {
|
.reduce(
|
||||||
acc[key] = obj[key]
|
(acc, key) => {
|
||||||
return acc
|
acc[key] = obj[key]
|
||||||
}, {})
|
return acc
|
||||||
|
},
|
||||||
|
{} as Record<string, T>,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function sortObjectByTitle(obj) {
|
function sortObjectByTitle(obj: Record<string, PermissionData | unknown>): Record<string, unknown> {
|
||||||
return Object.keys(obj)
|
return Object.keys(obj)
|
||||||
.sort((a, b) => {
|
.sort((a, b) => {
|
||||||
if (obj[a].displayTitle > obj[b].displayTitle) {
|
const aData = obj[a] as PermissionData
|
||||||
|
const bData = obj[b] as PermissionData
|
||||||
|
if (aData.displayTitle > bData.displayTitle) {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
if (obj[a].displayTitle < obj[b].displayTitle) {
|
if (aData.displayTitle < bData.displayTitle) {
|
||||||
return -1
|
return -1
|
||||||
}
|
}
|
||||||
return 0
|
return 0
|
||||||
})
|
})
|
||||||
.reduce((acc, key) => {
|
.reduce(
|
||||||
acc[key] = obj[key]
|
(acc, key) => {
|
||||||
return acc
|
acc[key] = obj[key]
|
||||||
}, {})
|
return acc
|
||||||
|
},
|
||||||
|
{} as Record<string, unknown>,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDisplayTitle(permissionName, progActorResources, isRest = false) {
|
function getDisplayTitle(
|
||||||
|
permissionName: string,
|
||||||
|
progActorResources: ProgActorResources,
|
||||||
|
isRest = false,
|
||||||
|
): { title: string; displayTitle: string } {
|
||||||
const tempTitle = permissionName.replace(/_/g, ' ')
|
const tempTitle = permissionName.replace(/_/g, ' ')
|
||||||
const permissionNameExists = progActorResources[permissionName]
|
const permissionNameExists = progActorResources[permissionName]
|
||||||
if (!permissionNameExists) {
|
if (!permissionNameExists) {
|
||||||
@@ -328,7 +443,7 @@ function getDisplayTitle(permissionName, progActorResources, isRest = false) {
|
|||||||
|
|
||||||
if (!title) {
|
if (!title) {
|
||||||
console.warn(`No title found for title ${title} resource group ${resourceGroup}`)
|
console.warn(`No title found for title ${title} resource group ${resourceGroup}`)
|
||||||
return ''
|
return { title: '', displayTitle: '' }
|
||||||
}
|
}
|
||||||
|
|
||||||
const displayTitle = isRest
|
const displayTitle = isRest
|
||||||
@@ -342,14 +457,16 @@ function getDisplayTitle(permissionName, progActorResources, isRest = false) {
|
|||||||
return { title, displayTitle }
|
return { title, displayTitle }
|
||||||
}
|
}
|
||||||
|
|
||||||
function sentenceCase(str) {
|
function sentenceCase(str: string): string {
|
||||||
return str.charAt(0).toUpperCase() + str.slice(1)
|
return str.charAt(0).toUpperCase() + str.slice(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates whether an operation has additional permissions beyond a single permission.
|
* Calculates whether an operation has additional permissions beyond a single permission.
|
||||||
*/
|
*/
|
||||||
export function calculateAdditionalPermissions(permissionSets) {
|
export function calculateAdditionalPermissions(
|
||||||
|
permissionSets: Array<Record<string, string>>,
|
||||||
|
): boolean {
|
||||||
return (
|
return (
|
||||||
permissionSets.length > 1 ||
|
permissionSets.length > 1 ||
|
||||||
permissionSets.some((permissionSet) => Object.keys(permissionSet).length > 1)
|
permissionSets.some((permissionSet) => Object.keys(permissionSet).length > 1)
|
||||||
@@ -360,7 +477,10 @@ export function calculateAdditionalPermissions(permissionSets) {
|
|||||||
* Determines whether a metadata permission should be filtered out when it has additional permissions.
|
* Determines whether a metadata permission should be filtered out when it has additional permissions.
|
||||||
* Prevents misleading documentation where mutating operations appear to only need metadata access.
|
* Prevents misleading documentation where mutating operations appear to only need metadata access.
|
||||||
*/
|
*/
|
||||||
export function shouldFilterMetadataPermission(permissionName, permissionSets) {
|
export function shouldFilterMetadataPermission(
|
||||||
|
permissionName: string,
|
||||||
|
permissionSets: Array<Record<string, string>>,
|
||||||
|
): boolean {
|
||||||
if (permissionName !== 'metadata') {
|
if (permissionName !== 'metadata') {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -368,7 +488,11 @@ export function shouldFilterMetadataPermission(permissionName, permissionSets) {
|
|||||||
return calculateAdditionalPermissions(permissionSets)
|
return calculateAdditionalPermissions(permissionSets)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isActorExcluded(excludedActors, actorType, actorTypeMap = {}) {
|
export function isActorExcluded(
|
||||||
|
excludedActors: string[] | undefined | null | unknown,
|
||||||
|
actorType: string,
|
||||||
|
actorTypeMap: Record<string, string> = {},
|
||||||
|
): boolean {
|
||||||
if (!excludedActors || !Array.isArray(excludedActors)) {
|
if (!excludedActors || !Array.isArray(excludedActors)) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -394,14 +518,21 @@ export function isActorExcluded(excludedActors, actorType, actorTypeMap = {}) {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
function addAppData(storage, category, data) {
|
function addAppData(
|
||||||
|
storage: Record<string, AppDataOperation[] | PermissionData>,
|
||||||
|
category: string,
|
||||||
|
data: AppDataOperation,
|
||||||
|
): void {
|
||||||
if (!storage[category]) {
|
if (!storage[category]) {
|
||||||
storage[category] = []
|
storage[category] = []
|
||||||
}
|
}
|
||||||
storage[category].push(data)
|
;(storage[category] as AppDataOperation[]).push(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function validateAppData(data, pageType) {
|
async function validateAppData(
|
||||||
|
data: Record<string, AppDataOperation[] | PermissionData>,
|
||||||
|
pageType: string,
|
||||||
|
): Promise<void> {
|
||||||
if (pageType.includes('permissions')) {
|
if (pageType.includes('permissions')) {
|
||||||
for (const value of Object.values(data)) {
|
for (const value of Object.values(data)) {
|
||||||
const { isValid, errors } = validateJson(permissionSchema, value)
|
const { isValid, errors } = validateJson(permissionSchema, value)
|
||||||
@@ -412,7 +543,7 @@ async function validateAppData(data, pageType) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (const arrayItems of Object.values(data)) {
|
for (const arrayItems of Object.values(data)) {
|
||||||
for (const item of arrayItems) {
|
for (const item of arrayItems as AppDataOperation[]) {
|
||||||
const { isValid, errors } = validateJson(enabledSchema, item)
|
const { isValid, errors } = validateJson(enabledSchema, item)
|
||||||
if (!isValid) {
|
if (!isValid) {
|
||||||
console.error(JSON.stringify(errors, null, 2))
|
console.error(JSON.stringify(errors, null, 2))
|
||||||
@@ -423,6 +554,14 @@ async function validateAppData(data, pageType) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ProgActorResourceContentOptions {
|
||||||
|
owner?: string
|
||||||
|
repo?: string
|
||||||
|
branch?: string
|
||||||
|
path?: string
|
||||||
|
gitHubSourceDirectory?: string | null
|
||||||
|
}
|
||||||
|
|
||||||
// When getting files from the GitHub repo locally (or in a Codespace)
|
// When getting files from the GitHub repo locally (or in a Codespace)
|
||||||
// you can pass the full or relative path to the `github` repository
|
// you can pass the full or relative path to the `github` repository
|
||||||
// directory on disk.
|
// directory on disk.
|
||||||
@@ -434,21 +573,21 @@ async function getProgActorResourceContent({
|
|||||||
branch,
|
branch,
|
||||||
path,
|
path,
|
||||||
gitHubSourceDirectory = null,
|
gitHubSourceDirectory = null,
|
||||||
}) {
|
}: ProgActorResourceContentOptions): Promise<ProgActorResources> {
|
||||||
// Get files either locally from disk or from the GitHub remote repo
|
// Get files either locally from disk or from the GitHub remote repo
|
||||||
let files
|
let files: string[]
|
||||||
if (gitHubSourceDirectory) {
|
if (gitHubSourceDirectory) {
|
||||||
files = await getProgActorContentFromDisk(gitHubSourceDirectory)
|
files = await getProgActorContentFromDisk(gitHubSourceDirectory)
|
||||||
} else {
|
} else {
|
||||||
files = await getDirectoryContents(owner, repo, branch, path)
|
files = await getDirectoryContents(owner!, repo!, branch!, path!)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to format the file content into a single object. Each file
|
// We need to format the file content into a single object. Each file
|
||||||
// contains a single key and a single value that needs to be added
|
// contains a single key and a single value that needs to be added
|
||||||
// to the object.
|
// to the object.
|
||||||
const progActorResources = {}
|
const progActorResources: ProgActorResources = {}
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const fileContent = yaml.load(file)
|
const fileContent = yaml.load(file) as Record<string, ProgActorResource>
|
||||||
// Each file should only contain a single key and value.
|
// Each file should only contain a single key and value.
|
||||||
if (Object.keys(fileContent).length !== 1) {
|
if (Object.keys(fileContent).length !== 1) {
|
||||||
throw new Error(`Error: The file ${JSON.stringify(fileContent)} must only have one key.`)
|
throw new Error(`Error: The file ${JSON.stringify(fileContent)} must only have one key.`)
|
||||||
@@ -460,7 +599,7 @@ async function getProgActorResourceContent({
|
|||||||
return progActorResources
|
return progActorResources
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getProgActorContentFromDisk(directory) {
|
async function getProgActorContentFromDisk(directory: string): Promise<string[]> {
|
||||||
const files = walk(directory, {
|
const files = walk(directory, {
|
||||||
includeBasePath: true,
|
includeBasePath: true,
|
||||||
directories: false,
|
directories: false,
|
||||||
Reference in New Issue
Block a user