Skip to content

Commit 07a466c

Browse files
committed
wip
1 parent d521f00 commit 07a466c

File tree

5 files changed

+204
-72
lines changed

5 files changed

+204
-72
lines changed

alchemy/src/docker/api/api.ts

Lines changed: 127 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,34 +16,47 @@ type InitParam<Init> = RequiredKeysOf<Init> extends never
1616
? [(Init & { [key: string]: unknown })?]
1717
: [Init & { [key: string]: unknown }];
1818

19-
type PickQueryParam<
19+
type ParamsOf<
2020
P extends keyof paths,
2121
M extends HttpMethod,
22-
// @ts-ignore: dyncamically inferred on usage
23-
K extends keyof NonNullable<paths[P][M]["parameters"]["query"]>,
24-
// @ts-ignore: dyncamically inferred on usage
25-
> = Pick<NonNullable<paths[P][M]["parameters"]["query"]>, K>;
22+
W extends "query" | "path" | "body",
23+
> = W extends "query"
24+
? // @ts-expect-error: dyncamically inferred on usage
25+
NonNullable<paths[P][M]["parameters"]["query"]>
26+
: W extends "path"
27+
? // @ts-expect-error: dyncamically inferred on usage
28+
NonNullable<paths[P][M]["parameters"]["path"]>
29+
: // @ts-expect-error: dyncamically inferred on usage
30+
NonNullable<paths[P][M]["requestBody"]["content"]["application/json"]>;
2631

27-
type PickPathParam<
28-
P extends keyof paths,
29-
M extends HttpMethod,
30-
// @ts-ignore: dyncamically inferred on usage
31-
K extends keyof NonNullable<paths[P][M]["parameters"]["path"]>,
32-
// @ts-ignore: dyncamically inferred on usage
33-
> = Pick<NonNullable<paths[P][M]["parameters"]["path"]>, K>;
32+
// type PickQueryParam<
33+
// P extends keyof paths,
34+
// M extends HttpMethod,
35+
// // @ts-ignore: dyncamically inferred on usage
36+
// K extends keyof NonNullable<paths[P][M]["parameters"]["query"]>,
37+
// // @ts-ignore: dyncamically inferred on usage
38+
// > = Pick<NonNullable<paths[P][M]["parameters"]["query"]>, K>;
3439

35-
type PickBodyParam<
36-
P extends keyof paths,
37-
M extends HttpMethod,
38-
K extends keyof NonNullable<
39-
// @ts-ignore: dyncamically inferred on usage
40-
paths[P][M]["requestBody"]["content"]["application/json"]
41-
>,
42-
> = Pick<
43-
// @ts-ignore: dyncamically inferred on usage
44-
NonNullable<paths[P][M]["requestBody"]["content"]["application/json"]>,
45-
K
46-
>;
40+
// type PickPathParam<
41+
// P extends keyof paths,
42+
// M extends HttpMethod,
43+
// // @ts-ignore: dyncamically inferred on usage
44+
// K extends keyof NonNullable<paths[P][M]["parameters"]["path"]>,
45+
// // @ts-ignore: dyncamically inferred on usage
46+
// > = Pick<NonNullable<paths[P][M]["parameters"]["path"]>, K>;
47+
48+
// type PickBodyParam<
49+
// P extends keyof paths,
50+
// M extends HttpMethod,
51+
// K extends keyof NonNullable<
52+
// // @ts-ignore: dyncamically inferred on usage
53+
// paths[P][M]["requestBody"]["content"]["application/json"]
54+
// >,
55+
// > = Pick<
56+
// // @ts-ignore: dyncamically inferred on usage
57+
// NonNullable<paths[P][M]["requestBody"]["content"]["application/json"]>,
58+
// K
59+
// >;
4760

