@geekmidas/cloud 10.0.0-alpha.11 → 10.0.0-alpha.13
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/index.cjs +3 -3
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +2 -3
- package/dist/utils/index.cjs +106 -5
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +62 -2
- package/dist/utils/index.d.cts.map +1 -0
- package/dist/utils/index.d.mts +62 -2
- package/dist/utils/index.d.mts.map +1 -0
- package/dist/utils/index.mjs +78 -2
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +7 -7
- package/src/dokploy/Application.ts +7 -0
- package/src/dokploy/__tests__/Application.provider.spec.ts +148 -0
- package/src/sst/__tests__/api.spec.ts +230 -0
- package/src/sst/__tests__/fromManifest.integration.spec.ts +368 -0
- package/src/sst/__tests__/function.spec.ts +192 -0
- package/src/sst/__tests__/links.spec.ts +209 -0
- package/src/sst/aws/Cache.ts +4 -4
- package/src/sst/aws/Database.ts +15 -1
- package/src/sst/fromManifest.ts +10 -3
- package/dist/index-B5CZ1xVf.d.cts +0 -64
- package/dist/index-B5CZ1xVf.d.cts.map +0 -1
- package/dist/index-DhHRjduZ.d.mts +0 -64
- package/dist/index-DhHRjduZ.d.mts.map +0 -1
- package/dist/utils-B1a2UuEO.cjs +0 -129
- package/dist/utils-B1a2UuEO.cjs.map +0 -1
- package/dist/utils-DMOXJ27j.mjs +0 -83
- package/dist/utils-DMOXJ27j.mjs.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
exports.buildResourceEnv =
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_utils_index = require("./utils/index.cjs");
|
|
3
|
+
exports.buildResourceEnv = require_utils_index.buildResourceEnv;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { buildResourceEnv } from "./index
|
|
1
|
+
import { buildResourceEnv } from "./utils/index.cjs";
|
|
2
2
|
export { buildResourceEnv };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { buildResourceEnv } from "./index
|
|
1
|
+
import { buildResourceEnv } from "./utils/index.mjs";
|
|
2
2
|
export { buildResourceEnv };
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,2 @@
|
|
|
1
|
-
import { buildResourceEnv } from "./utils
|
|
2
|
-
|
|
3
|
-
export { buildResourceEnv };
|
|
1
|
+
import { buildResourceEnv } from "./utils/index.mjs";
|
|
2
|
+
export { buildResourceEnv };
|
package/dist/utils/index.cjs
CHANGED
|
@@ -1,6 +1,107 @@
|
|
|
1
|
-
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
let node_os = require("node:os");
|
|
25
|
+
node_os = __toESM(node_os, 1);
|
|
26
|
+
let lodash_snakecase = require("lodash.snakecase");
|
|
27
|
+
lodash_snakecase = __toESM(lodash_snakecase, 1);
|
|
28
|
+
//#region src/utils/index.ts
|
|
29
|
+
function getLocalIpAddress() {
|
|
30
|
+
const networkInterfaces = node_os.default.networkInterfaces();
|
|
31
|
+
for (const interfaceName in networkInterfaces) {
|
|
32
|
+
const addresses = networkInterfaces[interfaceName] || [];
|
|
33
|
+
for (const addressInfo of addresses) if (addressInfo.family === "IPv4" && !addressInfo.internal) return addressInfo.address;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function environmentCase(name) {
|
|
38
|
+
return (0, lodash_snakecase.default)(name).toUpperCase().replace(/_\d+/g, (r) => {
|
|
39
|
+
return r.replace("_", "");
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
let ResourceType = /* @__PURE__ */ function(ResourceType) {
|
|
43
|
+
ResourceType["ApiGatewayV2"] = "sst.aws.ApiGatewayV2";
|
|
44
|
+
ResourceType["Postgres"] = "sst.aws.Postgres";
|
|
45
|
+
ResourceType["Function"] = "sst.aws.Function";
|
|
46
|
+
ResourceType["ObjectStorage"] = "sst.aws.Bucket";
|
|
47
|
+
ResourceType["Vpc"] = "sst.aws.Vpc";
|
|
48
|
+
ResourceType["Secret"] = "sst.sst.Secret";
|
|
49
|
+
ResourceType["SSTSecret"] = "sst:sst:Secret";
|
|
50
|
+
ResourceType["SSTFunction"] = "sst:sst:Function";
|
|
51
|
+
ResourceType["SSTApiGatewayV2"] = "sst:aws:ApiGatewayV2";
|
|
52
|
+
ResourceType["SSTPostgres"] = "sst:aws:Postgres";
|
|
53
|
+
ResourceType["SSTObjectStorage"] = "sst:aws:Bucket";
|
|
54
|
+
ResourceType["SnsTopic"] = "sst:aws:SnsTopic";
|
|
55
|
+
return ResourceType;
|
|
56
|
+
}({});
|
|
57
|
+
const secret = (name, value) => ({ [environmentCase(name)]: value.value });
|
|
58
|
+
const postgres = (key, value) => {
|
|
59
|
+
const prefix = `${environmentCase(key)}`;
|
|
60
|
+
return {
|
|
61
|
+
[`${prefix}_NAME`]: value.database,
|
|
62
|
+
[`${prefix}_HOST`]: value.host,
|
|
63
|
+
[`${prefix}_PASSWORD`]: value.password,
|
|
64
|
+
[`${prefix}_PORT`]: value.port,
|
|
65
|
+
[`${prefix}_USERNAME`]: value.username
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
const bucket = (name, value) => {
|
|
69
|
+
return { [`${`${environmentCase(name)}`}_NAME`]: value.name };
|
|
70
|
+
};
|
|
71
|
+
const topic = (name, value) => {
|
|
72
|
+
return { [`${`${environmentCase(name)}`}_ARN`]: value.arn };
|
|
73
|
+
};
|
|
74
|
+
const noop = (_name, _value) => ({});
|
|
75
|
+
const processors = {
|
|
76
|
+
["sst.aws.ApiGatewayV2"]: noop,
|
|
77
|
+
["sst.aws.Function"]: noop,
|
|
78
|
+
["sst.aws.Vpc"]: noop,
|
|
79
|
+
["sst.sst.Secret"]: secret,
|
|
80
|
+
["sst.aws.Postgres"]: postgres,
|
|
81
|
+
["sst.aws.Bucket"]: bucket,
|
|
82
|
+
["sst:sst:Secret"]: secret,
|
|
83
|
+
["sst:aws:Bucket"]: bucket,
|
|
84
|
+
["sst:sst:Function"]: noop,
|
|
85
|
+
["sst:aws:Postgres"]: postgres,
|
|
86
|
+
["sst:aws:ApiGatewayV2"]: noop,
|
|
87
|
+
["sst:aws:SnsTopic"]: topic
|
|
88
|
+
};
|
|
89
|
+
function buildResourceEnv(record) {
|
|
90
|
+
const env = {};
|
|
91
|
+
for (const [k, value] of Object.entries(record)) {
|
|
92
|
+
if (typeof value === "string") {
|
|
93
|
+
env[environmentCase(k)] = value;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const processor = processors[value.type];
|
|
97
|
+
if (processor) Object.assign(env, processor(k, value));
|
|
98
|
+
}
|
|
99
|
+
return env;
|
|
100
|
+
}
|
|
101
|
+
//#endregion
|
|
102
|
+
exports.ResourceType = ResourceType;
|
|
103
|
+
exports.buildResourceEnv = buildResourceEnv;
|
|
104
|
+
exports.environmentCase = environmentCase;
|
|
105
|
+
exports.getLocalIpAddress = getLocalIpAddress;
|
|
2
106
|
|
|
3
|
-
|
|
4
|
-
exports.buildResourceEnv = require_utils.buildResourceEnv;
|
|
5
|
-
exports.environmentCase = require_utils.environmentCase;
|
|
6
|
-
exports.getLocalIpAddress = require_utils.getLocalIpAddress;
|
|
107
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["os","snakecase"],"sources":["../../src/utils/index.ts"],"sourcesContent":["import os from 'node:os';\nimport snakecase from 'lodash.snakecase';\n\nexport function getLocalIpAddress() {\n\tconst networkInterfaces = os.networkInterfaces();\n\tfor (const interfaceName in networkInterfaces) {\n\t\tconst addresses = networkInterfaces[interfaceName] || [];\n\t\tfor (const addressInfo of addresses) {\n\t\t\t// Look for an IPv4 address that is not internal (loopback)\n\t\t\tif (addressInfo.family === 'IPv4' && !addressInfo.internal) {\n\t\t\t\treturn addressInfo.address;\n\t\t\t}\n\t\t}\n\t}\n\treturn null; // No suitable IP address found\n}\n\nexport function environmentCase(name: string) {\n\treturn snakecase(name)\n\t\t.toUpperCase()\n\t\t.replace(/_\\d+/g, (r) => {\n\t\t\treturn r.replace('_', '');\n\t\t});\n}\nexport enum ResourceType {\n\tApiGatewayV2 = 'sst.aws.ApiGatewayV2',\n\tPostgres = 'sst.aws.Postgres',\n\tFunction = 'sst.aws.Function',\n\tObjectStorage = 'sst.aws.Bucket',\n\tVpc = 'sst.aws.Vpc',\n\tSecret = 'sst.sst.Secret',\n\tSSTSecret = 'sst:sst:Secret',\n\tSSTFunction = 'sst:sst:Function',\n\tSSTApiGatewayV2 = 'sst:aws:ApiGatewayV2',\n\tSSTPostgres = 'sst:aws:Postgres',\n\tSSTObjectStorage = 'sst:aws:Bucket',\n\tSnsTopic = 'sst:aws:SnsTopic',\n}\n\nconst secret = (name: string, value: Secret) => ({\n\t[environmentCase(name)]: value.value,\n});\nconst postgres = (key: string, value: Postgres) => {\n\tconst prefix = `${environmentCase(key)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.database,\n\t\t[`${prefix}_HOST`]: value.host,\n\t\t[`${prefix}_PASSWORD`]: value.password,\n\t\t[`${prefix}_PORT`]: value.port,\n\t\t[`${prefix}_USERNAME`]: value.username,\n\t};\n};\n\nconst bucket = (name: string, value: Bucket) => {\n\tconst prefix = `${environmentCase(name)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.name,\n\t};\n};\n\nconst topic = (name: string, value: SnsTopic) => {\n\tconst prefix = `${environmentCase(name)}`;\n\tconst key = `${prefix}_ARN`;\n\n\treturn {\n\t\t[key]: value.arn,\n\t};\n};\n\nconst noop = (_name: string, _value: any) => ({});\n\nconst processors: Record<ResourceType, ResourceProcessor<any>> = {\n\t[ResourceType.ApiGatewayV2]: noop,\n\t[ResourceType.Function]: noop,\n\t[ResourceType.Vpc]: noop,\n\t[ResourceType.Secret]: secret,\n\t[ResourceType.Postgres]: postgres,\n\t[ResourceType.ObjectStorage]: bucket,\n\n\t[ResourceType.SSTSecret]: secret,\n\t[ResourceType.SSTObjectStorage]: bucket,\n\t[ResourceType.SSTFunction]: noop,\n\t[ResourceType.SSTPostgres]: postgres,\n\t[ResourceType.SSTApiGatewayV2]: noop,\n\t[ResourceType.SnsTopic]: topic,\n};\n\nexport function buildResourceEnv(\n\trecord: Record<string, Resource | string>,\n): Record<string, string> {\n\tconst env: Record<string, string> = {};\n\tfor (const [k, value] of Object.entries(record)) {\n\t\tif (typeof value === 'string') {\n\t\t\tenv[environmentCase(k)] = value;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst processor = processors[value.type];\n\t\tif (processor) {\n\t\t\tObject.assign(env, processor(k, value));\n\t\t} else {\n\t\t}\n\t}\n\n\treturn env;\n}\n\n/**\n * SST links a resource under one of two spellings — `sst.aws.Bucket` when it\n * comes from the state file, `sst:aws:Bucket` when it comes from the runtime —\n * and `processors` has always handled both. Only the dotted half was admitted\n * here, so the colon half could not be passed to `buildResourceEnv` without a\n * cast, and `SnsTopic` was missing from `Resource` altogether despite being the\n * only thing that produces `*_ARN`.\n */\nexport type ApiGatewayV2 = {\n\ttype: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;\n\turl: string;\n};\n\nexport type Postgres = {\n\tdatabase: string;\n\thost: string;\n\tpassword: string;\n\tport: number;\n\ttype: ResourceType.Postgres | ResourceType.SSTPostgres;\n\tusername: string;\n};\n\nexport type Function = {\n\tname: string;\n\ttype: ResourceType.Function | ResourceType.SSTFunction;\n};\n\nexport type Bucket = {\n\tname: string;\n\ttype: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;\n};\n\nexport type SnsTopic = {\n\tarn: string;\n\ttype: ResourceType.SnsTopic;\n};\n\nexport type Vpc = {\n\tbastion: string;\n\ttype: ResourceType.Vpc;\n};\n\nexport type Secret = {\n\ttype: ResourceType.Secret | ResourceType.SSTSecret;\n\tvalue: string;\n};\n\nexport type Resource =\n\t| ApiGatewayV2\n\t| Postgres\n\t| Function\n\t| Bucket\n\t| SnsTopic\n\t| Vpc\n\t| Secret;\n\nexport type ResourceProcessor<K extends Resource> = (\n\tname: string,\n\tvalue: K,\n) => Record<string, string | number>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGA,SAAgB,oBAAoB;CACnC,MAAM,oBAAoBA,QAAAA,QAAG,kBAAkB;CAC/C,KAAK,MAAM,iBAAiB,mBAAmB;EAC9C,MAAM,YAAY,kBAAkB,kBAAkB,CAAC;EACvD,KAAK,MAAM,eAAe,WAEzB,IAAI,YAAY,WAAW,UAAU,CAAC,YAAY,UACjD,OAAO,YAAY;CAGtB;CACA,OAAO;AACR;AAEA,SAAgB,gBAAgB,MAAc;CAC7C,QAAA,GAAOC,iBAAAA,QAAAA,CAAU,IAAI,CAAC,CACpB,YAAY,CAAC,CACb,QAAQ,UAAU,MAAM;EACxB,OAAO,EAAE,QAAQ,KAAK,EAAE;CACzB,CAAC;AACH;AACA,IAAY,eAAL,yBAAA,cAAA;CACN,aAAA,kBAAA;CACA,aAAA,cAAA;CACA,aAAA,cAAA;CACA,aAAA,mBAAA;CACA,aAAA,SAAA;CACA,aAAA,YAAA;CACA,aAAA,eAAA;CACA,aAAA,iBAAA;CACA,aAAA,qBAAA;CACA,aAAA,iBAAA;CACA,aAAA,sBAAA;CACA,aAAA,cAAA;;AACD,EAAA,CAAA,CAAA;AAEA,MAAM,UAAU,MAAc,WAAmB,GAC/C,gBAAgB,IAAI,IAAI,MAAM,MAChC;AACA,MAAM,YAAY,KAAa,UAAoB;CAClD,MAAM,SAAS,GAAG,gBAAgB,GAAG;CACrC,OAAO;GACL,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,aAAa,MAAM;GAC7B,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,aAAa,MAAM;CAC/B;AACD;AAEA,MAAM,UAAU,MAAc,UAAkB;CAE/C,OAAO,GACL,GAAG,GAFa,gBAAgB,IAAI,IAE1B,SAAS,MAAM,KAC3B;AACD;AAEA,MAAM,SAAS,MAAc,UAAoB;CAIhD,OAAO,GACL,GAHa,GADG,gBAAgB,IAAI,IAChB,QAGd,MAAM,IACd;AACD;AAEA,MAAM,QAAQ,OAAe,YAAiB,CAAC;AAE/C,MAAM,aAA2D;EACnC,yBAAA;EACJ,qBAAA;EACL,gBAAA;EACG,mBAAA;EACE,qBAAA;EACK,mBAAA;EAEJ,mBAAA;EACO,mBAAA;EACL,qBAAA;EACA,qBAAA;EACI,yBAAA;EACP,qBAAA;AAC1B;AAEA,SAAgB,iBACf,QACyB;CACzB,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,CAAC,GAAG,UAAU,OAAO,QAAQ,MAAM,GAAG;EAChD,IAAI,OAAO,UAAU,UAAU;GAC9B,IAAI,gBAAgB,CAAC,KAAK;GAC1B;EACD;EAEA,MAAM,YAAY,WAAW,MAAM;EACnC,IAAI,WACH,OAAO,OAAO,KAAK,UAAU,GAAG,KAAK,CAAC;CAGxC;CAEA,OAAO;AACR"}
|
package/dist/utils/index.d.cts
CHANGED
|
@@ -1,2 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
//#region src/utils/index.d.ts
|
|
2
|
+
export declare function getLocalIpAddress(): string | null;
|
|
3
|
+
export declare function environmentCase(name: string): string;
|
|
4
|
+
export declare enum ResourceType {
|
|
5
|
+
ApiGatewayV2 = "sst.aws.ApiGatewayV2",
|
|
6
|
+
Postgres = "sst.aws.Postgres",
|
|
7
|
+
Function = "sst.aws.Function",
|
|
8
|
+
ObjectStorage = "sst.aws.Bucket",
|
|
9
|
+
Vpc = "sst.aws.Vpc",
|
|
10
|
+
Secret = "sst.sst.Secret",
|
|
11
|
+
SSTSecret = "sst:sst:Secret",
|
|
12
|
+
SSTFunction = "sst:sst:Function",
|
|
13
|
+
SSTApiGatewayV2 = "sst:aws:ApiGatewayV2",
|
|
14
|
+
SSTPostgres = "sst:aws:Postgres",
|
|
15
|
+
SSTObjectStorage = "sst:aws:Bucket",
|
|
16
|
+
SnsTopic = "sst:aws:SnsTopic"
|
|
17
|
+
}
|
|
18
|
+
export declare function buildResourceEnv(record: Record<string, Resource | string>): Record<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* SST links a resource under one of two spellings — `sst.aws.Bucket` when it
|
|
21
|
+
* comes from the state file, `sst:aws:Bucket` when it comes from the runtime —
|
|
22
|
+
* and `processors` has always handled both. Only the dotted half was admitted
|
|
23
|
+
* here, so the colon half could not be passed to `buildResourceEnv` without a
|
|
24
|
+
* cast, and `SnsTopic` was missing from `Resource` altogether despite being the
|
|
25
|
+
* only thing that produces `*_ARN`.
|
|
26
|
+
*/
|
|
27
|
+
export type ApiGatewayV2 = {
|
|
28
|
+
type: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;
|
|
29
|
+
url: string;
|
|
30
|
+
};
|
|
31
|
+
export type Postgres = {
|
|
32
|
+
database: string;
|
|
33
|
+
host: string;
|
|
34
|
+
password: string;
|
|
35
|
+
port: number;
|
|
36
|
+
type: ResourceType.Postgres | ResourceType.SSTPostgres;
|
|
37
|
+
username: string;
|
|
38
|
+
};
|
|
39
|
+
export type Function = {
|
|
40
|
+
name: string;
|
|
41
|
+
type: ResourceType.Function | ResourceType.SSTFunction;
|
|
42
|
+
};
|
|
43
|
+
export type Bucket = {
|
|
44
|
+
name: string;
|
|
45
|
+
type: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;
|
|
46
|
+
};
|
|
47
|
+
export type SnsTopic = {
|
|
48
|
+
arn: string;
|
|
49
|
+
type: ResourceType.SnsTopic;
|
|
50
|
+
};
|
|
51
|
+
export type Vpc = {
|
|
52
|
+
bastion: string;
|
|
53
|
+
type: ResourceType.Vpc;
|
|
54
|
+
};
|
|
55
|
+
export type Secret = {
|
|
56
|
+
type: ResourceType.Secret | ResourceType.SSTSecret;
|
|
57
|
+
value: string;
|
|
58
|
+
};
|
|
59
|
+
export type Resource = ApiGatewayV2 | Postgres | Function | Bucket | SnsTopic | Vpc | Secret;
|
|
60
|
+
export type ResourceProcessor<K extends Resource> = (name: string, value: K) => Record<string, string | number>;
|
|
61
|
+
//#endregion
|
|
62
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/utils/index.ts"],"mappings":";wBAGgB;wBAcA,gBAAgB;oBAOpB;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;wBAmDe,iBACf,QAAQ,eAAe,qBACrB;;;;;;;;;YA0BS;EACX,MAAM,aAAa,eAAe,aAAa;EAC/C;;YAGW;EACX;EACA;EACA;EACA;EACA,MAAM,aAAa,WAAW,aAAa;EAC3C;;YAGW;EACX;EACA,MAAM,aAAa,WAAW,aAAa;;YAGhC;EACX;EACA,MAAM,aAAa,gBAAgB,aAAa;;YAGrC;EACX;EACA,MAAM,aAAa;;YAGR;EACX;EACA,MAAM,aAAa;;YAGR;EACX,MAAM,aAAa,SAAS,aAAa;EACzC;;YAGW,WACT,eACA,WACA,WACA,SACA,WACA,MACA;YAES,kBAAkB,UAAU,aACvC,cACA,OAAO,MACH"}
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,2 +1,62 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
//#region src/utils/index.d.ts
|
|
2
|
+
export declare function getLocalIpAddress(): string | null;
|
|
3
|
+
export declare function environmentCase(name: string): string;
|
|
4
|
+
export declare enum ResourceType {
|
|
5
|
+
ApiGatewayV2 = "sst.aws.ApiGatewayV2",
|
|
6
|
+
Postgres = "sst.aws.Postgres",
|
|
7
|
+
Function = "sst.aws.Function",
|
|
8
|
+
ObjectStorage = "sst.aws.Bucket",
|
|
9
|
+
Vpc = "sst.aws.Vpc",
|
|
10
|
+
Secret = "sst.sst.Secret",
|
|
11
|
+
SSTSecret = "sst:sst:Secret",
|
|
12
|
+
SSTFunction = "sst:sst:Function",
|
|
13
|
+
SSTApiGatewayV2 = "sst:aws:ApiGatewayV2",
|
|
14
|
+
SSTPostgres = "sst:aws:Postgres",
|
|
15
|
+
SSTObjectStorage = "sst:aws:Bucket",
|
|
16
|
+
SnsTopic = "sst:aws:SnsTopic"
|
|
17
|
+
}
|
|
18
|
+
export declare function buildResourceEnv(record: Record<string, Resource | string>): Record<string, string>;
|
|
19
|
+
/**
|
|
20
|
+
* SST links a resource under one of two spellings — `sst.aws.Bucket` when it
|
|
21
|
+
* comes from the state file, `sst:aws:Bucket` when it comes from the runtime —
|
|
22
|
+
* and `processors` has always handled both. Only the dotted half was admitted
|
|
23
|
+
* here, so the colon half could not be passed to `buildResourceEnv` without a
|
|
24
|
+
* cast, and `SnsTopic` was missing from `Resource` altogether despite being the
|
|
25
|
+
* only thing that produces `*_ARN`.
|
|
26
|
+
*/
|
|
27
|
+
export type ApiGatewayV2 = {
|
|
28
|
+
type: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;
|
|
29
|
+
url: string;
|
|
30
|
+
};
|
|
31
|
+
export type Postgres = {
|
|
32
|
+
database: string;
|
|
33
|
+
host: string;
|
|
34
|
+
password: string;
|
|
35
|
+
port: number;
|
|
36
|
+
type: ResourceType.Postgres | ResourceType.SSTPostgres;
|
|
37
|
+
username: string;
|
|
38
|
+
};
|
|
39
|
+
export type Function = {
|
|
40
|
+
name: string;
|
|
41
|
+
type: ResourceType.Function | ResourceType.SSTFunction;
|
|
42
|
+
};
|
|
43
|
+
export type Bucket = {
|
|
44
|
+
name: string;
|
|
45
|
+
type: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;
|
|
46
|
+
};
|
|
47
|
+
export type SnsTopic = {
|
|
48
|
+
arn: string;
|
|
49
|
+
type: ResourceType.SnsTopic;
|
|
50
|
+
};
|
|
51
|
+
export type Vpc = {
|
|
52
|
+
bastion: string;
|
|
53
|
+
type: ResourceType.Vpc;
|
|
54
|
+
};
|
|
55
|
+
export type Secret = {
|
|
56
|
+
type: ResourceType.Secret | ResourceType.SSTSecret;
|
|
57
|
+
value: string;
|
|
58
|
+
};
|
|
59
|
+
export type Resource = ApiGatewayV2 | Postgres | Function | Bucket | SnsTopic | Vpc | Secret;
|
|
60
|
+
export type ResourceProcessor<K extends Resource> = (name: string, value: K) => Record<string, string | number>;
|
|
61
|
+
//#endregion
|
|
62
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/utils/index.ts"],"mappings":";wBAGgB;wBAcA,gBAAgB;oBAOpB;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;wBAmDe,iBACf,QAAQ,eAAe,qBACrB;;;;;;;;;YA0BS;EACX,MAAM,aAAa,eAAe,aAAa;EAC/C;;YAGW;EACX;EACA;EACA;EACA;EACA,MAAM,aAAa,WAAW,aAAa;EAC3C;;YAGW;EACX;EACA,MAAM,aAAa,WAAW,aAAa;;YAGhC;EACX;EACA,MAAM,aAAa,gBAAgB,aAAa;;YAGrC;EACX;EACA,MAAM,aAAa;;YAGR;EACX;EACA,MAAM,aAAa;;YAGR;EACX,MAAM,aAAa,SAAS,aAAa;EACzC;;YAGW,WACT,eACA,WACA,WACA,SACA,WACA,MACA;YAES,kBAAkB,UAAU,aACvC,cACA,OAAO,MACH"}
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,3 +1,79 @@
|
|
|
1
|
-
import
|
|
1
|
+
import os from "node:os";
|
|
2
|
+
import snakecase from "lodash.snakecase";
|
|
3
|
+
//#region src/utils/index.ts
|
|
4
|
+
function getLocalIpAddress() {
|
|
5
|
+
const networkInterfaces = os.networkInterfaces();
|
|
6
|
+
for (const interfaceName in networkInterfaces) {
|
|
7
|
+
const addresses = networkInterfaces[interfaceName] || [];
|
|
8
|
+
for (const addressInfo of addresses) if (addressInfo.family === "IPv4" && !addressInfo.internal) return addressInfo.address;
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
function environmentCase(name) {
|
|
13
|
+
return snakecase(name).toUpperCase().replace(/_\d+/g, (r) => {
|
|
14
|
+
return r.replace("_", "");
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
let ResourceType = /* @__PURE__ */ function(ResourceType) {
|
|
18
|
+
ResourceType["ApiGatewayV2"] = "sst.aws.ApiGatewayV2";
|
|
19
|
+
ResourceType["Postgres"] = "sst.aws.Postgres";
|
|
20
|
+
ResourceType["Function"] = "sst.aws.Function";
|
|
21
|
+
ResourceType["ObjectStorage"] = "sst.aws.Bucket";
|
|
22
|
+
ResourceType["Vpc"] = "sst.aws.Vpc";
|
|
23
|
+
ResourceType["Secret"] = "sst.sst.Secret";
|
|
24
|
+
ResourceType["SSTSecret"] = "sst:sst:Secret";
|
|
25
|
+
ResourceType["SSTFunction"] = "sst:sst:Function";
|
|
26
|
+
ResourceType["SSTApiGatewayV2"] = "sst:aws:ApiGatewayV2";
|
|
27
|
+
ResourceType["SSTPostgres"] = "sst:aws:Postgres";
|
|
28
|
+
ResourceType["SSTObjectStorage"] = "sst:aws:Bucket";
|
|
29
|
+
ResourceType["SnsTopic"] = "sst:aws:SnsTopic";
|
|
30
|
+
return ResourceType;
|
|
31
|
+
}({});
|
|
32
|
+
const secret = (name, value) => ({ [environmentCase(name)]: value.value });
|
|
33
|
+
const postgres = (key, value) => {
|
|
34
|
+
const prefix = `${environmentCase(key)}`;
|
|
35
|
+
return {
|
|
36
|
+
[`${prefix}_NAME`]: value.database,
|
|
37
|
+
[`${prefix}_HOST`]: value.host,
|
|
38
|
+
[`${prefix}_PASSWORD`]: value.password,
|
|
39
|
+
[`${prefix}_PORT`]: value.port,
|
|
40
|
+
[`${prefix}_USERNAME`]: value.username
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const bucket = (name, value) => {
|
|
44
|
+
return { [`${`${environmentCase(name)}`}_NAME`]: value.name };
|
|
45
|
+
};
|
|
46
|
+
const topic = (name, value) => {
|
|
47
|
+
return { [`${`${environmentCase(name)}`}_ARN`]: value.arn };
|
|
48
|
+
};
|
|
49
|
+
const noop = (_name, _value) => ({});
|
|
50
|
+
const processors = {
|
|
51
|
+
["sst.aws.ApiGatewayV2"]: noop,
|
|
52
|
+
["sst.aws.Function"]: noop,
|
|
53
|
+
["sst.aws.Vpc"]: noop,
|
|
54
|
+
["sst.sst.Secret"]: secret,
|
|
55
|
+
["sst.aws.Postgres"]: postgres,
|
|
56
|
+
["sst.aws.Bucket"]: bucket,
|
|
57
|
+
["sst:sst:Secret"]: secret,
|
|
58
|
+
["sst:aws:Bucket"]: bucket,
|
|
59
|
+
["sst:sst:Function"]: noop,
|
|
60
|
+
["sst:aws:Postgres"]: postgres,
|
|
61
|
+
["sst:aws:ApiGatewayV2"]: noop,
|
|
62
|
+
["sst:aws:SnsTopic"]: topic
|
|
63
|
+
};
|
|
64
|
+
function buildResourceEnv(record) {
|
|
65
|
+
const env = {};
|
|
66
|
+
for (const [k, value] of Object.entries(record)) {
|
|
67
|
+
if (typeof value === "string") {
|
|
68
|
+
env[environmentCase(k)] = value;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const processor = processors[value.type];
|
|
72
|
+
if (processor) Object.assign(env, processor(k, value));
|
|
73
|
+
}
|
|
74
|
+
return env;
|
|
75
|
+
}
|
|
76
|
+
//#endregion
|
|
77
|
+
export { ResourceType, buildResourceEnv, environmentCase, getLocalIpAddress };
|
|
2
78
|
|
|
3
|
-
|
|
79
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/utils/index.ts"],"sourcesContent":["import os from 'node:os';\nimport snakecase from 'lodash.snakecase';\n\nexport function getLocalIpAddress() {\n\tconst networkInterfaces = os.networkInterfaces();\n\tfor (const interfaceName in networkInterfaces) {\n\t\tconst addresses = networkInterfaces[interfaceName] || [];\n\t\tfor (const addressInfo of addresses) {\n\t\t\t// Look for an IPv4 address that is not internal (loopback)\n\t\t\tif (addressInfo.family === 'IPv4' && !addressInfo.internal) {\n\t\t\t\treturn addressInfo.address;\n\t\t\t}\n\t\t}\n\t}\n\treturn null; // No suitable IP address found\n}\n\nexport function environmentCase(name: string) {\n\treturn snakecase(name)\n\t\t.toUpperCase()\n\t\t.replace(/_\\d+/g, (r) => {\n\t\t\treturn r.replace('_', '');\n\t\t});\n}\nexport enum ResourceType {\n\tApiGatewayV2 = 'sst.aws.ApiGatewayV2',\n\tPostgres = 'sst.aws.Postgres',\n\tFunction = 'sst.aws.Function',\n\tObjectStorage = 'sst.aws.Bucket',\n\tVpc = 'sst.aws.Vpc',\n\tSecret = 'sst.sst.Secret',\n\tSSTSecret = 'sst:sst:Secret',\n\tSSTFunction = 'sst:sst:Function',\n\tSSTApiGatewayV2 = 'sst:aws:ApiGatewayV2',\n\tSSTPostgres = 'sst:aws:Postgres',\n\tSSTObjectStorage = 'sst:aws:Bucket',\n\tSnsTopic = 'sst:aws:SnsTopic',\n}\n\nconst secret = (name: string, value: Secret) => ({\n\t[environmentCase(name)]: value.value,\n});\nconst postgres = (key: string, value: Postgres) => {\n\tconst prefix = `${environmentCase(key)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.database,\n\t\t[`${prefix}_HOST`]: value.host,\n\t\t[`${prefix}_PASSWORD`]: value.password,\n\t\t[`${prefix}_PORT`]: value.port,\n\t\t[`${prefix}_USERNAME`]: value.username,\n\t};\n};\n\nconst bucket = (name: string, value: Bucket) => {\n\tconst prefix = `${environmentCase(name)}`;\n\treturn {\n\t\t[`${prefix}_NAME`]: value.name,\n\t};\n};\n\nconst topic = (name: string, value: SnsTopic) => {\n\tconst prefix = `${environmentCase(name)}`;\n\tconst key = `${prefix}_ARN`;\n\n\treturn {\n\t\t[key]: value.arn,\n\t};\n};\n\nconst noop = (_name: string, _value: any) => ({});\n\nconst processors: Record<ResourceType, ResourceProcessor<any>> = {\n\t[ResourceType.ApiGatewayV2]: noop,\n\t[ResourceType.Function]: noop,\n\t[ResourceType.Vpc]: noop,\n\t[ResourceType.Secret]: secret,\n\t[ResourceType.Postgres]: postgres,\n\t[ResourceType.ObjectStorage]: bucket,\n\n\t[ResourceType.SSTSecret]: secret,\n\t[ResourceType.SSTObjectStorage]: bucket,\n\t[ResourceType.SSTFunction]: noop,\n\t[ResourceType.SSTPostgres]: postgres,\n\t[ResourceType.SSTApiGatewayV2]: noop,\n\t[ResourceType.SnsTopic]: topic,\n};\n\nexport function buildResourceEnv(\n\trecord: Record<string, Resource | string>,\n): Record<string, string> {\n\tconst env: Record<string, string> = {};\n\tfor (const [k, value] of Object.entries(record)) {\n\t\tif (typeof value === 'string') {\n\t\t\tenv[environmentCase(k)] = value;\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst processor = processors[value.type];\n\t\tif (processor) {\n\t\t\tObject.assign(env, processor(k, value));\n\t\t} else {\n\t\t}\n\t}\n\n\treturn env;\n}\n\n/**\n * SST links a resource under one of two spellings — `sst.aws.Bucket` when it\n * comes from the state file, `sst:aws:Bucket` when it comes from the runtime —\n * and `processors` has always handled both. Only the dotted half was admitted\n * here, so the colon half could not be passed to `buildResourceEnv` without a\n * cast, and `SnsTopic` was missing from `Resource` altogether despite being the\n * only thing that produces `*_ARN`.\n */\nexport type ApiGatewayV2 = {\n\ttype: ResourceType.ApiGatewayV2 | ResourceType.SSTApiGatewayV2;\n\turl: string;\n};\n\nexport type Postgres = {\n\tdatabase: string;\n\thost: string;\n\tpassword: string;\n\tport: number;\n\ttype: ResourceType.Postgres | ResourceType.SSTPostgres;\n\tusername: string;\n};\n\nexport type Function = {\n\tname: string;\n\ttype: ResourceType.Function | ResourceType.SSTFunction;\n};\n\nexport type Bucket = {\n\tname: string;\n\ttype: ResourceType.ObjectStorage | ResourceType.SSTObjectStorage;\n};\n\nexport type SnsTopic = {\n\tarn: string;\n\ttype: ResourceType.SnsTopic;\n};\n\nexport type Vpc = {\n\tbastion: string;\n\ttype: ResourceType.Vpc;\n};\n\nexport type Secret = {\n\ttype: ResourceType.Secret | ResourceType.SSTSecret;\n\tvalue: string;\n};\n\nexport type Resource =\n\t| ApiGatewayV2\n\t| Postgres\n\t| Function\n\t| Bucket\n\t| SnsTopic\n\t| Vpc\n\t| Secret;\n\nexport type ResourceProcessor<K extends Resource> = (\n\tname: string,\n\tvalue: K,\n) => Record<string, string | number>;\n"],"mappings":";;;AAGA,SAAgB,oBAAoB;CACnC,MAAM,oBAAoB,GAAG,kBAAkB;CAC/C,KAAK,MAAM,iBAAiB,mBAAmB;EAC9C,MAAM,YAAY,kBAAkB,kBAAkB,CAAC;EACvD,KAAK,MAAM,eAAe,WAEzB,IAAI,YAAY,WAAW,UAAU,CAAC,YAAY,UACjD,OAAO,YAAY;CAGtB;CACA,OAAO;AACR;AAEA,SAAgB,gBAAgB,MAAc;CAC7C,OAAO,UAAU,IAAI,CAAC,CACpB,YAAY,CAAC,CACb,QAAQ,UAAU,MAAM;EACxB,OAAO,EAAE,QAAQ,KAAK,EAAE;CACzB,CAAC;AACH;AACA,IAAY,eAAL,yBAAA,cAAA;CACN,aAAA,kBAAA;CACA,aAAA,cAAA;CACA,aAAA,cAAA;CACA,aAAA,mBAAA;CACA,aAAA,SAAA;CACA,aAAA,YAAA;CACA,aAAA,eAAA;CACA,aAAA,iBAAA;CACA,aAAA,qBAAA;CACA,aAAA,iBAAA;CACA,aAAA,sBAAA;CACA,aAAA,cAAA;;AACD,EAAA,CAAA,CAAA;AAEA,MAAM,UAAU,MAAc,WAAmB,GAC/C,gBAAgB,IAAI,IAAI,MAAM,MAChC;AACA,MAAM,YAAY,KAAa,UAAoB;CAClD,MAAM,SAAS,GAAG,gBAAgB,GAAG;CACrC,OAAO;GACL,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,aAAa,MAAM;GAC7B,GAAG,OAAO,SAAS,MAAM;GACzB,GAAG,OAAO,aAAa,MAAM;CAC/B;AACD;AAEA,MAAM,UAAU,MAAc,UAAkB;CAE/C,OAAO,GACL,GAAG,GAFa,gBAAgB,IAAI,IAE1B,SAAS,MAAM,KAC3B;AACD;AAEA,MAAM,SAAS,MAAc,UAAoB;CAIhD,OAAO,GACL,GAHa,GADG,gBAAgB,IAAI,IAChB,QAGd,MAAM,IACd;AACD;AAEA,MAAM,QAAQ,OAAe,YAAiB,CAAC;AAE/C,MAAM,aAA2D;EACnC,yBAAA;EACJ,qBAAA;EACL,gBAAA;EACG,mBAAA;EACE,qBAAA;EACK,mBAAA;EAEJ,mBAAA;EACO,mBAAA;EACL,qBAAA;EACA,qBAAA;EACI,yBAAA;EACP,qBAAA;AAC1B;AAEA,SAAgB,iBACf,QACyB;CACzB,MAAM,MAA8B,CAAC;CACrC,KAAK,MAAM,CAAC,GAAG,UAAU,OAAO,QAAQ,MAAM,GAAG;EAChD,IAAI,OAAO,UAAU,UAAU;GAC9B,IAAI,gBAAgB,CAAC,KAAK;GAC1B;EACD;EAEA,MAAM,YAAY,WAAW,MAAM;EACnC,IAAI,WACH,OAAO,OAAO,KAAK,UAAU,GAAG,KAAK,CAAC;CAGxC;CAEA,OAAO;AACR"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geekmidas/cloud",
|
|
3
|
-
"version": "10.0.0-alpha.
|
|
3
|
+
"version": "10.0.0-alpha.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"lodash.snakecase": "~4.1.1",
|
|
44
|
-
"@geekmidas/envkit": "10.0.0-alpha.
|
|
45
|
-
"@geekmidas/manifest": "10.0.0-alpha.
|
|
44
|
+
"@geekmidas/envkit": "10.0.0-alpha.13",
|
|
45
|
+
"@geekmidas/manifest": "10.0.0-alpha.13"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@types/lodash.get": "~4.4.9",
|
|
@@ -58,10 +58,10 @@
|
|
|
58
58
|
"@pulumi/aws": ">=7.47.0",
|
|
59
59
|
"@pulumi/random": ">=4.21.2",
|
|
60
60
|
"@pulumi/pulumi": ">=3.263.0",
|
|
61
|
-
"@geekmidas/manifest": "^10.0.0-alpha.
|
|
62
|
-
"@geekmidas/storage": "^10.0.0-alpha.
|
|
63
|
-
"@geekmidas/events": "^10.0.0-alpha.
|
|
64
|
-
"@geekmidas/db": "^10.0.0-alpha.
|
|
61
|
+
"@geekmidas/manifest": "^10.0.0-alpha.13",
|
|
62
|
+
"@geekmidas/storage": "^10.0.0-alpha.13",
|
|
63
|
+
"@geekmidas/events": "^10.0.0-alpha.13",
|
|
64
|
+
"@geekmidas/db": "^10.0.0-alpha.13"
|
|
65
65
|
},
|
|
66
66
|
"peerDependenciesMeta": {
|
|
67
67
|
"@geekmidas/manifest": {
|
|
@@ -241,6 +241,13 @@ export interface ApplicationArgs {
|
|
|
241
241
|
environmentId: Input<string>;
|
|
242
242
|
}
|
|
243
243
|
|
|
244
|
+
/**
|
|
245
|
+
* The provider itself, for tests: its create/read/update/delete are what talk
|
|
246
|
+
* to Dokploy, and a dynamic resource gives no other way to reach them without a
|
|
247
|
+
* Pulumi engine.
|
|
248
|
+
*/
|
|
249
|
+
export { provider as applicationProvider };
|
|
250
|
+
|
|
244
251
|
export class Application extends dynamic.Resource {
|
|
245
252
|
declare readonly applicationId: Output<string>;
|
|
246
253
|
declare readonly appName: Output<string>;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { HttpResponse, http } from 'msw';
|
|
2
|
+
import { setupServer } from 'msw/node';
|
|
3
|
+
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest';
|
|
4
|
+
import { applicationProvider as provider } from '../Application';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The provider against Dokploy's tRPC API, served by MSW: what each lifecycle
|
|
8
|
+
* call sends, and how it reads what comes back — including "already gone".
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const endpoint = 'https://dokploy.test';
|
|
12
|
+
const inputs = {
|
|
13
|
+
endpoint,
|
|
14
|
+
apiToken: 'token-1',
|
|
15
|
+
name: 'My API',
|
|
16
|
+
projectId: 'proj-1',
|
|
17
|
+
environmentId: 'env-1',
|
|
18
|
+
};
|
|
19
|
+
/** What state holds once the application exists: every later call gets it. */
|
|
20
|
+
const outs = { ...inputs, applicationId: 'app-1', appName: 'my-api' };
|
|
21
|
+
|
|
22
|
+
const server = setupServer();
|
|
23
|
+
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
|
|
24
|
+
afterEach(() => server.resetHandlers());
|
|
25
|
+
afterAll(() => server.close());
|
|
26
|
+
|
|
27
|
+
/** Records the one request a call makes, and answers it. */
|
|
28
|
+
function answer(
|
|
29
|
+
method: 'get' | 'post',
|
|
30
|
+
path: string,
|
|
31
|
+
respond: () => Response,
|
|
32
|
+
): { request: () => Promise<{ body: unknown; headers: Headers }> } {
|
|
33
|
+
let seen: Request | undefined;
|
|
34
|
+
server.use(
|
|
35
|
+
http[method](`${endpoint}/api/trpc/${path}`, ({ request }) => {
|
|
36
|
+
seen = request.clone();
|
|
37
|
+
return respond();
|
|
38
|
+
}),
|
|
39
|
+
);
|
|
40
|
+
return {
|
|
41
|
+
request: async () => ({
|
|
42
|
+
body: seen && method === 'post' ? await seen.json() : undefined,
|
|
43
|
+
headers: seen!.headers,
|
|
44
|
+
}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe('Dokploy application provider', () => {
|
|
49
|
+
it('creates the application under the name Dokploy derives', async () => {
|
|
50
|
+
const call = answer('post', 'application.create', () =>
|
|
51
|
+
HttpResponse.json({ applicationId: 'app-1', appName: 'my-api' }),
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
const result = await provider.create!(inputs);
|
|
55
|
+
|
|
56
|
+
const { body, headers } = await call.request();
|
|
57
|
+
expect(headers.get('x-api-key')).toBe('token-1');
|
|
58
|
+
expect(body).toEqual({
|
|
59
|
+
name: 'My API',
|
|
60
|
+
projectId: 'proj-1',
|
|
61
|
+
environmentId: 'env-1',
|
|
62
|
+
appName: 'my-api',
|
|
63
|
+
});
|
|
64
|
+
// Pulumi's id is Dokploy's, so state and the Dokploy UI name the same thing.
|
|
65
|
+
expect(result).toEqual({
|
|
66
|
+
id: 'app-1',
|
|
67
|
+
outs: { ...inputs, applicationId: 'app-1', appName: 'my-api' },
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('refreshes from the server, and reports one deleted by hand as gone', async () => {
|
|
72
|
+
answer('get', 'application.one', () =>
|
|
73
|
+
HttpResponse.json({ applicationId: 'app-1', appName: 'my-api-2' }),
|
|
74
|
+
);
|
|
75
|
+
expect(await provider.read!('app-1', outs)).toEqual({
|
|
76
|
+
id: 'app-1',
|
|
77
|
+
props: { ...inputs, applicationId: 'app-1', appName: 'my-api-2' },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
server.resetHandlers();
|
|
81
|
+
answer('get', 'application.one', () => HttpResponse.json(null));
|
|
82
|
+
expect(await provider.read!('app-1', outs)).toEqual({ id: undefined });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('diffs through the same rule preview uses', async () => {
|
|
86
|
+
expect(
|
|
87
|
+
await provider.diff!('app-1', outs, { ...inputs, name: 'Gateway' }),
|
|
88
|
+
).toMatchObject({ changes: true, replaces: ['name'] });
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('renames in place, and reports the app name that follows', async () => {
|
|
92
|
+
const call = answer('post', 'application.update', () =>
|
|
93
|
+
HttpResponse.json({}),
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const result = await provider.update!('app-1', outs, {
|
|
97
|
+
...inputs,
|
|
98
|
+
name: 'My Api',
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect((await call.request()).body).toEqual({
|
|
102
|
+
applicationId: 'app-1',
|
|
103
|
+
name: 'My Api',
|
|
104
|
+
});
|
|
105
|
+
expect(result).toEqual({
|
|
106
|
+
outs: {
|
|
107
|
+
...inputs,
|
|
108
|
+
name: 'My Api',
|
|
109
|
+
applicationId: 'app-1',
|
|
110
|
+
appName: 'my-api',
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('treats an application that is already gone as deleted', async () => {
|
|
116
|
+
const call = answer('post', 'application.remove', () =>
|
|
117
|
+
HttpResponse.text('no such application', {
|
|
118
|
+
status: 404,
|
|
119
|
+
statusText: 'Not Found',
|
|
120
|
+
}),
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
await expect(provider.delete!('app-1', outs)).resolves.toBeUndefined();
|
|
124
|
+
expect((await call.request()).body).toEqual({ applicationId: 'app-1' });
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('fails a delete, or any call, that Dokploy refused for another reason', async () => {
|
|
128
|
+
answer('post', 'application.remove', () =>
|
|
129
|
+
HttpResponse.text('database is locked', {
|
|
130
|
+
status: 500,
|
|
131
|
+
statusText: 'Internal Server Error',
|
|
132
|
+
}),
|
|
133
|
+
);
|
|
134
|
+
await expect(provider.delete!('app-1', outs)).rejects.toThrow(
|
|
135
|
+
'Dokploy application.remove failed: 500 Internal Server Error — database is locked',
|
|
136
|
+
);
|
|
137
|
+
|
|
138
|
+
server.resetHandlers();
|
|
139
|
+
answer(
|
|
140
|
+
'post',
|
|
141
|
+
'application.create',
|
|
142
|
+
() => new HttpResponse(null, { status: 401, statusText: 'Unauthorized' }),
|
|
143
|
+
);
|
|
144
|
+
await expect(provider.create!(inputs)).rejects.toThrow(
|
|
145
|
+
'Dokploy application.create failed: 401 Unauthorized',
|
|
146
|
+
);
|
|
147
|
+
});
|
|
148
|
+
});
|