@heroku/sdk 0.8.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/data/database.js +6 -6
- package/dist/resources/data/internal/postgres-utils.d.ts +2 -1
- package/dist/resources/data/internal/resolve-pg-database.d.ts +28 -7
- package/dist/resources/data/internal/resolve-pg-database.js +87 -18
- package/dist/resources/data/maintenance.js +1 -1
- package/dist/resources/data/postgres-database.js +1 -1
- package/dist/resources/data/{backup → transfer}/capture-and-wait.d.ts +10 -3
- package/dist/resources/data/{backup → transfer}/capture-and-wait.js +2 -2
- package/dist/resources/data/transfer/index.d.ts +11 -0
- package/dist/resources/data/transfer/index.js +12 -0
- package/dist/resources/data/{restore → transfer}/restore-and-wait.d.ts +10 -3
- package/dist/resources/data/{restore → transfer}/restore-and-wait.js +2 -2
- package/dist/resources/data/{wait-for-transfer.d.ts → transfer/wait-for-transfer.d.ts} +2 -2
- package/dist/resources/data/{wait-for-transfer.js → transfer/wait-for-transfer.js} +2 -2
- package/dist/resources/data/transfer-schedule.js +3 -3
- package/dist/resources/extensions/data.d.ts +1 -3
- package/dist/resources/extensions/data.js +1 -3
- package/package.json +5 -8
- package/dist/resources/data/backup/index.d.ts +0 -5
- package/dist/resources/data/backup/index.js +0 -6
- package/dist/resources/data/restore/index.d.ts +0 -5
- package/dist/resources/data/restore/index.js +0 -6
|
@@ -2,32 +2,32 @@ import { extendResource } from '../../core/extend-resource.js';
|
|
|
2
2
|
import { resolvePgDatabase } from './internal/resolve-pg-database.js';
|
|
3
3
|
export async function describe(ctx, appIdentity, addonIdentity, options = {}) {
|
|
4
4
|
options.signal?.throwIfAborted();
|
|
5
|
-
const addon = await resolvePgDatabase(ctx,
|
|
5
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
6
6
|
return ctx.data.database.info(addon.id);
|
|
7
7
|
}
|
|
8
8
|
export async function upgradeWaitStatus(ctx, appIdentity, addonIdentity, options = {}) {
|
|
9
9
|
options.signal?.throwIfAborted();
|
|
10
|
-
const addon = await resolvePgDatabase(ctx,
|
|
10
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
11
11
|
return ctx.data.database.upgradeWaitStatus(addon.id);
|
|
12
12
|
}
|
|
13
13
|
export async function dryRunUpgrade(ctx, appIdentity, addonIdentity, body = {}, options = {}) {
|
|
14
14
|
options.signal?.throwIfAborted();
|
|
15
|
-
const addon = await resolvePgDatabase(ctx,
|
|
15
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
16
16
|
return ctx.data.database.dryRunUpgrade(addon.id, body);
|
|
17
17
|
}
|
|
18
18
|
export async function runUpgrade(ctx, appIdentity, addonIdentity, body = {}, options = {}) {
|
|
19
19
|
options.signal?.throwIfAborted();
|
|
20
|
-
const addon = await resolvePgDatabase(ctx,
|
|
20
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
21
21
|
return ctx.data.database.runUpgrade(addon.id, body);
|
|
22
22
|
}
|
|
23
23
|
export async function prepareUpgrade(ctx, appIdentity, addonIdentity, body = {}, options = {}) {
|
|
24
24
|
options.signal?.throwIfAborted();
|
|
25
|
-
const addon = await resolvePgDatabase(ctx,
|
|
25
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
26
26
|
return ctx.data.database.prepareUpgrade(addon.id, body);
|
|
27
27
|
}
|
|
28
28
|
export async function cancelUpgrade(ctx, appIdentity, addonIdentity, options = {}) {
|
|
29
29
|
options.signal?.throwIfAborted();
|
|
30
|
-
const addon = await resolvePgDatabase(ctx,
|
|
30
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
31
31
|
return ctx.data.database.cancelUpgrade(addon.id);
|
|
32
32
|
}
|
|
33
33
|
export const databaseExtensions = extendResource('data', 'database', ctx => ({
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { AddOn } from '@heroku/types/3.sdk';
|
|
2
2
|
import { ResourceCtx } from '../../../core/extend-resource.js';
|
|
3
|
+
import { AddOnAttachmentWithInclusions } from '../../platform/add-on-attachment/resolve.js';
|
|
3
4
|
/**
|
|
4
5
|
* Returns an arbitrary legacy database add-on based on the provided app name.
|
|
5
6
|
*/
|
|
6
7
|
export declare function getArbitraryLegacyDB(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string): Promise<AddOn>;
|
|
7
8
|
export declare const getAddonService: () => string;
|
|
8
|
-
export declare const isPostgresAddon: (addon: AddOn) => boolean;
|
|
9
|
+
export declare const isPostgresAddon: (addon: AddOn | AddOnAttachmentWithInclusions["addon"]) => boolean;
|
|
9
10
|
export declare const isAdvancedDatabase: (addon: AddOn) => boolean;
|
|
10
11
|
export declare const isLegacyDatabase: (addon: AddOn) => boolean;
|
|
@@ -6,10 +6,19 @@ export type ResolvedPgDatabaseWithAttachment = {
|
|
|
6
6
|
attachment: ResolvedAddOnAttachment;
|
|
7
7
|
};
|
|
8
8
|
export type ResolvePgDatabaseOptions = ResolveAddonAttachmentOptions & {
|
|
9
|
-
appIdentity?: string;
|
|
10
9
|
includeAttachment?: boolean;
|
|
11
10
|
input?: string;
|
|
12
11
|
};
|
|
12
|
+
/**
|
|
13
|
+
* Thrown by `resolvePgDatabase` when the attachment resolution endpoint
|
|
14
|
+
* does not find a match, the matched attachment is not the configured
|
|
15
|
+
* Postgres service, or the app has no Postgres databases at all.
|
|
16
|
+
*/
|
|
17
|
+
export declare class PgDatabaseNotFoundError extends Error {
|
|
18
|
+
constructor(message: string, options?: {
|
|
19
|
+
cause?: unknown;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
13
22
|
/**
|
|
14
23
|
* Resolve a Heroku Postgres database add-on from an attachment identity.
|
|
15
24
|
*
|
|
@@ -23,7 +32,8 @@ export type ResolvePgDatabaseOptions = ResolveAddonAttachmentOptions & {
|
|
|
23
32
|
* `::` ourselves and send `app`/`addon` as separate fields, which
|
|
24
33
|
* works for either shape.
|
|
25
34
|
* - any other string (attachment name, config var, UUID, or
|
|
26
|
-
* globally-unique add-on name) → routed through `resolveAddonAttachment
|
|
35
|
+
* globally-unique add-on name) → routed through `resolveAddonAttachment`,
|
|
36
|
+
* scoped to `appIdentity`.
|
|
27
37
|
*
|
|
28
38
|
* This function only uses the attachment resolver. To resolve a database
|
|
29
39
|
* through the add-on resolve endpoint, call `resolveAddon` directly.
|
|
@@ -37,11 +47,22 @@ export type ResolvePgDatabaseOptions = ResolveAddonAttachmentOptions & {
|
|
|
37
47
|
* resolves to the same value on the app, they're treated as equivalent
|
|
38
48
|
* (e.g. the same database attached under two names) rather than ambiguous.
|
|
39
49
|
*
|
|
40
|
-
* Throws `
|
|
41
|
-
*
|
|
42
|
-
*
|
|
50
|
+
* Throws a `PgDatabaseNotFoundError` when the resolve endpoint 404s, the matched
|
|
51
|
+
* attachment is not the configured Postgres service, or the app has no databases.
|
|
52
|
+
* Throws an `AddonAttachmentAmbiguousError` when the remaining matches are not
|
|
53
|
+
* the same database. Throws a plain `Error` if `appIdentity` is empty and `input`
|
|
54
|
+
* has no `::`-prefixed app to fall back on.
|
|
55
|
+
*
|
|
56
|
+
* `resolvePgDatabase` should not throw a bare `NotFoundError`.
|
|
57
|
+
* Extensions that use it typically couple it with a second data call
|
|
58
|
+
* (e.g. `data.database.info(addon.id)`) that can also 404. Callers often
|
|
59
|
+
* catch `NotFoundError` to handle that second call's failure specifically.
|
|
60
|
+
* However, if a resolution failure surfaced as a `NotFoundError` too, that
|
|
61
|
+
* catch would swallow it and mislabel it as the second call's failure instead.
|
|
62
|
+
* Resolution failures are reported as `PgDatabaseNotFoundError` so the two stay
|
|
63
|
+
* separable by type.
|
|
43
64
|
*/
|
|
44
|
-
export declare function resolvePgDatabase(ctx: Pick<ResourceCtx, 'platform'>, options: ResolvePgDatabaseOptions & {
|
|
65
|
+
export declare function resolvePgDatabase(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, options: ResolvePgDatabaseOptions & {
|
|
45
66
|
includeAttachment: true;
|
|
46
67
|
}): Promise<ResolvedPgDatabaseWithAttachment>;
|
|
47
|
-
export declare function resolvePgDatabase(ctx: Pick<ResourceCtx, 'platform'>, options?: ResolvePgDatabaseOptions): Promise<ResolvedPgDatabase>;
|
|
68
|
+
export declare function resolvePgDatabase(ctx: Pick<ResourceCtx, 'platform'>, appIdentity: string, options?: ResolvePgDatabaseOptions): Promise<ResolvedPgDatabase>;
|
|
@@ -1,36 +1,105 @@
|
|
|
1
|
+
import { NotFoundError } from '@heroku/heroku-fetch';
|
|
1
2
|
import createDebug from 'debug';
|
|
2
|
-
import { AddonAttachmentAmbiguousError, resolveAddonAttachment } from '../../platform/add-on-attachment/resolve.js';
|
|
3
|
-
import { getAddonService } from './postgres-utils.js';
|
|
3
|
+
import { AddonAttachmentAmbiguousError, AddonAttachmentNotFoundError, resolveAddonAttachment } from '../../platform/add-on-attachment/resolve.js';
|
|
4
|
+
import { getAddonService, isPostgresAddon } from './postgres-utils.js';
|
|
4
5
|
const debug = createDebug('heroku:sdk:resources:database');
|
|
5
6
|
const DEFAULT_PG_ATTACHMENT = 'DATABASE_URL';
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Thrown by `resolvePgDatabase` when the attachment resolution endpoint
|
|
9
|
+
* does not find a match, the matched attachment is not the configured
|
|
10
|
+
* Postgres service, or the app has no Postgres databases at all.
|
|
11
|
+
*/
|
|
12
|
+
export class PgDatabaseNotFoundError extends Error {
|
|
13
|
+
constructor(message, options) {
|
|
14
|
+
super(message, options);
|
|
15
|
+
this.name = 'PgDatabaseNotFoundError';
|
|
11
16
|
}
|
|
17
|
+
}
|
|
18
|
+
export async function resolvePgDatabase(ctx, appIdentity, options = {}) {
|
|
19
|
+
const { includeAttachment, input, ...rest } = options;
|
|
12
20
|
const databaseReference = input ?? DEFAULT_PG_ATTACHMENT;
|
|
13
21
|
const { addon, app } = databaseReference.includes('::')
|
|
14
22
|
? parseBranchReference(databaseReference, appIdentity)
|
|
15
23
|
: { addon: databaseReference, app: appIdentity };
|
|
16
|
-
|
|
24
|
+
if (!app) {
|
|
25
|
+
throw new Error('resolvePgDatabase requires a non-empty app identity.');
|
|
26
|
+
}
|
|
27
|
+
debug('resolve input=%s addon=%s app=%s', input ?? '<default>', addon, app);
|
|
17
28
|
let attachment;
|
|
18
29
|
try {
|
|
19
|
-
attachment = await resolveAddonAttachment(ctx, app, addon, { ...rest, addonService:
|
|
30
|
+
attachment = await resolveAddonAttachment(ctx, app, addon, { ...rest, addonService: getAddonService() });
|
|
20
31
|
}
|
|
21
32
|
catch (error) {
|
|
22
|
-
if (
|
|
33
|
+
if (error instanceof NotFoundError || error instanceof AddonAttachmentNotFoundError) {
|
|
34
|
+
throw await buildDatabaseNotFoundError(ctx, app, addon, error, rest.signal);
|
|
35
|
+
}
|
|
36
|
+
if (error instanceof AddonAttachmentAmbiguousError) {
|
|
37
|
+
const equivalent = collapseIfEquivalent(error.matches);
|
|
38
|
+
if (!equivalent)
|
|
39
|
+
throw error;
|
|
40
|
+
let config;
|
|
41
|
+
try {
|
|
42
|
+
config = await ctx.platform
|
|
43
|
+
.withOptions({ signal: rest.signal })
|
|
44
|
+
.configVar.infoForApp(equivalent[0].app.name);
|
|
45
|
+
}
|
|
46
|
+
catch (configVarError) {
|
|
47
|
+
rest.signal?.throwIfAborted();
|
|
48
|
+
debug('config var lookup failed while collapsing ambiguous matches: %O', configVarError);
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
if (!allMatchSameUrl(equivalent, config))
|
|
52
|
+
throw error;
|
|
53
|
+
attachment = equivalent[0];
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
23
56
|
throw error;
|
|
24
|
-
|
|
25
|
-
if (!equivalent)
|
|
26
|
-
throw error;
|
|
27
|
-
const config = await ctx.platform.configVar.infoForApp(equivalent[0].app.name);
|
|
28
|
-
if (!allMatchSameUrl(equivalent, config))
|
|
29
|
-
throw error;
|
|
30
|
-
attachment = equivalent[0];
|
|
57
|
+
}
|
|
31
58
|
}
|
|
32
59
|
return includeAttachment ? { addon: attachment.addon, attachment } : attachment.addon;
|
|
33
60
|
}
|
|
61
|
+
async function buildDatabaseNotFoundError(ctx, appIdentity, attachmentId, error, signal) {
|
|
62
|
+
let attachments;
|
|
63
|
+
try {
|
|
64
|
+
attachments = await listPostgresAttachments(ctx, appIdentity, signal);
|
|
65
|
+
}
|
|
66
|
+
catch (listError) {
|
|
67
|
+
signal?.throwIfAborted();
|
|
68
|
+
if (listError instanceof NotFoundError) {
|
|
69
|
+
// `NotFoundError` from the attachment resolution endpoint is ambiguous.
|
|
70
|
+
// It could be because the app doesn't exist, or because the attachment
|
|
71
|
+
// doesn't exist. The endpoint for listing all attachments only 404s when
|
|
72
|
+
// the app doesn't exist, so we rethrow the error to cover that case.
|
|
73
|
+
throw new PgDatabaseNotFoundError(`Couldn't find that app: ${appIdentity}`, { cause: listError });
|
|
74
|
+
}
|
|
75
|
+
debug('listing attachments failed while building not-found error: %O', listError);
|
|
76
|
+
throw new PgDatabaseNotFoundError(`Could not resolve ${stripUrlSuffix(attachmentId)} on ${appIdentity}`, { cause: error });
|
|
77
|
+
}
|
|
78
|
+
if (attachments.length === 0) {
|
|
79
|
+
return new PgDatabaseNotFoundError(`${appIdentity} has no ${getAddonService()} databases`, { cause: error });
|
|
80
|
+
}
|
|
81
|
+
const validOptions = attachments
|
|
82
|
+
.map(attachment => {
|
|
83
|
+
const urlVarName = urlConfigVarName(attachment.config_vars);
|
|
84
|
+
return urlVarName ? stripUrlSuffix(urlVarName) : attachment.name;
|
|
85
|
+
})
|
|
86
|
+
.join(', ');
|
|
87
|
+
const databaseName = stripUrlSuffix(attachmentId);
|
|
88
|
+
if (error instanceof AddonAttachmentNotFoundError) {
|
|
89
|
+
return new PgDatabaseNotFoundError(`${databaseName} is not a ${getAddonService()} database on ${appIdentity}. Valid options are: ${validOptions}`, { cause: error });
|
|
90
|
+
}
|
|
91
|
+
return new PgDatabaseNotFoundError(`Unknown database: ${databaseName} on ${appIdentity}. Valid options are: ${validOptions}`, { cause: error });
|
|
92
|
+
}
|
|
93
|
+
async function listPostgresAttachments(ctx, appIdentity, signal) {
|
|
94
|
+
const attachments = await ctx.platform
|
|
95
|
+
.withHeaders({ 'Accept-Inclusion': 'addon:plan,config_vars' })
|
|
96
|
+
.withOptions({ signal })
|
|
97
|
+
.addOnAttachment.listByApp(appIdentity);
|
|
98
|
+
return attachments.filter(attachment => isPostgresAddon(attachment.addon));
|
|
99
|
+
}
|
|
100
|
+
function stripUrlSuffix(name) {
|
|
101
|
+
return name.endsWith('_URL') ? name.slice(0, -4) : name;
|
|
102
|
+
}
|
|
34
103
|
/**
|
|
35
104
|
* Narrows ambiguous matches to a same-add-on group worth comparing by
|
|
36
105
|
* config var value. Returns `undefined` (not worth a config-vars round
|
|
@@ -62,5 +131,5 @@ function parseBranchReference(reference, fallbackApp) {
|
|
|
62
131
|
if (match) {
|
|
63
132
|
return { addon: match[2], app: match[1] };
|
|
64
133
|
}
|
|
65
|
-
return
|
|
134
|
+
return { addon: reference, app: fallbackApp };
|
|
66
135
|
}
|
|
@@ -2,7 +2,7 @@ import { extendResource } from '../../core/extend-resource.js';
|
|
|
2
2
|
import { resolvePgDatabase } from './internal/resolve-pg-database.js';
|
|
3
3
|
export async function info(ctx, appIdentity, addonIdentity, options = {}) {
|
|
4
4
|
options.signal?.throwIfAborted();
|
|
5
|
-
const addon = await resolvePgDatabase(ctx,
|
|
5
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
6
6
|
return ctx.data.maintenance.info(addon.id);
|
|
7
7
|
}
|
|
8
8
|
export const maintenanceExtensions = extendResource('data', 'maintenance', ctx => ({
|
|
@@ -2,7 +2,7 @@ import { extendResource } from '../../core/extend-resource.js';
|
|
|
2
2
|
import { resolvePgDatabase } from './internal/resolve-pg-database.js';
|
|
3
3
|
export async function listCredentials(ctx, appIdentity, addonIdentity, options = {}) {
|
|
4
4
|
options.signal?.throwIfAborted();
|
|
5
|
-
const addon = await resolvePgDatabase(ctx,
|
|
5
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
6
6
|
return ctx.data.postgresDatabase.listCredentials(addon.id);
|
|
7
7
|
}
|
|
8
8
|
export const postgresDatabaseExtensions = extendResource('data', 'postgresDatabase', ctx => ({
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TransferInfoByAppResult } from '@heroku/types/data';
|
|
2
2
|
import type { ResourceCtx } from '../../../core/extend-resource.js';
|
|
3
3
|
import type { ResolvedPgDatabase } from '../internal/resolve-pg-database.js';
|
|
4
|
-
import type { WaitForTransferOptions } from '
|
|
4
|
+
import type { WaitForTransferOptions } from './wait-for-transfer.js';
|
|
5
5
|
import { Poller } from '../../../utils/poller.js';
|
|
6
|
+
/**
|
|
7
|
+
* `data.backup.create`'s declared return type, `BackupCreateResult`, only
|
|
8
|
+
* lists `uuid`/`from_type`/`to_type`, but the actual response includes the
|
|
9
|
+
* full transfer record. Widen the type here so callers (e.g. `waitPoller`) can
|
|
10
|
+
* use the full transfer record.
|
|
11
|
+
*/
|
|
12
|
+
export type BackupCreateInfo = TransferInfoByAppResult;
|
|
6
13
|
export type CaptureAndWaitOptions = WaitForTransferOptions & {
|
|
7
14
|
/**
|
|
8
15
|
* Progress hooks fired once before capturing the backup `poller.onStart(addon)`
|
|
@@ -14,6 +21,6 @@ export type CaptureAndWaitOptions = WaitForTransferOptions & {
|
|
|
14
21
|
* complete `poller.onStart(backup)` and once after the transfer
|
|
15
22
|
* finishes successfully `poller.onStop(backup)`.
|
|
16
23
|
*/
|
|
17
|
-
waitPoller?: Poller<
|
|
24
|
+
waitPoller?: Poller<BackupCreateInfo>;
|
|
18
25
|
};
|
|
19
26
|
export declare function captureAndWait(ctx: Pick<ResourceCtx, 'data' | 'platform'>, appIdentity: string, addonIdentity?: string, options?: CaptureAndWaitOptions): Promise<TransferInfoByAppResult>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { resolvePgDatabase } from '../internal/resolve-pg-database.js';
|
|
2
|
-
import { waitForTransfer } from '
|
|
2
|
+
import { waitForTransfer } from './wait-for-transfer.js';
|
|
3
3
|
export async function captureAndWait(ctx, appIdentity, addonIdentity, options = {}) {
|
|
4
4
|
const { capturePoller, signal, waitPoller } = options;
|
|
5
5
|
signal?.throwIfAborted();
|
|
6
|
-
const addon = await resolvePgDatabase(ctx,
|
|
6
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
7
7
|
capturePoller?.onStart?.(addon);
|
|
8
8
|
const backup = await ctx.data.backup.create(addon.id);
|
|
9
9
|
capturePoller?.onStop?.(addon);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { CaptureAndWaitOptions } from './capture-and-wait.js';
|
|
2
|
+
import type { RestoreAndWaitOptions } from './restore-and-wait.js';
|
|
3
|
+
export { captureAndWait, type CaptureAndWaitOptions } from './capture-and-wait.js';
|
|
4
|
+
export { restoreAndWait, type RestoreAndWaitOptions } from './restore-and-wait.js';
|
|
5
|
+
export { transferExtensions, TransferFailedError, TransferTimeoutError, type WaitForTransferOptions, } from './wait-for-transfer.js';
|
|
6
|
+
export declare const restoreExtensions: import("../../../core/extend-resource.js").ResourceExtension<"data", "restore", {
|
|
7
|
+
restoreAndWait: (appIdentity: string, addonIdentity: string | undefined, backupUrl: string, options?: RestoreAndWaitOptions) => Promise<import("@heroku/types/data").TransferInfoByAppResult>;
|
|
8
|
+
}>;
|
|
9
|
+
export declare const backupExtensions: import("../../../core/extend-resource.js").ResourceExtension<"data", "backup", {
|
|
10
|
+
captureAndWait: (appIdentity: string, addonIdentity?: string, options?: CaptureAndWaitOptions) => Promise<import("@heroku/types/data").TransferInfoByAppResult>;
|
|
11
|
+
}>;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { extendResource } from '../../../core/extend-resource.js';
|
|
2
|
+
import { captureAndWait } from './capture-and-wait.js';
|
|
3
|
+
import { restoreAndWait } from './restore-and-wait.js';
|
|
4
|
+
export { captureAndWait } from './capture-and-wait.js';
|
|
5
|
+
export { restoreAndWait } from './restore-and-wait.js';
|
|
6
|
+
export { transferExtensions, TransferFailedError, TransferTimeoutError, } from './wait-for-transfer.js';
|
|
7
|
+
export const restoreExtensions = extendResource('data', 'restore', ctx => ({
|
|
8
|
+
restoreAndWait: (appIdentity, addonIdentity, backupUrl, options) => restoreAndWait(ctx, appIdentity, addonIdentity, backupUrl, options),
|
|
9
|
+
}));
|
|
10
|
+
export const backupExtensions = extendResource('data', 'backup', ctx => ({
|
|
11
|
+
captureAndWait: (appIdentity, addonIdentity, options) => captureAndWait(ctx, appIdentity, addonIdentity, options),
|
|
12
|
+
}));
|
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { TransferInfoByAppResult } from '@heroku/types/data';
|
|
2
2
|
import type { ResourceCtx } from '../../../core/extend-resource.js';
|
|
3
3
|
import type { ResolvedPgDatabase } from '../internal/resolve-pg-database.js';
|
|
4
|
-
import type { WaitForTransferOptions } from '
|
|
4
|
+
import type { WaitForTransferOptions } from './wait-for-transfer.js';
|
|
5
5
|
import { Poller } from '../../../utils/poller.js';
|
|
6
|
+
/**
|
|
7
|
+
* `data.restore.create`'s declared return type, `RestoreCreateResult`, only
|
|
8
|
+
* lists `uuid`/`from_type`/`to_type`, but the actual response includes the
|
|
9
|
+
* full transfer record. Widen the type here so callers (e.g. `waitPoller`) can
|
|
10
|
+
* use the full transfer record.
|
|
11
|
+
*/
|
|
12
|
+
export type RestoreCreateInfo = TransferInfoByAppResult;
|
|
6
13
|
export type RestoreAndWaitOptions = WaitForTransferOptions & {
|
|
7
14
|
/**
|
|
8
15
|
* Extension names to preinstall on the restore target.
|
|
@@ -19,6 +26,6 @@ export type RestoreAndWaitOptions = WaitForTransferOptions & {
|
|
|
19
26
|
* complete `poller.onStart(restore)` and once after the restore
|
|
20
27
|
* reaches a terminal state `poller.onStop(restore)`.
|
|
21
28
|
*/
|
|
22
|
-
waitPoller?: Poller<
|
|
29
|
+
waitPoller?: Poller<RestoreCreateInfo>;
|
|
23
30
|
};
|
|
24
31
|
export declare function restoreAndWait(ctx: Pick<ResourceCtx, 'data' | 'platform'>, appIdentity: string, addonIdentity: string | undefined, backupUrl: string, options?: RestoreAndWaitOptions): Promise<TransferInfoByAppResult>;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { resolvePgDatabase } from '../internal/resolve-pg-database.js';
|
|
2
|
-
import { waitForTransfer } from '
|
|
2
|
+
import { waitForTransfer } from './wait-for-transfer.js';
|
|
3
3
|
export async function restoreAndWait(ctx, appIdentity, addonIdentity, backupUrl, options = {}) {
|
|
4
4
|
const { extensions, restorePoller, signal, waitPoller } = options;
|
|
5
5
|
signal?.throwIfAborted();
|
|
6
|
-
const addon = await resolvePgDatabase(ctx,
|
|
6
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
7
7
|
restorePoller?.onStart?.(addon);
|
|
8
8
|
// eslint-disable-next-line camelcase
|
|
9
9
|
const restore = await ctx.data.restore.create(addon.id, { backup_url: backupUrl, extensions });
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { TransferInfoByAppResult } from '@heroku/types/data';
|
|
2
|
-
import type { ResourceCtx } from '
|
|
2
|
+
import type { ResourceCtx } from '../../../core/extend-resource.js';
|
|
3
3
|
/**
|
|
4
4
|
* Thrown by `waitForTransfer` when the transfer reaches a terminal state
|
|
5
5
|
* with `succeeded: false`.
|
|
@@ -59,6 +59,6 @@ export type WaitForTransferOptions = {
|
|
|
59
59
|
* if the transfer does not finish before timeout.
|
|
60
60
|
*/
|
|
61
61
|
export declare function waitForTransfer(ctx: Pick<ResourceCtx, 'data'>, appIdentity: string, transferId: string, options?: WaitForTransferOptions): Promise<TransferInfoByAppResult>;
|
|
62
|
-
export declare const transferExtensions: import("
|
|
62
|
+
export declare const transferExtensions: import("../../../core/extend-resource.js").ResourceExtension<"data", "transfer", {
|
|
63
63
|
waitForTransfer: (appIdentity: string, transferId: string, options?: WaitForTransferOptions) => Promise<TransferInfoByAppResult>;
|
|
64
64
|
}>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { extendResource } from '
|
|
2
|
-
import { wait } from '
|
|
1
|
+
import { extendResource } from '../../../core/extend-resource.js';
|
|
2
|
+
import { wait } from '../../../utils/wait.js';
|
|
3
3
|
const DEFAULT_INTERVAL_MS = 3000;
|
|
4
4
|
const MAX_FAILURES = 20;
|
|
5
5
|
/**
|
|
@@ -9,8 +9,8 @@ export async function list(ctx, appIdentity, options = {}) {
|
|
|
9
9
|
}
|
|
10
10
|
export async function create(ctx, appIdentity, addonIdentity, body, options = {}) {
|
|
11
11
|
options.signal?.throwIfAborted();
|
|
12
|
-
const { addon, attachment } = await resolvePgDatabase(ctx, {
|
|
13
|
-
|
|
12
|
+
const { addon, attachment } = await resolvePgDatabase(ctx, appIdentity, {
|
|
13
|
+
includeAttachment: true, input: addonIdentity, ...options,
|
|
14
14
|
});
|
|
15
15
|
let requestBody = body;
|
|
16
16
|
if (!requestBody.schedule_name) {
|
|
@@ -21,7 +21,7 @@ export async function create(ctx, appIdentity, addonIdentity, body, options = {}
|
|
|
21
21
|
}
|
|
22
22
|
export async function del(ctx, appIdentity, addonIdentity, scheduleId, options = {}) {
|
|
23
23
|
options.signal?.throwIfAborted();
|
|
24
|
-
const addon = await resolvePgDatabase(ctx,
|
|
24
|
+
const addon = await resolvePgDatabase(ctx, appIdentity, { input: addonIdentity, ...options });
|
|
25
25
|
return ctx.data.transferSchedule.delete(addon.id, scheduleId);
|
|
26
26
|
}
|
|
27
27
|
export const transferScheduleExtensions = extendResource('data', 'transferSchedule', ctx => ({
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
export { backupExtensions } from '../data/backup/index.js';
|
|
2
1
|
export { databaseExtensions } from '../data/database.js';
|
|
3
2
|
export { maintenanceExtensions } from '../data/maintenance.js';
|
|
4
3
|
export { postgresDatabaseExtensions } from '../data/postgres-database.js';
|
|
5
4
|
export { redisExtensions } from '../data/redis/index.js';
|
|
6
|
-
export { restoreExtensions } from '../data/restore/index.js';
|
|
7
5
|
export { type TransferSchedule, transferScheduleExtensions } from '../data/transfer-schedule.js';
|
|
8
|
-
export { transferExtensions, TransferFailedError, TransferTimeoutError, } from '../data/
|
|
6
|
+
export { backupExtensions, type CaptureAndWaitOptions, type RestoreAndWaitOptions, restoreExtensions, transferExtensions, TransferFailedError, TransferTimeoutError, type WaitForTransferOptions, } from '../data/transfer/index.js';
|
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
export { backupExtensions } from '../data/backup/index.js';
|
|
2
1
|
export { databaseExtensions } from '../data/database.js';
|
|
3
2
|
export { maintenanceExtensions } from '../data/maintenance.js';
|
|
4
3
|
export { postgresDatabaseExtensions } from '../data/postgres-database.js';
|
|
5
4
|
export { redisExtensions } from '../data/redis/index.js';
|
|
6
|
-
export { restoreExtensions } from '../data/restore/index.js';
|
|
7
5
|
export { transferScheduleExtensions } from '../data/transfer-schedule.js';
|
|
8
|
-
export { transferExtensions, TransferFailedError, TransferTimeoutError, } from '../data/
|
|
6
|
+
export { backupExtensions, restoreExtensions, transferExtensions, TransferFailedError, TransferTimeoutError, } from '../data/transfer/index.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@heroku/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Heroku SDK",
|
|
5
5
|
"author": "Heroku",
|
|
6
6
|
"type": "module",
|
|
@@ -102,18 +102,15 @@
|
|
|
102
102
|
"types": "./dist/resources/platform/vpn-connection/index.d.ts",
|
|
103
103
|
"default": "./dist/resources/platform/vpn-connection/index.js"
|
|
104
104
|
},
|
|
105
|
-
"./resources/data/backup": {
|
|
106
|
-
"types": "./dist/resources/data/backup/index.d.ts",
|
|
107
|
-
"default": "./dist/resources/data/backup/index.js"
|
|
108
|
-
},
|
|
109
105
|
"./resources/data/redis": {
|
|
110
106
|
"types": "./dist/resources/data/redis/index.d.ts",
|
|
111
107
|
"default": "./dist/resources/data/redis/index.js"
|
|
112
108
|
},
|
|
113
|
-
"./resources/data/
|
|
114
|
-
"types": "./dist/resources/data/
|
|
115
|
-
"default": "./dist/resources/data/
|
|
109
|
+
"./resources/data/transfer": {
|
|
110
|
+
"types": "./dist/resources/data/transfer/index.d.ts",
|
|
111
|
+
"default": "./dist/resources/data/transfer/index.js"
|
|
116
112
|
},
|
|
113
|
+
"./resources/data/internal/*": null,
|
|
117
114
|
"./resources/*": {
|
|
118
115
|
"types": "./dist/resources/*.d.ts",
|
|
119
116
|
"default": "./dist/resources/*.js"
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { CaptureAndWaitOptions } from './capture-and-wait.js';
|
|
2
|
-
export { captureAndWait, type CaptureAndWaitOptions } from './capture-and-wait.js';
|
|
3
|
-
export declare const backupExtensions: import("../../../core/extend-resource.js").ResourceExtension<"data", "backup", {
|
|
4
|
-
captureAndWait: (appIdentity: string, addonIdentity?: string, options?: CaptureAndWaitOptions) => Promise<import("@heroku/types/data").TransferInfoByAppResult>;
|
|
5
|
-
}>;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { extendResource } from '../../../core/extend-resource.js';
|
|
2
|
-
import { captureAndWait } from './capture-and-wait.js';
|
|
3
|
-
export { captureAndWait } from './capture-and-wait.js';
|
|
4
|
-
export const backupExtensions = extendResource('data', 'backup', ctx => ({
|
|
5
|
-
captureAndWait: (appIdentity, addonIdentity, options) => captureAndWait(ctx, appIdentity, addonIdentity, options),
|
|
6
|
-
}));
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import type { RestoreAndWaitOptions } from './restore-and-wait.js';
|
|
2
|
-
export { restoreAndWait, type RestoreAndWaitOptions } from './restore-and-wait.js';
|
|
3
|
-
export declare const restoreExtensions: import("../../../core/extend-resource.js").ResourceExtension<"data", "restore", {
|
|
4
|
-
restoreAndWait: (appIdentity: string, addonIdentity: string | undefined, backupUrl: string, options?: RestoreAndWaitOptions) => Promise<import("@heroku/types/data").TransferInfoByAppResult>;
|
|
5
|
-
}>;
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { extendResource } from '../../../core/extend-resource.js';
|
|
2
|
-
import { restoreAndWait } from './restore-and-wait.js';
|
|
3
|
-
export { restoreAndWait } from './restore-and-wait.js';
|
|
4
|
-
export const restoreExtensions = extendResource('data', 'restore', ctx => ({
|
|
5
|
-
restoreAndWait: (appIdentity, addonIdentity, backupUrl, options) => restoreAndWait(ctx, appIdentity, addonIdentity, backupUrl, options),
|
|
6
|
-
}));
|