Feature/sdk040dependencies (#2609)

* update registry upload to take id for new admin permissions (#2605)

* wip

* wip: Get the get dependencies

* wip check_dependencies

* wip: Get the build working to the vm

* wip: Add in the last of the things that where needed for the new sdk

* Add fix

* wip: implement the changes

* wip: Fix the naming

---------

Co-authored-by: Lucy <12953208+elvece@users.noreply.github.com>
This commit is contained in:
Jade
2024-04-26 17:51:33 -06:00
committed by GitHub
parent e08d93b2aa
commit 8a38666105
24 changed files with 417 additions and 39 deletions

View File

@@ -0,0 +1,44 @@
import {
DependencyConfig as DependencyConfigType,
DeepPartial,
Effects,
} from "../types"
import { deepEqual } from "../util/deepEqual"
import { deepMerge } from "../util/deepMerge"
import { SDKManifest } from "../manifest/ManifestTypes"
export type Update<QueryResults, RemoteConfig> = (options: {
remoteConfig: RemoteConfig
queryResults: QueryResults
}) => Promise<RemoteConfig>
export class DependencyConfig<
Manifest extends SDKManifest,
Store,
Input extends Record<string, any>,
RemoteConfig extends Record<string, any>,
> {
static defaultUpdate = async (options: {
queryResults: unknown
remoteConfig: unknown
}): Promise<unknown> => {
return deepMerge({}, options.remoteConfig, options.queryResults || {})
}
constructor(
readonly dependencyConfig: (options: {
effects: Effects
localConfig: Input
}) => Promise<void | DeepPartial<RemoteConfig>>,
readonly update: Update<
void | DeepPartial<RemoteConfig>,
RemoteConfig
> = DependencyConfig.defaultUpdate as any,
) {}
async query(options: { effects: Effects; localConfig: unknown }) {
return this.dependencyConfig({
localConfig: options.localConfig as Input,
effects: options.effects,
})
}
}

View File

@@ -0,0 +1,115 @@
import {
Effects,
PackageId,
DependencyRequirement,
SetHealth,
CheckDependencyResult,
} from "../types"
export type CheckAllDependencies = {
notRunning: () => Promise<CheckDependencyResult[]>
notInstalled: () => Promise<CheckDependencyResult[]>
healthErrors: () => Promise<{ [id: string]: SetHealth[] }>
throwIfNotRunning: () => Promise<void>
throwIfNotValid: () => Promise<undefined>
throwIfNotInstalled: () => Promise<void>
throwIfError: () => Promise<void>
isValid: () => Promise<boolean>
}
export function checkAllDependencies(effects: Effects): CheckAllDependencies {
const dependenciesPromise = effects.getDependencies()
const resultsPromise = dependenciesPromise.then((dependencies) =>
effects.checkDependencies({
packageIds: dependencies.map((dep) => dep.id),
}),
)
const dependenciesByIdPromise = dependenciesPromise.then((d) =>
d.reduce(
(acc, dep) => {
acc[dep.id] = dep
return acc
},
{} as { [id: PackageId]: DependencyRequirement },
),
)
const healthErrors = async () => {
const results = await resultsPromise
const dependenciesById = await dependenciesByIdPromise
const answer: { [id: PackageId]: SetHealth[] } = {}
for (const result of results) {
const dependency = dependenciesById[result.packageId]
if (!dependency) continue
if (dependency.kind !== "running") continue
const healthChecks = result.healthChecks
.filter((x) => dependency.healthChecks.includes(x.id))
.filter((x) => !!x.message)
if (healthChecks.length === 0) continue
answer[result.packageId] = healthChecks
}
return answer
}
const notInstalled = () =>
resultsPromise.then((x) => x.filter((x) => !x.isInstalled))
const notRunning = async () => {
const results = await resultsPromise
const dependenciesById = await dependenciesByIdPromise
return results.filter((x) => {
const dependency = dependenciesById[x.packageId]
if (!dependency) return false
if (dependency.kind !== "running") return false
return !x.isRunning
})
}
const entries = <B>(x: { [k: string]: B }) => Object.entries(x)
const first = <A>(x: A[]): A | undefined => x[0]
const sinkVoid = <A>(x: A) => void 0
const throwIfError = () =>
healthErrors()
.then(entries)
.then(first)
.then((x) => {
if (!x) return
const [id, healthChecks] = x
if (healthChecks.length > 0)
throw `Package ${id} has the following errors: ${healthChecks.map((x) => x.message).join(", ")}`
})
const throwIfNotRunning = () =>
notRunning().then((results) => {
if (results[0])
throw new Error(`Package ${results[0].packageId} is not running`)
})
const throwIfNotInstalled = () =>
notInstalled().then((results) => {
if (results[0])
throw new Error(`Package ${results[0].packageId} is not installed`)
})
const throwIfNotValid = async () =>
Promise.all([
throwIfNotRunning(),
throwIfNotInstalled(),
throwIfError(),
]).then(sinkVoid)
const isValid = () =>
throwIfNotValid().then(
() => true,
() => false,
)
return {
notRunning,
notInstalled,
healthErrors,
throwIfNotRunning,
throwIfNotValid,
throwIfNotInstalled,
throwIfError,
isValid,
}
}

View File

@@ -0,0 +1,9 @@
// prettier-ignore
export type ReadonlyDeep<A> =
A extends Function ? A :
A extends {} ? { readonly [K in keyof A]: ReadonlyDeep<A[K]> } : A;
export type MaybePromise<A> = Promise<A> | A
export type Message = string
import "./DependencyConfig"
import "./setupDependencyConfig"

View File

@@ -0,0 +1,22 @@
import { Config } from "../config/builder/config"
import { SDKManifest } from "../manifest/ManifestTypes"
import { ExpectedExports } from "../types"
import { DependencyConfig } from "./DependencyConfig"
export function setupDependencyConfig<
Store,
Input extends Record<string, any>,
Manifest extends SDKManifest,
>(
_config: Config<Input, Store> | Config<Input, never>,
autoConfigs: {
[key in keyof Manifest["dependencies"] & string]: DependencyConfig<
Manifest,
Store,
Input,
any
> | null
},
): ExpectedExports.dependencyConfig {
return autoConfigs
}