@hubspot/cli 7.9.0-beta.0 → 7.9.0-beta.1
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/lang/en.d.ts +1 -0
- package/lang/en.js +1 -0
- package/lib/__tests__/http.test.d.ts +1 -0
- package/lib/__tests__/http.test.js +40 -0
- package/lib/http.d.ts +1 -0
- package/lib/http.js +26 -0
- package/lib/projects/localDev/AppDevModeInterface.d.ts +1 -0
- package/lib/projects/localDev/AppDevModeInterface.js +16 -0
- package/package.json +1 -1
package/lang/en.d.ts
CHANGED
|
@@ -2835,6 +2835,7 @@ export declare const lib: {
|
|
|
2835
2835
|
readonly success: (appName: string, accountId: number) => string;
|
|
2836
2836
|
};
|
|
2837
2837
|
readonly appDataNotFound: "An error occurred while fetching data for your app.";
|
|
2838
|
+
readonly oauthAppRedirectUrlError: (redirectUrl: string) => string;
|
|
2838
2839
|
};
|
|
2839
2840
|
readonly LocalDevWebsocketServer: {
|
|
2840
2841
|
readonly errors: {
|
package/lang/en.js
CHANGED
|
@@ -2829,6 +2829,7 @@ export const lib = {
|
|
|
2829
2829
|
success: (appName, accountId) => `Your app ${chalk.bold(appName)} has been installed successfully on account ${uiAccountDescription(accountId)}\n`,
|
|
2830
2830
|
},
|
|
2831
2831
|
appDataNotFound: 'An error occurred while fetching data for your app.',
|
|
2832
|
+
oauthAppRedirectUrlError: (redirectUrl) => `${chalk.bold('No reponse from your OAuth service:')} ${redirectUrl}\nYour app needs a valid OAuth2 service to be installed for local dev. ${uiLink('Learn more', 'https://developers.hubspot.com/docs/apps/developer-platform/build-apps/authentication/oauth/working-with-oauth')}`,
|
|
2832
2833
|
},
|
|
2833
2834
|
LocalDevWebsocketServer: {
|
|
2834
2835
|
errors: {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
|
|
2
|
+
import http from 'http';
|
|
3
|
+
import { isServerRunningAtUrl } from '../http.js';
|
|
4
|
+
describe('isServerRunningAtUrl', () => {
|
|
5
|
+
let server;
|
|
6
|
+
let serverPort;
|
|
7
|
+
beforeAll(async () => {
|
|
8
|
+
server = http.createServer((req, res) => {
|
|
9
|
+
res.writeHead(200);
|
|
10
|
+
res.end('OK');
|
|
11
|
+
});
|
|
12
|
+
await new Promise(resolve => {
|
|
13
|
+
server.listen(0, () => {
|
|
14
|
+
serverPort = server.address().port;
|
|
15
|
+
resolve();
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
afterAll(async () => {
|
|
20
|
+
await new Promise(resolve => {
|
|
21
|
+
server.close(() => resolve());
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
it('should return true when a server is running', async () => {
|
|
25
|
+
const result = await isServerRunningAtUrl(`http://localhost:${serverPort}`);
|
|
26
|
+
expect(result).toBe(true);
|
|
27
|
+
});
|
|
28
|
+
it('should return false when connection is refused', async () => {
|
|
29
|
+
const result = await isServerRunningAtUrl('http://localhost:59999');
|
|
30
|
+
expect(result).toBe(false);
|
|
31
|
+
});
|
|
32
|
+
it('should return false for invalid URLs', async () => {
|
|
33
|
+
const result = await isServerRunningAtUrl('not-a-valid-url');
|
|
34
|
+
expect(result).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
it('should handle different paths on the same server', async () => {
|
|
37
|
+
const result = await isServerRunningAtUrl(`http://localhost:${serverPort}/some/path`);
|
|
38
|
+
expect(result).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
package/lib/http.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function isServerRunningAtUrl(serverUrl: string): Promise<boolean>;
|
package/lib/http.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import https from 'https';
|
|
3
|
+
const IS_SERVER_RUNNING_TIMEOUT = 2000;
|
|
4
|
+
export function isServerRunningAtUrl(serverUrl) {
|
|
5
|
+
return new Promise(resolve => {
|
|
6
|
+
try {
|
|
7
|
+
const url = new URL(serverUrl);
|
|
8
|
+
const protocol = url.protocol === 'https:' ? https : http;
|
|
9
|
+
const req = protocol.get(serverUrl);
|
|
10
|
+
req.on('socket', socket => {
|
|
11
|
+
socket.on('connect', () => {
|
|
12
|
+
resolve(true);
|
|
13
|
+
req.destroy();
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
req.on('error', (err) => {
|
|
17
|
+
resolve(err.code !== 'ECONNREFUSED');
|
|
18
|
+
});
|
|
19
|
+
req.end();
|
|
20
|
+
setTimeout(() => resolve(false), IS_SERVER_RUNNING_TIMEOUT);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
resolve(false);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -26,6 +26,7 @@ declare class AppDevModeInterface {
|
|
|
26
26
|
private autoInstallStaticAuthApp;
|
|
27
27
|
private installAppOrOpenInstallUrl;
|
|
28
28
|
private checkTestAccountAppInstallation;
|
|
29
|
+
private validateOauthAppRedirectUrl;
|
|
29
30
|
private resolveAppInstallPromise;
|
|
30
31
|
private handleAppInstallSuccessDevServerMessage;
|
|
31
32
|
private handleAppInstallInitiatedDevServerMessage;
|
|
@@ -13,6 +13,7 @@ import { uiLogger } from '../../ui/logger.js';
|
|
|
13
13
|
import { getOauthAppInstallUrl, getStaticAuthAppInstallUrl, } from '../../app/urls.js';
|
|
14
14
|
import { isDeveloperTestAccount, isSandbox } from '../../accountTypes.js';
|
|
15
15
|
import SpinniesManager from '../../ui/SpinniesManager.js';
|
|
16
|
+
import { isServerRunningAtUrl } from '../../http.js';
|
|
16
17
|
class AppDevModeInterface {
|
|
17
18
|
localDevState;
|
|
18
19
|
localDevLogger;
|
|
@@ -214,6 +215,18 @@ class AppDevModeInterface {
|
|
|
214
215
|
}
|
|
215
216
|
return { needsInstall: !isInstalledWithScopeGroups, isReinstall };
|
|
216
217
|
}
|
|
218
|
+
async validateOauthAppRedirectUrl() {
|
|
219
|
+
const redirectUrl = this.appNode?.config.auth.redirectUrls[0];
|
|
220
|
+
if (!redirectUrl) {
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const serverIsRunningAtRedirectUrl = await isServerRunningAtUrl(redirectUrl);
|
|
224
|
+
if (!serverIsRunningAtRedirectUrl) {
|
|
225
|
+
uiLogger.log('');
|
|
226
|
+
uiLogger.error(lib.AppDevModeInterface.oauthAppRedirectUrlError(redirectUrl));
|
|
227
|
+
process.exit(EXIT_CODES.ERROR);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
217
230
|
resolveAppInstallPromise() {
|
|
218
231
|
if (this.appInstallResolve) {
|
|
219
232
|
this.appInstallResolve();
|
|
@@ -284,6 +297,9 @@ class AppDevModeInterface {
|
|
|
284
297
|
failColor: 'white',
|
|
285
298
|
});
|
|
286
299
|
}
|
|
300
|
+
if (this.isOAuthApp()) {
|
|
301
|
+
await this.validateOauthAppRedirectUrl();
|
|
302
|
+
}
|
|
287
303
|
this.localDevState.addListener('devServerMessage', this.onDevServerMessage);
|
|
288
304
|
await this.installAppOrOpenInstallUrl(isReinstall || false);
|
|
289
305
|
}
|