From 71fb018bc405caecf51112c1bdb492d9c1998ee7 Mon Sep 17 00:00:00 2001 From: Sergey Petushkov Date: Thu, 25 May 2023 17:34:49 +0200 Subject: [PATCH 1/5] chore: add new utm attributes to all mongodb links --- src/explorer/helpTree.ts | 17 +++----- src/language/mongoDBService.ts | 7 ++-- src/test/suite/explorer/helpExplorer.test.ts | 7 +--- src/utils/links.ts | 41 ++++++++++++++++++++ 4 files changed, 52 insertions(+), 20 deletions(-) create mode 100644 src/utils/links.ts diff --git a/src/explorer/helpTree.ts b/src/explorer/helpTree.ts index 5211757a0..e9aaca4c0 100644 --- a/src/explorer/helpTree.ts +++ b/src/explorer/helpTree.ts @@ -1,9 +1,9 @@ import * as vscode from 'vscode'; import path from 'path'; - import { getImagesPath } from '../extensionConstants'; import { TelemetryService } from '../telemetry'; import { openLink } from '../utils/linkHelper'; +import LINKS from '../utils/links'; const HELP_LINK_CONTEXT_VALUE = 'HELP_LINK'; @@ -76,14 +76,14 @@ export default class HelpTree if (!element) { const whatsNew = new HelpLinkTreeItem( "What's New", - 'https://github.com/mongodb-js/vscode/blob/main/CHANGELOG.md', + LINKS.changelog, 'whatsNew', 'megaphone' ); const extensionDocs = new HelpLinkTreeItem( 'Extension Documentation', - 'https://docs.mongodb.com/mongodb-vscode/', + LINKS.extensionDocs, 'extensionDocumentation', 'book' ); @@ -97,26 +97,21 @@ export default class HelpTree const feedback = new HelpLinkTreeItem( 'Suggest a Feature', - 'https://feedback.mongodb.com/forums/929236-mongodb-for-vs-code/', + LINKS.feedback, 'feedback', 'lightbulb' ); const reportBug = new HelpLinkTreeItem( 'Report a Bug', - 'https://github.com/mongodb-js/vscode/issues', + LINKS.reportBug, 'reportABug', 'report' ); - const telemetryUserIdentity = - this._telemetryService?.getTelemetryUserIdentity(); - const ajsAid = telemetryUserIdentity - ? `&ajs_aid=${telemetryUserIdentity[0]}` - : ''; const atlas = new HelpLinkTreeItem( 'Create Free Atlas Cluster', - `https://mongodb.com/products/vs-code/vs-code-atlas-signup?utm_campaign=vs-code-extension&utm_source=visual-studio&utm_medium=product${ajsAid}`, + LINKS.createAtlasCluster, 'freeClusterCTA', 'atlas', true diff --git a/src/language/mongoDBService.ts b/src/language/mongoDBService.ts index 319404710..1f0c9d4bf 100644 --- a/src/language/mongoDBService.ts +++ b/src/language/mongoDBService.ts @@ -36,6 +36,7 @@ import type { import type { ClearCompletionsCache } from '../types/completionsCache'; import { Visitor } from './visitor'; import type { CompletionState } from './visitor'; +import LINKS from '../utils/links'; import DIAGNOSTIC_CODES from './diagnosticCodes'; @@ -445,7 +446,7 @@ export default class MongoDBService { description?: string; }) { const title = operator.replace(/[$]/g, ''); - const link = `https://www.mongodb.com/docs/manual/reference/operator/aggregation/${title}/`; + const link = LINKS.aggregationDocs(title); return { kind: MarkupKind.Markdown, value: description @@ -461,7 +462,7 @@ export default class MongoDBService { bsonType: string; description?: string; }) { - const link = `https://www.mongodb.com/docs/mongodb-shell/reference/data-types/#${bsonType}`; + const link = LINKS.bsonDocs(bsonType); return { kind: MarkupKind.Markdown, value: description @@ -478,7 +479,7 @@ export default class MongoDBService { description?: string; }) { const title = variable.replace(/[$]/g, ''); - const link = `https://www.mongodb.com/docs/manual/reference/aggregation-variables/#mongodb-variable-variable.${title}`; + const link = LINKS.systemVariableDocs(title); return { kind: MarkupKind.Markdown, value: description diff --git a/src/test/suite/explorer/helpExplorer.test.ts b/src/test/suite/explorer/helpExplorer.test.ts index e2e0ddf9d..e7c2a2084 100644 --- a/src/test/suite/explorer/helpExplorer.test.ts +++ b/src/test/suite/explorer/helpExplorer.test.ts @@ -44,12 +44,7 @@ suite('Help Explorer Test Suite', function () { const atlasHelpItem = helpTreeItems[5]; assert.strictEqual(atlasHelpItem.label, 'Create Free Atlas Cluster'); - const telemetryUserIdentity = - mdbTestExtension.testExtensionController._telemetryService.getTelemetryUserIdentity(); - assert.strictEqual( - atlasHelpItem.url, - `https://mongodb.com/products/vs-code/vs-code-atlas-signup?utm_campaign=vs-code-extension&utm_source=visual-studio&utm_medium=product&ajs_aid=${telemetryUserIdentity[0]}` - ); + assert.strictEqual(atlasHelpItem.url.includes('mongodb.com'), true); assert.strictEqual(atlasHelpItem.iconName, 'atlas'); assert.strictEqual(atlasHelpItem.linkId, 'freeClusterCTA'); assert(atlasHelpItem.useRedirect === true); diff --git a/src/utils/links.ts b/src/utils/links.ts new file mode 100644 index 000000000..393d0cab5 --- /dev/null +++ b/src/utils/links.ts @@ -0,0 +1,41 @@ +const addUTMAttrs = (url: string) => { + const parsed = new URL(url); + if (!parsed.host.includes('mongodb')) { + return url; + } + parsed.searchParams.set('utm_source', 'vscode'); + parsed.searchParams.set('utm_medium', 'product'); + return parsed.toString(); +}; + +const LINKS = { + changelog: 'https://github.com/mongodb-js/vscode/blob/main/CHANGELOG.md', + extensionDocs: 'https://docs.mongodb.com/mongodb-vscode/', + mongodbDocs: 'https://docs.mongodb.com/manual/', + feedback: 'https://feedback.mongodb.com/forums/929236-mongodb-for-vs-code/', + reportBug: 'https://github.com/mongodb-js/vscode/issues', + createAtlasCluster: + 'https://mongodb.com/products/vs-code/vs-code-atlas-signup', + aggregationDocs: (title: string) => { + return `https://www.mongodb.com/docs/manual/reference/operator/aggregation/${title}/`; + }, + bsonDocs: (type: string) => { + return `https://www.mongodb.com/docs/mongodb-shell/reference/data-types/#${type}`; + }, + systemVariableDocs: (name: string) => { + return `https://www.mongodb.com/docs/manual/reference/aggregation-variables/#mongodb-variable-variable.${name}`; + }, +}; + +export default Object.fromEntries( + Object.entries(LINKS).map(([k, v]) => { + return [ + k, + typeof v === 'string' + ? addUTMAttrs(v) + : (name: string) => { + return addUTMAttrs(v(name)); + }, + ]; + }) +) as typeof LINKS; From ae2e4fa92517d0cf9163933a211c6fda4d938e43 Mon Sep 17 00:00:00 2001 From: Sergey Petushkov Date: Thu, 1 Jun 2023 14:36:02 +0200 Subject: [PATCH 2/5] chore: add ajs_aid back --- src/explorer/helpTree.ts | 9 ++++++++- src/test/suite/explorer/helpExplorer.test.ts | 6 ++++++ src/utils/links.ts | 6 ++++-- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/explorer/helpTree.ts b/src/explorer/helpTree.ts index e9aaca4c0..e60748a2e 100644 --- a/src/explorer/helpTree.ts +++ b/src/explorer/helpTree.ts @@ -109,9 +109,16 @@ export default class HelpTree 'report' ); + const telemetryUserIdentity = + this._telemetryService?.getTelemetryUserIdentity(); + const atlas = new HelpLinkTreeItem( 'Create Free Atlas Cluster', - LINKS.createAtlasCluster, + LINKS.createAtlasCluster( + telemetryUserIdentity?.userId ?? + telemetryUserIdentity?.anonymousId ?? + '' + ), 'freeClusterCTA', 'atlas', true diff --git a/src/test/suite/explorer/helpExplorer.test.ts b/src/test/suite/explorer/helpExplorer.test.ts index e7c2a2084..402d21093 100644 --- a/src/test/suite/explorer/helpExplorer.test.ts +++ b/src/test/suite/explorer/helpExplorer.test.ts @@ -45,6 +45,12 @@ suite('Help Explorer Test Suite', function () { assert.strictEqual(atlasHelpItem.label, 'Create Free Atlas Cluster'); assert.strictEqual(atlasHelpItem.url.includes('mongodb.com'), true); + const { userId, anonymousId } = + mdbTestExtension.testExtensionController._telemetryService.getTelemetryUserIdentity(); + assert.strictEqual( + new URL(atlasHelpItem.url).searchParams.get('ajs_aid'), + userId ?? anonymousId + ); assert.strictEqual(atlasHelpItem.iconName, 'atlas'); assert.strictEqual(atlasHelpItem.linkId, 'freeClusterCTA'); assert(atlasHelpItem.useRedirect === true); diff --git a/src/utils/links.ts b/src/utils/links.ts index 393d0cab5..9d2dde1a9 100644 --- a/src/utils/links.ts +++ b/src/utils/links.ts @@ -14,8 +14,10 @@ const LINKS = { mongodbDocs: 'https://docs.mongodb.com/manual/', feedback: 'https://feedback.mongodb.com/forums/929236-mongodb-for-vs-code/', reportBug: 'https://github.com/mongodb-js/vscode/issues', - createAtlasCluster: - 'https://mongodb.com/products/vs-code/vs-code-atlas-signup', + createAtlasCluster: (userId: string) => { + const ajsAid = userId ? `?ajs_aid=${encodeURIComponent(userId)}` : ''; + return `https://mongodb.com/products/vs-code/vs-code-atlas-signup${ajsAid}`; + }, aggregationDocs: (title: string) => { return `https://www.mongodb.com/docs/manual/reference/operator/aggregation/${title}/`; }, From ec697a49f5a7bdbc4c9c660a8452b1cd0d12e48a Mon Sep 17 00:00:00 2001 From: Sergey Petushkov Date: Thu, 1 Jun 2023 15:52:10 +0200 Subject: [PATCH 3/5] chore: move more links to utils/links --- src/explorer/helpTree.ts | 4 +-- .../components/atlas-cta/atlas-cta.test.tsx | 12 +++++++-- src/utils/links.ts | 27 ++++++++++++++++--- .../components/atlas-cta/atlas-cta.tsx | 5 ++-- .../general-tab/authentication/kerberos.tsx | 3 ++- .../general-tab/authentication/ldap.tsx | 3 ++- .../authentication/mongodb-authentication.tsx | 3 ++- .../authentication/scram-sha-256.tsx | 3 ++- .../ssh-tunnel-identity-file-validation.tsx | 3 ++- .../ssh-tunnel-password-validation.tsx | 3 ++- .../ssl-tab/ssl-server-client-validation.tsx | 7 ++--- .../ssl-tab/ssl-server-validation.tsx | 3 ++- .../connection-status/connection-status.tsx | 3 ++- .../resources-panel/resources-panel.tsx | 23 ++++++++-------- 14 files changed, 70 insertions(+), 32 deletions(-) diff --git a/src/explorer/helpTree.ts b/src/explorer/helpTree.ts index e60748a2e..832c4da37 100644 --- a/src/explorer/helpTree.ts +++ b/src/explorer/helpTree.ts @@ -83,14 +83,14 @@ export default class HelpTree const extensionDocs = new HelpLinkTreeItem( 'Extension Documentation', - LINKS.extensionDocs, + LINKS.extensionDocs(), 'extensionDocumentation', 'book' ); const mdbDocs = new HelpLinkTreeItem( 'MongoDB Documentation', - 'https://docs.mongodb.com/manual/', + LINKS.mongodbDocs, 'mongoDBDocumentation', 'leaf' ); diff --git a/src/test/suite/views/webview-app/components/atlas-cta/atlas-cta.test.tsx b/src/test/suite/views/webview-app/components/atlas-cta/atlas-cta.test.tsx index 33d280750..7fb41a279 100644 --- a/src/test/suite/views/webview-app/components/atlas-cta/atlas-cta.test.tsx +++ b/src/test/suite/views/webview-app/components/atlas-cta/atlas-cta.test.tsx @@ -60,8 +60,16 @@ describe('Resources Panel Component Test Suite', () => { 'OPEN_TRUSTED_LINK' ); assert.strictEqual( - fakeVscodeWindowPostMessage.firstCall.args[0].linkTo, - 'https://mongodb.com/products/vs-code/vs-code-atlas-signup?utm_campaign=vs-code-extension&utm_source=visual-studio&utm_medium=product&ajs_aid=mockAnonymousID' + fakeVscodeWindowPostMessage.firstCall.args[0].linkTo.includes( + 'mongodb.com' + ), + true + ); + assert.strictEqual( + new URL( + fakeVscodeWindowPostMessage.firstCall.args[0].linkTo + ).searchParams.get('ajs_aid'), + 'mockAnonymousID' ); // The assert below is a bit redundant but will prevent us from redirecting to a non-https URL by mistake assert( diff --git a/src/utils/links.ts b/src/utils/links.ts index 9d2dde1a9..fd1376b4b 100644 --- a/src/utils/links.ts +++ b/src/utils/links.ts @@ -10,14 +10,21 @@ const addUTMAttrs = (url: string) => { const LINKS = { changelog: 'https://github.com/mongodb-js/vscode/blob/main/CHANGELOG.md', - extensionDocs: 'https://docs.mongodb.com/mongodb-vscode/', - mongodbDocs: 'https://docs.mongodb.com/manual/', feedback: 'https://feedback.mongodb.com/forums/929236-mongodb-for-vs-code/', + github: 'https://github.com/mongodb-js/vscode', reportBug: 'https://github.com/mongodb-js/vscode/issues', - createAtlasCluster: (userId: string) => { - const ajsAid = userId ? `?ajs_aid=${encodeURIComponent(userId)}` : ''; + atlas: 'https://www.mongodb.com/cloud/atlas', + createAtlasCluster: (anonymousId: string) => { + const ajsAid = anonymousId + ? `?ajs_aid=${encodeURIComponent(anonymousId)}` + : ''; return `https://mongodb.com/products/vs-code/vs-code-atlas-signup${ajsAid}`; }, + docs: 'https://docs.mongodb.com/', + mongodbDocs: 'https://docs.mongodb.com/manual/', + extensionDocs(subcategory = '') { + return `https://docs.mongodb.com/mongodb-vscode/${subcategory}`; + }, aggregationDocs: (title: string) => { return `https://www.mongodb.com/docs/manual/reference/operator/aggregation/${title}/`; }, @@ -27,6 +34,18 @@ const LINKS = { systemVariableDocs: (name: string) => { return `https://www.mongodb.com/docs/manual/reference/aggregation-variables/#mongodb-variable-variable.${name}`; }, + kerberosPrincipalDocs: + 'https://docs.mongodb.com/manual/core/kerberos/#principals', + ldapDocs: 'https://docs.mongodb.com/manual/core/security-ldap/', + authDatabaseDocs: + 'https://docs.mongodb.com/manual/core/security-users/#user-authentication-database', + sshConnectionDocs: + 'https://docs.mongodb.com/compass/current/connect/advanced-connection-options/ssh-connection/#ssh-connection', + configureSSLDocs(subsection = '') { + return `https://docs.mongodb.com/manual/tutorial/configure-ssl/${subsection}`; + }, + pemKeysDocs: + 'https://docs.mongodb.com/manual/reference/configuration-options/#net.ssl.PEMKeyPassword', }; export default Object.fromEntries( diff --git a/src/views/webview-app/components/atlas-cta/atlas-cta.tsx b/src/views/webview-app/components/atlas-cta/atlas-cta.tsx index 11e957e21..8069c1d23 100644 --- a/src/views/webview-app/components/atlas-cta/atlas-cta.tsx +++ b/src/views/webview-app/components/atlas-cta/atlas-cta.tsx @@ -10,6 +10,7 @@ import { VSCODE_EXTENSION_SEGMENT_ANONYMOUS_ID } from '../../extension-app-messa import AtlasLogo from './atlas-logo'; import styles from './atlas-cta.less'; +import LINKS from '../../../../utils/links'; type DispatchProps = { onLinkClicked: (screen: string, linkId: string) => void; @@ -19,7 +20,7 @@ type DispatchProps = { class AtlasCTA extends React.Component { onAtlasCtaClicked = (): void => { const telemetryUserId = window[VSCODE_EXTENSION_SEGMENT_ANONYMOUS_ID]; - const atlasLink = `https://mongodb.com/products/vs-code/vs-code-atlas-signup?utm_campaign=vs-code-extension&utm_source=visual-studio&utm_medium=product&ajs_aid=${telemetryUserId}`; + const atlasLink = LINKS.createAtlasCluster(telemetryUserId); this.props.openTrustedLink(atlasLink); this.onLinkClicked('overviewPage', 'freeClusterCTA'); @@ -43,7 +44,7 @@ class AtlasCTA extends React.Component { className={styles['atlas-cta-text-link']} target="_blank" rel="noopener" - href="https://www.mongodb.com/cloud/atlas" + href={LINKS.atlas} onClick={this.onLinkClicked.bind( this, 'overviewPage', diff --git a/src/views/webview-app/components/connect-form/general-tab/authentication/kerberos.tsx b/src/views/webview-app/components/connect-form/general-tab/authentication/kerberos.tsx index a9c45de70..63a6079a3 100644 --- a/src/views/webview-app/components/connect-form/general-tab/authentication/kerberos.tsx +++ b/src/views/webview-app/components/connect-form/general-tab/authentication/kerberos.tsx @@ -10,6 +10,7 @@ import { import FormInput from '../../../form/form-input'; import styles from '../../../../connect.module.less'; +import LINKS from '../../../../../../utils/links'; type DispatchProps = { kerberosParametersChanged: (newParams: KerberosParameters) => void; @@ -126,7 +127,7 @@ class Kerberos extends React.Component { changeHandler={this.onPrincipalChanged} value={kerberosPrincipal || ''} // Open the help page for the principal. - linkTo="https://docs.mongodb.com/manual/core/kerberos/#principals" + linkTo={LINKS.kerberosPrincipalDocs} /> void; @@ -58,7 +59,7 @@ class LDAP extends React.Component { changeHandler={this.onUsernameChanged} value={ldapUsername || ''} // Open the help page for LDAP. - linkTo="https://docs.mongodb.com/manual/core/security-ldap/" + linkTo={LINKS.ldapDocs} /> void; @@ -78,7 +79,7 @@ class MongoDBAuthentication extends React.Component { changeHandler={this.onAuthSourceChanged} value={mongodbDatabaseName || ''} // Opens "Authentication Database" documentation. - linkTo="https://docs.mongodb.com/manual/core/security-users/#user-authentication-database" + linkTo={LINKS.authDatabaseDocs} /> ); diff --git a/src/views/webview-app/components/connect-form/general-tab/authentication/scram-sha-256.tsx b/src/views/webview-app/components/connect-form/general-tab/authentication/scram-sha-256.tsx index 2adf72c48..c0c230ed0 100644 --- a/src/views/webview-app/components/connect-form/general-tab/authentication/scram-sha-256.tsx +++ b/src/views/webview-app/components/connect-form/general-tab/authentication/scram-sha-256.tsx @@ -8,6 +8,7 @@ import { UsernameChangedAction, } from '../../../../store/actions'; import FormInput from '../../../form/form-input'; +import LINKS from '../../../../../../utils/links'; type DispatchProps = { onAuthSourceChanged: (newAuthSource: string) => void; @@ -78,7 +79,7 @@ class ScramSha256 extends React.Component { changeHandler={this.onAuthSourceChanged} value={mongodbDatabaseName || ''} // Opens "Authentication Database" documentation. - linkTo="https://docs.mongodb.com/manual/core/security-users/#user-authentication-database" + linkTo={LINKS.authDatabaseDocs} /> ); diff --git a/src/views/webview-app/components/connect-form/ssh-tab/ssh-tunnel-identity-file-validation.tsx b/src/views/webview-app/components/connect-form/ssh-tab/ssh-tunnel-identity-file-validation.tsx index 42d3c55a6..65d76f66c 100644 --- a/src/views/webview-app/components/connect-form/ssh-tab/ssh-tunnel-identity-file-validation.tsx +++ b/src/views/webview-app/components/connect-form/ssh-tab/ssh-tunnel-identity-file-validation.tsx @@ -13,6 +13,7 @@ import { AppState } from '../../../store/store'; import FormInput from '../../form/form-input'; import FileInputButton from '../../form/file-input-button'; import FormGroup from '../../form/form-group'; +import LINKS from '../../../../../utils/links'; type DispatchProps = { onChangeSSHTunnelIdentityFile: () => void; @@ -97,7 +98,7 @@ class SSHTunnelIdentityFileValidation extends React.Component { error={!isValid && sshTunnelHostname === undefined} changeHandler={this.onSSHTunnelHostnameChanged} value={sshTunnelHostname || ''} - linkTo="https://docs.mongodb.com/compass/current/connect" + linkTo={LINKS.sshConnectionDocs} /> void; @@ -83,7 +84,7 @@ class SSHTunnelPasswordValidation extends React.Component { error={!isValid && sshTunnelHostname === undefined} changeHandler={this.onSSHTunnelHostnameChanged} value={sshTunnelHostname || ''} - linkTo="https://docs.mongodb.com/compass/current/connect" + linkTo={LINKS.sshConnectionDocs} /> { error={!isValid && sslCA === undefined} onClick={this.onCertificateAuthorityChanged} values={sslCA} - link="https://docs.mongodb.com/manual/tutorial/configure-ssl/#certificate-authorities" + link={LINKS.configureSSLDocs('#certificate-authorities')} /> { error={!isValid && sslCert === undefined} onClick={this.onClientCertificateChanged} values={sslCert} - link="https://docs.mongodb.com/manual/tutorial/configure-ssl/#pem-file" + link={LINKS.configureSSLDocs('#pem-file')} /> { changeHandler={this.onClientKeyPasswordChanged} value={sslPass || ''} // Opens documentation about net.ssl.PEMKeyPassword. - linkTo="https://docs.mongodb.com/manual/reference/configuration-options/#net.ssl.PEMKeyPassword" + linkTo={LINKS.pemKeysDocs} /> ); diff --git a/src/views/webview-app/components/connect-form/ssl-tab/ssl-server-validation.tsx b/src/views/webview-app/components/connect-form/ssl-tab/ssl-server-validation.tsx index 8a1d3375e..62ca074ce 100644 --- a/src/views/webview-app/components/connect-form/ssl-tab/ssl-server-validation.tsx +++ b/src/views/webview-app/components/connect-form/ssl-tab/ssl-server-validation.tsx @@ -7,6 +7,7 @@ import { ActionTypes, OnChangeSSLCAAction } from '../../../store/actions'; import { AppState } from '../../../store/store'; import styles from '../../../connect.module.less'; +import LINKS from '../../../../../utils/links'; type StateProps = { isValid: boolean; @@ -39,7 +40,7 @@ class SSLServerValidation extends React.Component { error={!isValid && sslCA === undefined} id="sslCA" label="Certificate Authority" - link="https://docs.mongodb.com/manual/tutorial/configure-ssl/#certificate-authorities" + link={LINKS.configureSSLDocs('#certificate-authorities')} multi onClick={this.onClickChangeSSLCA} values={this.props.sslCA} diff --git a/src/views/webview-app/components/connection-status/connection-status.tsx b/src/views/webview-app/components/connection-status/connection-status.tsx index 76a6da799..6a51e4a22 100644 --- a/src/views/webview-app/components/connection-status/connection-status.tsx +++ b/src/views/webview-app/components/connection-status/connection-status.tsx @@ -15,6 +15,7 @@ import InfoSprinkle from '../info-sprinkle/info-sprinkle'; import { CONNECTION_STATUS } from '../../extension-app-message-constants'; import styles from './connection-status.less'; +import LINKS from '../../../../utils/links'; const CONNECTION_STATUS_POLLING_FREQ_MS = 1000; @@ -92,7 +93,7 @@ export class ConnectionStatus extends React.Component<
All set. Ready to start?
Create a playground. - +