Skip to content

Commit a13e689

Browse files
authored
Merge pull request #15 from phasehq/feat--implicit-init
feat: implicit init
2 parents 1049909 + fa50878 commit a13e689

File tree

4 files changed

+38
-107
lines changed

4 files changed

+38
-107
lines changed

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@phase.dev/phase-node",
3-
"version": "3.1.1",
4-
"description": "Node.js Server SDK for Phase",
3+
"version": "3.2.0",
4+
"description": "Node.js SDK for Phase",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",
77
"repository": "https://github.com/phasehq/node-sdk",

src/index.ts

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,14 @@ export type {
3636

3737

3838
export default class Phase {
39-
token: string;
40-
host: string;
41-
tokenType: string | null = null;
42-
version: string | null = null;
43-
bearerToken: string | null = null;
44-
keypair: PhaseKeyPair = {} as PhaseKeyPair;
45-
apps: App[] = [];
39+
private token: string;
40+
private host: string;
41+
private _tokenType: string | null = null;
42+
private _version: string | null = null;
43+
private _bearerToken: string | null = null;
44+
private _keypair: PhaseKeyPair = {} as PhaseKeyPair;
45+
private apps: App[] = [];
46+
_isInitialized: boolean = false
4647

4748
constructor(token: string, host?: string) {
4849
this.host = host || DEFAULT_HOST;
@@ -64,7 +65,7 @@ export default class Phase {
6465
const data: SessionResponse = response.data;
6566

6667
// Set the keypair for the class instance
67-
this.keypair = {
68+
this._keypair = {
6869
publicKey: this.token.split(":")[3],
6970
privateKey: await reconstructPrivateKey(
7071
data.wrapped_key_share,
@@ -80,7 +81,7 @@ export default class Phase {
8081
const { publicKey, privateKey, salt } = await unwrapEnvKeys(
8182
envData.wrapped_seed,
8283
envData.wrapped_salt,
83-
this.keypair
84+
this._keypair
8485
);
8586

8687
const { id, name } = envData.environment;
@@ -106,6 +107,7 @@ export default class Phase {
106107

107108
const apps: App[] = await Promise.all(appPromises);
108109
this.apps = apps;
110+
this._isInitialized = true
109111
} catch (error) {
110112
if (axios.isAxiosError(error) && error.response) {
111113
throw `Error: ${error.response.status}: ${
@@ -139,18 +141,18 @@ export default class Phase {
139141
const [tokenType, version, bearerToken] = token.split(":");
140142

141143
// Assign the parsed values to the instance properties
142-
this.tokenType = tokenType.includes("user")
144+
this._tokenType = tokenType.includes("user")
143145
? "User"
144146
: version === "v1"
145147
? "Service"
146148
: "ServiceAccount";
147-
this.version = version;
148-
this.bearerToken = bearerToken;
149+
this._version = version;
150+
this._bearerToken = bearerToken;
149151
}
150152

151153
private getAuthHeaders() {
152154
return {
153-
Authorization: `Bearer ${this.tokenType} ${this.bearerToken}`,
155+
Authorization: `Bearer ${this._tokenType} ${this._bearerToken}`,
154156
Accept: "application/json",
155157
"X-Use-Camel-Case": true,
156158
"User-agent": `phase-node-sdk/${LIB_VERSION}`,
@@ -159,6 +161,9 @@ export default class Phase {
159161

160162
async get(options: GetSecretOptions): Promise<Secret[]> {
161163
return new Promise<Secret[]>(async (resolve, reject) => {
164+
165+
if (!this._isInitialized) await this.init()
166+
162167
const cache = new Map<string, string>();
163168

164169
const app = this.apps.find((app) => app.id === options.appId);
@@ -170,7 +175,7 @@ export default class Phase {
170175
(e) => e.name.toLowerCase() === options.envName.toLowerCase()
171176
);
172177
if (!env) {
173-
return reject(`Invalid environment name: ${options.envName}`);
178+
return reject(`Invalid environment name: '${options.envName}'`);
174179
}
175180

176181
try {
@@ -313,14 +318,16 @@ export default class Phase {
313318
return new Promise<void>(async (resolve, reject) => {
314319
const { appId, envName } = options;
315320

321+
if (!this._isInitialized) await this.init()
322+
316323
const app = this.apps.find((app) => app.id === appId);
317324
if (!app) {
318325
throw "Invalid app id";
319326
}
320327

321-
const env = app?.environments.find((env) => env.name === envName);
328+
const env = app?.environments.find((env) => env.name.toLowerCase() === envName.toLowerCase());
322329
if (!env) {
323-
throw "Invalid environment name";
330+
throw `Invalid environment name: '${envName}'`;
324331
}
325332

326333
try {
@@ -379,14 +386,16 @@ export default class Phase {
379386
return new Promise<void>(async (resolve, reject) => {
380387
const { appId, envName } = options;
381388

389+
if (!this._isInitialized) await this.init()
390+
382391
const app = this.apps.find((app) => app.id === appId);
383392
if (!app) {
384393
throw "Invalid app id";
385394
}
386395

387-
const env = app?.environments.find((env) => env.name === envName);
396+
const env = app?.environments.find((env) => env.name.toLowerCase() === envName.toLowerCase());
388397
if (!env) {
389-
throw "Invalid environment name";
398+
throw `Invalid environment name: '${envName}'`;
390399
}
391400

392401
try {
@@ -440,14 +449,16 @@ export default class Phase {
440449
try {
441450
const { appId, envName } = options;
442451

452+
if (!this._isInitialized) await this.init()
453+
443454
const app = this.apps.find((app) => app.id === appId);
444455
if (!app) {
445456
throw "Invalid app id";
446457
}
447458

448-
const env = app?.environments.find((env) => env.name === envName);
459+
const env = app?.environments.find((env) => env.name.toLowerCase() === envName.toLowerCase());
449460
if (!env) {
450-
throw "Invalid environment name";
461+
throw `Invalid environment name: '${envName}'`;
451462
}
452463

453464
const requestHeaders = { environment: env.id };

src/version.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export const LIB_VERSION = "3.1.1";
1+
export const LIB_VERSION = "3.2.0";

tests/sdk/init.test.ts

Lines changed: 4 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -243,50 +243,10 @@ describe("Phase SDK - init() with valid user token", () => {
243243
const phase = new Phase(validUserToken, mockHost);
244244
await phase.init();
245245

246-
expect(phase.token).toBe(validUserToken);
247-
expect(phase.host).toBe(mockHost);
248-
expect(phase.tokenType).toBe("User"); // Assuming the token is a user token
249-
expect(phase.version).toBe("v1");
250-
expect(phase.bearerToken).toBe(validUserToken.split(":")[2]); // Extracted from the token
251-
252-
expect(phase.keypair).toHaveProperty("publicKey");
253-
expect(phase.keypair).toHaveProperty("privateKey");
254-
expect(phase.keypair.privateKey).toBeDefined();
255-
256-
expect(phase.apps.length).toBe(mockResponse.apps.length);
257-
for (let i = 0; i < phase.apps.length; i++) {
258-
expect(phase.apps[i].id).toBe(mockResponse.apps[i].id);
259-
expect(phase.apps[i].name).toBe(mockResponse.apps[i].name);
260-
261-
expect(phase.apps[i].environments.length).toBe(
262-
mockResponse.apps[i].environment_keys.length
263-
);
264-
265-
for (let j = 0; j < phase.apps[i].environments.length; j++) {
266-
expect(phase.apps[i].environments[j].keypair.publicKey).toBeDefined();
267-
expect(phase.apps[i].environments[j].keypair.privateKey).toBeDefined();
268-
expect(phase.apps[i].environments[j].salt).toBeDefined();
269-
}
270-
}
246+
expect(phase._isInitialized).toBe(true);
271247
});
272248

273-
it("should reconstruct the private key and decrypt environment keys correctly with a user token", async () => {
274-
const phase = new Phase(validUserToken, mockHost);
275-
await phase.init();
276-
277-
// Ensure reconstructPrivateKey was called with real data
278-
expect(phase.keypair.privateKey).toBeDefined();
279-
expect(phase.keypair.publicKey).toBe(validUserToken.split(":")[3]);
280-
281-
// Verify each environment key was unwrapped correctly
282-
for (const app of phase.apps) {
283-
for (const env of app.environments) {
284-
expect(env.keypair.publicKey).toBeDefined();
285-
expect(env.keypair.privateKey).toBeDefined();
286-
expect(env.salt).toBeDefined();
287-
}
288-
}
289-
});
249+
290250
});
291251

292252
describe("Phase SDK - init() with valid service token", () => {
@@ -386,48 +346,8 @@ describe("Phase SDK - init() with valid service token", () => {
386346
const phase = new Phase(validServiceToken, mockHost);
387347
await phase.init();
388348

389-
expect(phase.token).toBe(validServiceToken);
390-
expect(phase.host).toBe(mockHost);
391-
expect(phase.tokenType).toBe("ServiceAccount");
392-
expect(phase.version).toBe("v2");
393-
expect(phase.bearerToken).toBe(validServiceToken.split(":")[2]); // Extracted from the token
394-
395-
expect(phase.keypair).toHaveProperty("publicKey");
396-
expect(phase.keypair).toHaveProperty("privateKey");
397-
expect(phase.keypair.privateKey).toBeDefined();
398-
399-
expect(phase.apps.length).toBe(mockResponse.apps.length);
400-
for (let i = 0; i < phase.apps.length; i++) {
401-
expect(phase.apps[i].id).toBe(mockResponse.apps[i].id);
402-
expect(phase.apps[i].name).toBe(mockResponse.apps[i].name);
403-
404-
expect(phase.apps[i].environments.length).toBe(
405-
mockResponse.apps[i].environment_keys.length
406-
);
407-
408-
for (let j = 0; j < phase.apps[i].environments.length; j++) {
409-
expect(phase.apps[i].environments[j].keypair.publicKey).toBeDefined();
410-
expect(phase.apps[i].environments[j].keypair.privateKey).toBeDefined();
411-
expect(phase.apps[i].environments[j].salt).toBeDefined();
412-
}
413-
}
349+
expect(phase._isInitialized).toBe(true);
414350
});
415351

416-
it("should reconstruct the private key and decrypt environment keys correctly with a service token", async () => {
417-
const phase = new Phase(validServiceToken, mockHost);
418-
await phase.init();
419-
420-
// Ensure reconstructPrivateKey was called with real data
421-
expect(phase.keypair.privateKey).toBeDefined();
422-
expect(phase.keypair.publicKey).toBe(validServiceToken.split(":")[3]);
423-
424-
// Verify each environment key was unwrapped correctly
425-
for (const app of phase.apps) {
426-
for (const env of app.environments) {
427-
expect(env.keypair.publicKey).toBeDefined();
428-
expect(env.keypair.privateKey).toBeDefined();
429-
expect(env.salt).toBeDefined();
430-
}
431-
}
432-
});
352+
433353
});

0 commit comments

Comments
 (0)