4861
export function createDockerApi() {
4962
const client = createClient<paths>({
@@ -118,10 +131,8 @@ export function createDockerApi() {
118131

119132
return {
120133
containers: {},
121-
network: (nameOrId: PickPathParam<"/networks/{id}", "get", "id">) => ({
122-
inspect: async (
123-
params?: PickQueryParam<"/networks/{id}", "get", "verbose" | "scope">,
124-
) => {
134+
network: (nameOrId: ParamsOf<"/networks/{id}", "get", "path">) => ({
135+
inspect: async (params?: InspectNetworkParams) => {
125136
const network = await GET("/networks/{id}", {
126137
params: {
127138
path: { id: nameOrId.id },
@@ -140,13 +151,98 @@ export function createDockerApi() {
140151
}),
141152
}),
142153
networks: {
143-
list: () => GET("/networks"),
144-
create: (body: PickBodyParam<"/networks/create", "post", "Name">) =>
145-
POST("/networks/create", { body: { Name: body.Name } }),
154+
// TODO(manuel): Implement filters query param
155+
list: (params?: ListNetworksParams) =>
156+
GET("/networks", {
157+
params: { query: { filters: params?.filters } },
158+
}),
159+
create: (params: NetworkCreateParams) =>
160+
POST("/networks/create", {
161+
body: {
162+
Name: params.Name,
163+
Driver: params.Driver,
164+
Labels: params.Labels,
165+
EnableIPv4: params.EnableIPv4,
166+
EnableIPv6: params.EnableIPv6,
167+
},
168+
}),
169+
// TODO(manuel): Implement filters query param
170+
prune: () => POST("/networks/prune"),
146171
},
147172
};
148173
}
149174

175+
interface InspectNetworkParams
176+
extends ParamsOf<"/networks/{id}", "get", "query"> {}
177+
178+
interface ListNetworksParams extends ParamsOf<"/networks", "get", "query"> {}
179+
180+
interface NetworkCreateParams
181+
extends ParamsOf<"/networks/create", "post", "body"> {}
182+
183+
const client = createDockerApi();
184+
185+
// const network = await client.network({ id: "host" }).inspect({ verbose: true });
186+
// const networks = await client.networks.list();
187+
const network = await client.networks.create({
188+
Name: "test",
189+
Driver: "bridge",
190+
Labels: {},
191+
EnableIPv4: true,
192+
EnableIPv6: true,
193+
});
194+
console.log(network);
195+
196+
// const createPicker =
197+
// <
198+
// P extends keyof paths,
199+
// M extends HttpMethod,
200+
// W extends "query" | "path" | "body",
201+
// K extends (W extends "query"
202+
// ? // @ts-expect-error: dyncamically inferred on usage
203+
// keyof NonNullable<paths[P][M]["parameters"]["query"]>
204+
// : W extends "path"
205+
// ? // @ts-expect-error: dyncamically inferred on usage
206+
// keyof NonNullable<paths[P][M]["parameters"]["path"]>
207+
// : keyof NonNullable<
208+
// // @ts-ignore: dyncamically inferred on usage
209+
// paths[P][M]["requestBody"]["content"]["application/json"]
210+
// >)[],
211+
// >(
212+
// path: P,
213+
// method: M,
214+
// type: W,
215+
// keys: K,
216+
// ) =>
217+
// (
218+
// params: W extends "query"
219+
// ? // @ts-expect-error: dyncamically inferred on usage
220+
// NonNullable<paths[P][M]["parameters"]["query"]>
221+
// : W extends "path"
222+
// ? // @ts-expect-error: dyncamically inferred on usage
223+
// NonNullable<paths[P][M]["parameters"]["path"]>
224+
// : NonNullable<
225+
// // @ts-ignore: dyncamically inferred on usage
226+
// paths[P][M]["requestBody"]["content"]["application/json"]
227+
// >,
228+
// // @ts-ignore: dyncamically inferred on usage
229+
// ): Pick<typeof params, K[number]> =>
230+
// // @ts-ignore: dyncamically inferred on usage
231+
// Object.fromEntries(
232+
// Object.entries(params).filter(([key]) => keys.includes(key as K[number])),
233+
// );
234+
235+
// type ListNetworksParams = PickQueryParam<"/networks", "get", "filters">;
236+
237+
// const pickNetworkCreateParams = createPicker("/networks/create", "post", [
238+
// "Name",
239+
// "Driver",
240+
// "Labels",
241+
// "EnableIPv4",
242+
// "EnableIPv6",
243+
// ] as const);
244+
// type NetworkCreateParams = ReturnType<typeof pickNetworkCreateParams>;
245+
150246
// class DockerHttpClient {
151247
// constructor(private props: DockerHttpClientProps) {}
152248

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { describe, expect } from "bun:test";
2+
import { alchemy } from "../../../src/alchemy.js";
3+
import { destroy } from "../../destroy.js";
4+
import { Network } from "./network.js";
5+
6+
import "../../test/bun.js";
7+
8+
const test = alchemy.test(import.meta);
9+
10+
describe("Network", () => {
11+
test("should create a test network with default driver", async (scope) => {
12+
try {
13+
const networkName = `alchemy-test-network-${Date.now()}`;
14+
const network = await Network("test-network", {
15+
Name: networkName,
16+
});
17+
18+
expect(network.Name).toBe(networkName);
19+
expect(network.Driver).toBe("bridge"); // default value
20+
} finally {
21+
await destroy(scope);
22+
}
23+
});
24+
25+
test("should update a network", async (scope) => {
26+
try {
27+
const networkName = `alchemy-test-network-${Date.now()}`;
28+
const network = await Network("test-network", {
29+
Name: networkName,
30+
});
31+
32+
expect(network.Name).toBe(networkName);
33+
} finally {
34+
await destroy(scope);
35+
}
36+
});
37+
38+
test("should use specified driver", async (scope) => {
39+
try {
40+
const networkName = `alchemy-test-network-${Date.now()}`;
41+
const network = await Network("test-network", {
42+
Name: networkName,
43+
Driver: "overlay",
44+
});
45+
46+
expect(network.Driver).toBe("overlay");
47+
} finally {
48+
await destroy(scope);
49+
}
50+
});
51+
52+
test("should use specified labels", async (scope) => {
53+
try {
54+
const networkName = `alchemy-test-network-${Date.now()}`;
55+
const network = await Network("test-network", {
56+
Name: networkName,
57+
Labels: { "alchemy.test": "true" },
58+
});
59+
60+
expect(network.Labels).toMatchObject(
61+
expect.objectContaining({
62+
"alchemy.test": "true",
63+
}),
64+
);
65+
} finally {
66+
await destroy(scope);
67+
}
68+
});
69+
});

alchemy/src/docker/api/network.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
import type { Context } from "../../context.ts";
22
import { Resource } from "../../resource.ts";
3+
import type { MarkAsRequired } from "../type-utils.ts";
34
import { createDockerApi } from "./api.ts";
45
import type { components } from "./types.ts";
56

6-
type Network = Required<Pick<components["schemas"]["Network"], "Name">> &
7-
Omit<components["schemas"]["Network"], "Name"> &
7+
type Network = MarkAsRequired<components["schemas"]["Network"], "Name"> &
88
Resource<"docker::with-api::Network">;
99

10-
export type NetworkProps = Required<Pick<Network, "Name">>;
10+
export type NetworkProps = Required<Pick<Network, "Name">> &
11+
Pick<Network, "Driver" | "Labels">;
1112

1213
export const Network = Resource(
1314
"docker::api::Network",
@@ -31,13 +32,14 @@ export const Network = Resource(
3132

3233
if (this.phase === "update") {
3334
if (this.output.Name !== props.Name) {
34-
console.log(`replacing "${this.output.Name}" with "${props.Name}"`);
3535
this.replace();
3636
}
3737
}
3838

3939
const { Id: id } = await docker.networks.create({
4040
Name: props.Name,
41+
Driver: props.Driver,
42+
Labels: props.Labels,
4143
});
4244
const network = await docker.network({ id }).inspect();
4345
return this(network);

alchemy/src/docker/type-utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export type MarkAsRequired<T, K extends keyof T> = Required<Pick<T, K>> &
2+
Omit<T, K>;

alchemy/test/docker/api/network.test.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.

0 commit comments

Comments
 (0)