[go: nahoru, domu]

Skip to content

Commit

Permalink
Fix Deep* types in metaprogramming. Simplify API libraries (#6728)
Browse files Browse the repository at this point in the history
* Fix Deep* types in metaprogramming. Simplify API libraries

* Changes to clean up diff
  • Loading branch information
inlined committed Feb 9, 2024
1 parent 0d975e3 commit f584799
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 134 deletions.
10 changes: 9 additions & 1 deletion src/firebaseConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@
// 'npm run generate:json-schema' to regenerate the schema files.
//

import { RequireAtLeastOne } from "./metaprogramming";
import type { HttpsOptions } from "firebase-functions/v2/https";
import { IngressSetting, MemoryOption, VpcEgressSetting } from "firebase-functions/v2/options";
/**
* Creates a type that requires at least one key to be present in an interface
* type. For example, RequireAtLeastOne<{ foo: string; bar: string }> can hold
* a value of { foo: "a" }, { bar: "b" }, or { foo: "a", bar: "b" } but not {}
* Sourced from - https://docs.microsoft.com/en-us/javascript/api/@azure/keyvault-certificates/requireatleastone?view=azure-node-latest
*/
export type RequireAtLeastOne<T> = {
[K in keyof T]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<keyof T, K>>>;
}[keyof T];

// should be sourced from - https://github.com/firebase/firebase-tools/blob/master/src/deploy/functions/runtimes/index.ts#L15
type CloudFunctionRuntimes =
Expand Down
75 changes: 31 additions & 44 deletions src/gcp/apphosting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { apphostingOrigin } from "../api";
import { ensure } from "../ensureApiEnabled";
import * as deploymentTool from "../deploymentTool";
import { FirebaseError } from "../error";
import { DeepOmit, RecursiveKeyOf, assertImplements } from "../metaprogramming";

export const API_HOST = new URL(apphostingOrigin).host;
export const API_VERSION = "v1alpha";
Expand Down Expand Up @@ -44,6 +45,8 @@ export interface Backend {

export type BackendOutputOnlyFields = "name" | "createTime" | "updateTime" | "uri";

assertImplements<BackendOutputOnlyFields, RecursiveKeyOf<Backend>>();

export interface Build {
name: string;
state: BuildState;
Expand Down Expand Up @@ -81,7 +84,14 @@ export type BuildOutputOnlyFields =
| "etag"
| "createTime"
| "updateTime"
| "deleteTime";
| "deleteTime"
| "source.codebase.displayName"
| "source.codebase.hash"
| "source.codebase.commitMessage"
| "source.codebase.uri"
| "source.codebase.commitTime";

assertImplements<BuildOutputOnlyFields, RecursiveKeyOf<Build>>();

export interface BuildConfig {
minInstances?: number;
Expand All @@ -105,19 +115,6 @@ interface CodebaseSource {
commitTime: string;
}

export type CodebaseSourceOutputOnlyFields =
| "displayName"
| "hash"
| "commitMessage"
| "uri"
| "commitTime";

export type BuildInput = Omit<Build, BuildOutputOnlyFields | "source"> & {
source: Omit<BuildSource, "codebase"> & {
codebase: Omit<CodebaseSource, CodebaseSourceOutputOnlyFields>;
};
};

interface Status {
code: number;
message: string;
Expand Down Expand Up @@ -165,6 +162,8 @@ export type RolloutOutputOnlyFields =
| "etag"
| "reconciling";

assertImplements<RolloutOutputOnlyFields, RecursiveKeyOf<Rollout>>();

export interface ListRolloutsResponse {
rollouts: Rollout[];
unreachable: string[];
Expand Down Expand Up @@ -192,7 +191,12 @@ export type TrafficOutputOnlyFields =
| "createTime"
| "updateTime"
| "etag"
| "uid";
| "uid"
| "rolloutPolicy.disabledTime"
| "rolloutPolicy.stages.startTime"
| "rolloutPolicy.stages.endTime";

assertImplements<TrafficOutputOnlyFields, RecursiveKeyOf<Traffic>>();

export interface TrafficSet {
splits: TrafficSplit[];
Expand All @@ -215,16 +219,6 @@ export interface RolloutPolicy {
disabledTime: string;
}

export type RolloutPolicyOutputOnlyFields = "disabledTime";

export type RolloutPolicyInput = Omit<RolloutPolicy, RolloutPolicyOutputOnlyFields | "stages"> & {
stages: Omit<RolloutStage, "startTime" | "endTime">[];
};

export type TrafficInput = Omit<Traffic, TrafficOutputOnlyFields | "rolloutPolicy"> & {
rolloutPolicy: RolloutPolicyInput;
};

export type RolloutProgression =
| "PROGRESSION_UNSPECIFIED"
| "IMMEDIATE"
Expand All @@ -243,8 +237,6 @@ export interface RolloutStage {
endTime: string;
}

export type RolloutStageOutputOnlyFields = "startTime" | "endTime";

interface OperationMetadata {
createTime: string;
endTime: string;
Expand Down Expand Up @@ -275,10 +267,10 @@ export interface ListBackendsResponse {
export async function createBackend(
projectId: string,
location: string,
backendReqBoby: Omit<Backend, BackendOutputOnlyFields>,
backendReqBoby: DeepOmit<Backend, BackendOutputOnlyFields>,
backendId: string,
): Promise<Operation> {
const res = await client.post<Omit<Backend, BackendOutputOnlyFields>, Operation>(
const res = await client.post<DeepOmit<Backend, BackendOutputOnlyFields>, Operation>(
`projects/${projectId}/locations/${location}/backends`,
{
...backendReqBoby,
Expand Down Expand Up @@ -382,9 +374,9 @@ export async function createBuild(
location: string,
backendId: string,
buildId: string,
buildInput: Omit<BuildInput, "name">,
buildInput: DeepOmit<Build, BuildOutputOnlyFields | "name">,
): Promise<Operation> {
const res = await client.post<Omit<BuildInput, "name">, Operation>(
const res = await client.post<DeepOmit<Build, BuildOutputOnlyFields | "name">, Operation>(
`projects/${projectId}/locations/${location}/backends/${backendId}/builds`,
{
...buildInput,
Expand All @@ -406,9 +398,9 @@ export async function createRollout(
location: string,
backendId: string,
rolloutId: string,
rollout: Omit<Rollout, RolloutOutputOnlyFields | "name">,
rollout: DeepOmit<Rollout, RolloutOutputOnlyFields | "name">,
): Promise<Operation> {
const res = await client.post<Omit<Rollout, RolloutOutputOnlyFields | "name">, Operation>(
const res = await client.post<DeepOmit<Rollout, RolloutOutputOnlyFields | "name">, Operation>(
`projects/${projectId}/locations/${location}/backends/${backendId}/rollouts`,
{
...rollout,
Expand Down Expand Up @@ -456,21 +448,16 @@ export async function updateTraffic(
projectId: string,
location: string,
backendId: string,
traffic: Omit<TrafficInput, "name">,
traffic: DeepOmit<Traffic, TrafficOutputOnlyFields | "name">,
): Promise<Operation> {
// BUG(b/322891558): setting deep fields on rolloutPolicy doesn't work for some
// reason. Create a copy without deep fields to force the updateMask to be
// correct.
const trafficCopy = { ...traffic };
if ("rolloutPolicy" in traffic) {
trafficCopy.rolloutPolicy = {} as any;
}
const fieldMasks = proto.fieldMasks(trafficCopy);
// reason. Prevent recursion into that field.
const fieldMasks = proto.fieldMasks(traffic, "rolloutPolicy");
const queryParams = {
updateMask: fieldMasks.join(","),
};
const name = `projects/${projectId}/locations/${location}/backends/${backendId}/traffic`;
const res = await client.patch<TrafficInput, Operation>(
const res = await client.patch<DeepOmit<Traffic, TrafficOutputOnlyFields>, Operation>(
name,
{ ...traffic, name },
{
Expand Down Expand Up @@ -510,11 +497,11 @@ export async function listLocations(projectId: string): Promise<Location[]> {
}

/**
* Ensure that Frameworks API is enabled on the project.
* Ensure that the App Hosting API is enabled on the project.
*/
export async function ensureApiEnabled(options: any): Promise<void> {
const projectId = needProjectId(options);
return await ensure(projectId, API_HOST, "frameworks", true);
return await ensure(projectId, API_HOST, "app hosting", true);
}

/**
Expand Down
7 changes: 2 additions & 5 deletions src/hosting/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {
HostingSource,
} from "../firebaseConfig";
import { partition } from "../functional";
import { RequireAtLeastOne } from "../metaprogramming";
import { dirExistsSync } from "../fsutils";
import { resolveProjectPath } from "../projectPath";
import { HostingOptions } from "./options";
Expand Down Expand Up @@ -117,10 +116,8 @@ export function extract(options: HostingOptions): HostingMultiple {

if (!Array.isArray(config.hosting)) {
// Upgrade the type because we pinky swear to ensure site exists as a backup.
const res = cloneDeep(config.hosting) as unknown as RequireAtLeastOne<{
site: string;
target: string;
}>;
const res = cloneDeep(config.hosting) as unknown as HostingMultiple[number];

// earlier the default RTDB instance was used as the hosting site
// because it used to be created along with the Firebase project.
// RTDB instance creation is now deferred and decoupled from project creation.
Expand Down
5 changes: 2 additions & 3 deletions src/hosting/options.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { FirebaseConfig } from "../firebaseConfig";
import { Implements } from "../metaprogramming";
import { assertImplements } from "../metaprogramming";
import { Options } from "../options";
import { HostingResolved } from "./config";

Expand Down Expand Up @@ -27,5 +27,4 @@ export interface HostingOptions {

// This line caues a compile-time error if HostingOptions has a field that is
// missing in Options or incompatible with the type in Options.
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const optionsAreHostingOptions: Implements<Options, HostingOptions> = true;
assertImplements<Options, HostingOptions>();
8 changes: 4 additions & 4 deletions src/init/features/apphosting/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {
BackendOutputOnlyFields,
API_VERSION,
Build,
BuildInput,
Rollout,
} from "../../../gcp/apphosting";
import { Repository } from "../../../gcp/cloudbuild";
Expand All @@ -18,6 +17,7 @@ import { promptOnce } from "../../../prompt";
import { DEFAULT_REGION } from "./constants";
import { ensure } from "../../../ensureApiEnabled";
import * as deploymentTool from "../../../deploymentTool";
import { DeepOmit } from "../../../metaprogramming";

const apphostingPollerOptions: Omit<poller.OperationPollerOptions, "operationResourceName"> = {
apiOrigin: apphostingOrigin,
Expand Down Expand Up @@ -86,7 +86,7 @@ export async function doSetup(setup: any, projectId: string): Promise<void> {
default: "main",
message: "Pick a branch for continuous deployment",
});
const traffic: Omit<apphosting.TrafficInput, "name"> = {
const traffic: DeepOmit<apphosting.Traffic, apphosting.TrafficOutputOnlyFields | "name"> = {
rolloutPolicy: {
codebaseBranch: branch,
stages: [
Expand Down Expand Up @@ -157,7 +157,7 @@ async function promptLocation(projectId: string, locations: string[]): Promise<s
})) as string;
}

function toBackend(cloudBuildConnRepo: Repository): Omit<Backend, BackendOutputOnlyFields> {
function toBackend(cloudBuildConnRepo: Repository): DeepOmit<Backend, BackendOutputOnlyFields> {
return {
servingLocality: "GLOBAL_ACCESS",
codebase: {
Expand Down Expand Up @@ -206,7 +206,7 @@ export async function onboardRollout(
projectId: string,
location: string,
backendId: string,
buildInput: Omit<BuildInput, "name">,
buildInput: DeepOmit<Build, apphosting.BuildOutputOnlyFields | "name">,
): Promise<{ rollout: Rollout; build: Build }> {
logBullet("Starting a new rollout... this may take a few minutes.");
const buildId = await apphosting.getNextRolloutId(projectId, location, backendId, 1);
Expand Down
Loading

0 comments on commit f584799

Please sign in to comment.