Skip to content

Commit

Permalink
chore: add telemetry realworld app (#26896)
Browse files Browse the repository at this point in the history
* chore: capture telemetry for realworld app maybe

* idk what i was doing

* setup record key and telemetry

* testing

* override project id

* some times we just need a little context.

* Adding tests

* Adding comment
  • Loading branch information
mjhenkes authored Jun 1, 2023
1 parent 7ef4300 commit 3d1ec09
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 14 deletions.
11 changes: 6 additions & 5 deletions .circleci/workflows.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ mainBuildFilters: &mainBuildFilters
- /^release\/\d+\.\d+\.\d+$/
# use the following branch as well to ensure that v8 snapshot cache updates are fully tested
- 'update-v8-snapshot-cache-on-develop'
- 'ryanm/feat/unify-cdp-approach-in-electron'
- 'matth/chore/add-telemetry-realworld-app'

# usually we don't build Mac app - it takes a long time
# but sometimes we want to really confirm we are doing the right thing
Expand Down Expand Up @@ -139,7 +139,7 @@ commands:
- run:
name: Check current branch to persist artifacts
command: |
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" && "$CIRCLE_BRANCH" != "ryanm/feat/unify-cdp-approach-in-electron" ]]; then
if [[ "$CIRCLE_BRANCH" != "develop" && "$CIRCLE_BRANCH" != "release/"* && "$CIRCLE_BRANCH" != "update-v8-snapshot-cache-on-develop" && "$CIRCLE_BRANCH" != "matth/chore/add-telemetry-realworld-app" ]]; then
echo "Not uploading artifacts or posting install comment for this branch."
circleci-agent step halt
fi
Expand Down Expand Up @@ -752,7 +752,7 @@ commands:
command:
description: Test command to run to start Cypress tests
type: string
default: "yarn cypress:run"
default: "CYPRESS_INTERNAL_ENABLE_TELEMETRY=1 CYPRESS_RECORD_KEY=$MAIN_RECORD_KEY CYPRESS_PROJECT_ID=ypt4pf yarn cypress:run"
# if the repo to clone and test is a monorepo, you can
# run tests inside a specific subfolder
folder:
Expand Down Expand Up @@ -822,7 +822,7 @@ commands:
name: Run tests using browser "<<parameters.browser>>"
working_directory: /tmp/<<parameters.repo>>/<<parameters.folder>>
command: |
<<parameters.command>> -- --browser <<parameters.browser>>
<<parameters.command>> --browser <<parameters.browser>> --record false
- unless:
condition: <<parameters.browser>>
steps:
Expand All @@ -839,7 +839,7 @@ commands:
- run:
name: Run tests using browser "<<parameters.browser>>"
working_directory: /tmp/<<parameters.repo>>
command: <<parameters.command>> -- --browser <<parameters.browser>>
command: <<parameters.command>> --browser <<parameters.browser>> --record false
- unless:
condition: <<parameters.browser>>
steps:
Expand Down Expand Up @@ -2753,6 +2753,7 @@ linux-x64-workflow: &linux-x64-workflow
requires:
- create-build-artifacts
- test-binary-against-cypress-realworld-app:
context: test-runner:cypress-record-key
<<: *mainBuildFilters
requires:
- create-build-artifacts
Expand Down
4 changes: 1 addition & 3 deletions packages/server/lib/cypress.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,7 @@ module.exports = {

debug('from argv %o got options %o', argv, options)

if (options.key) {
telemetry.exporter()?.attachRecordKey(options.key)
}
telemetry.exporter()?.attachRecordKey(options.key)

if (options.headless) {
// --headless is same as --headed false
Expand Down
29 changes: 29 additions & 0 deletions packages/telemetry/src/span-exporters/cloud-span-exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,20 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
enc: OTLPExporterNodeConfigBasePlusEncryption['encryption'] | undefined
projectId?: string
recordKey?: string
requirementsToExport: 'met'| 'unmet' | 'unknown'
sendWithHttp: typeof sendWithHttp
constructor (config: OTLPExporterNodeConfigBasePlusEncryption = {}) {
super(config)
this.enc = config.encryption
this.delayedItemsToExport = []
this.sendWithHttp = sendWithHttp
// when encryption is on, requirementsToExport will be set to unknown until projectId and/or record key are attached.
// We will delay sending spans until requirementsToExport is either met or unmet. If unmet we will fail all attempts to send.
if (this.enc) {
this.requirementsToExport = 'unknown'
this.headers['x-cypress-encrypted'] = '1'
} else {
this.requirementsToExport = 'met'
}
}

Expand All @@ -50,6 +56,11 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
*/
attachProjectId (projectId: string | null | undefined): void {
if (!projectId) {
if (this.requirementsToExport === 'unknown') {
this.requirementsToExport = 'unmet'
this.abortDelayedItems()
}

return
}

Expand All @@ -65,6 +76,11 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
*/
attachRecordKey (recordKey: string | null | undefined): void {
if (!recordKey) {
if (this.requirementsToExport === 'unknown') {
this.requirementsToExport = 'unmet'
this.abortDelayedItems()
}

return
}

Expand All @@ -77,6 +93,7 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
*/
setAuthorizationHeader () {
if (this.projectId && this.recordKey) {
this.requirementsToExport = 'met'
this.headers.Authorization = `Basic ${Buffer.from(`${this.projectId}:${this.recordKey}`).toString('base64')}`
this.sendDelayedItems()
}
Expand All @@ -95,6 +112,14 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
}
}

abortDelayedItems () {
this.delayedItemsToExport.forEach((item) => {
item.onError(new Error('Spans cannot be sent, exporter has unmet requirements, either project id or record key are undefined.'))
})

this.delayedItemsToExport = []
}

/**
* Overrides send if we need to encrypt the request.
* @param objects
Expand All @@ -113,6 +138,10 @@ export class OTLPTraceExporter extends OTLPTraceExporterHttp {
return
}

if (this.requirementsToExport === 'unmet') {
onError(new Error('Spans cannot be sent, exporter has unmet requirements, either project id or record key are undefined.'))
}

let serviceRequest: string

if (typeof objects !== 'string') {
Expand Down
72 changes: 66 additions & 6 deletions packages/telemetry/test/span-exporters/cloud-span-exporter.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ describe('cloudSpanExporter', () => {
const exporter = new OTLPTraceExporter(genericRequest)

expect(exporter.headers['x-cypress-encrypted']).to.equal('1')
expect(exporter.requirementsToExport).to.equal('unknown')
expect(exporter.enc).to.not.be.undefined
})

it('does not set encrypted header if not set', () => {
const exporter = new OTLPTraceExporter()

expect(exporter.headers['x-cypress-encrypted']).to.be.undefined
expect(exporter.requirementsToExport).to.equal('met')
expect(exporter.enc).to.be.undefined
})
})
Expand All @@ -42,15 +44,20 @@ describe('cloudSpanExporter', () => {
expect(callCount).to.equal(1)
})

it('does nothing if id is not passed', () => {
const exporter = new OTLPTraceExporter()
it('sets requirements to unmet if id is not passed', () => {
const exporter = new OTLPTraceExporter(genericRequest)

let callCount = 0
let abortCallCount = 0

exporter.setAuthorizationHeader = () => {
callCount++
}

exporter.abortDelayedItems = () => {
abortCallCount++
}

expect(exporter.headers['x-project-id']).to.be.undefined
expect(exporter.projectId).to.be.undefined

Expand All @@ -59,6 +66,9 @@ describe('cloudSpanExporter', () => {
expect(exporter.headers['x-project-id']).to.be.undefined
expect(exporter.projectId).to.be.undefined
expect(callCount).to.equal(0)

expect(exporter.requirementsToExport).to.equal('unmet')
expect(abortCallCount).to.equal(1)
})
})

Expand All @@ -80,21 +90,29 @@ describe('cloudSpanExporter', () => {
expect(callCount).to.equal(1)
})

it('does nothing if record key is not passed', () => {
const exporter = new OTLPTraceExporter()
it('sets requirements to unmet if record key is not passed', () => {
const exporter = new OTLPTraceExporter(genericRequest)

let callCount = 0
let abortCallCount = 0

exporter.setAuthorizationHeader = () => {
callCount++
}

exporter.abortDelayedItems = () => {
abortCallCount++
}

expect(exporter.recordKey).to.be.undefined

exporter.attachRecordKey(undefined)

expect(exporter.recordKey).to.be.undefined
expect(callCount).to.equal(0)

expect(exporter.requirementsToExport).to.equal('unmet')
expect(abortCallCount).to.equal(1)
})
})

Expand All @@ -109,10 +127,9 @@ describe('cloudSpanExporter', () => {

const authorization = exporter.headers.Authorization

console.log('auth', authorization)

// MTIzOjQ1Ng== is 123:456 base64 encoded
expect(authorization).to.equal(`Basic MTIzOjQ1Ng==`)
expect(exporter.requirementsToExport).to.equal('met')
})
})

Expand Down Expand Up @@ -206,6 +223,24 @@ describe('cloudSpanExporter', () => {
})
})

describe('abortDelayedItems', () => {
it('aborts any delayed items', (done) => {
const exporter = new OTLPTraceExporter()

exporter.delayedItemsToExport.push({
serviceRequest: 'req',
onSuccess: () => {},
onError: (error) => {
expect(error.message).to.equal('Spans cannot be sent, exporter has unmet requirements, either project id or record key are undefined.')
done()
},
})

exporter.abortDelayedItems()
expect(exporter.delayedItemsToExport.length).to.equal(0)
})
})

describe('send', () => {
it('returns if shutdownOnce.isCalled is true', () => {
const exporter = new OTLPTraceExporter()
Expand Down Expand Up @@ -395,5 +430,30 @@ describe('cloudSpanExporter', () => {
expect(exporter.delayedItemsToExport.length).to.equal(1)
expect(exporter.delayedItemsToExport[0].serviceRequest).to.equal('string')
})

it('errors if requirements are unmet', (done) => {
const exporter = new OTLPTraceExporter()

exporter.requirementsToExport = 'unmet'

exporter.convert = (objects) => {
throw 'convert should not be called'
}

exporter.sendWithHttp = (collector, body, contentType, resolve, reject) => {
throw 'sendWithHttp should not be called'
}

const onSuccess = () => {
throw 'onSuccess should not be called'
}

const onError = (error) => {
expect(error.message).to.equal('Spans cannot be sent, exporter has unmet requirements, either project id or record key are undefined.')
done()
}

exporter.send('string', onSuccess, onError)
})
})
})

5 comments on commit 3d1ec09

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3d1ec09 Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.13.1/linux-arm64/develop-3d1ec096262953b936f29a2dba0f4af57ac11b17/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3d1ec09 Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the linux x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.13.1/linux-x64/develop-3d1ec096262953b936f29a2dba0f4af57ac11b17/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3d1ec09 Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin arm64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.13.1/darwin-arm64/develop-3d1ec096262953b936f29a2dba0f4af57ac11b17/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3d1ec09 Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the win32 x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.13.1/win32-x64/develop-3d1ec096262953b936f29a2dba0f4af57ac11b17/cypress.tgz

@cypress-bot
Copy link
Contributor

@cypress-bot cypress-bot bot commented on 3d1ec09 Jun 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Circle has built the darwin x64 version of the Test Runner.

Learn more about this pre-release build at https://on.cypress.io/advanced-installation#Install-pre-release-version

Run this command to install the pre-release locally:

npm install https://cdn.cypress.io/beta/npm/12.13.1/darwin-x64/develop-3d1ec096262953b936f29a2dba0f4af57ac11b17/cypress.tgz

Please sign in to comment.