Skip to content

Commit

Permalink
feat(import): send import results to discord (#6662)
Browse files Browse the repository at this point in the history
This PR adds the ability to send github import results (errors/warnings)
to Discord (to the channel `#site-import-webhook`).

**Details:**
1. Added a generic API endpoint for sending messages to Discord from our
code. We use `discord-webhook-node` to format the message. The generic
logic is here:
[utopia-remix/app/util/discordWebhookUtils.ts](https://github.com/concrete-utopia/utopia/pull/6662/files#diff-77fb1f38c2e0b74476d2dac0b094d0c3f56cd598ca46f5ba2f348dd1afd8c4ae)
2. The code in the client that sends the results, here:
[editor/src/core/shared/import/import-operation-service.ts](https://github.com/concrete-utopia/utopia/pull/6662/files#diff-426bff74c9da628f136029668cad91728508ef49a539c96a379c8c1e3f70e150R250-R265)
3. The code in the server that builds the textual site import message
here:
[utopia-remix/app/handlers/discordMessageBuilder.ts](https://github.com/concrete-utopia/utopia/pull/6662/files#diff-d2102489b75734896e5f1e06678c3f0ca926650eb08cdd14e00e79315f947302R22-R80)

No other logic was changed, the rest of the code changes are mainly
refactors / types.

The code is generic and we can later
[add](https://github.com/concrete-utopia/utopia/pull/6662/files#diff-77fb1f38c2e0b74476d2dac0b094d0c3f56cd598ca46f5ba2f348dd1afd8c4aeR20-R22)
more Discord webhooks for other functionalities, easily.
The webhook url for the specific channel is hidden in an env var -
`DISCORD_WEBHOOK_SITE_IMPORT`, it is already set on our
staging/production servers, and currently not on local machines.

**Messages Example:**

<img width="434" alt="image"
src="https://github.com/user-attachments/assets/3b991dc1-f315-48b0-bccf-1d3df66591af">

**Manual Tests:**
I hereby swear that:

- [X] I opened a hydrogen project and it loaded
- [X] I could navigate to various routes in Play mode
  • Loading branch information
liady authored Nov 21, 2024
1 parent 80561fc commit a4cf331
Show file tree
Hide file tree
Showing 19 changed files with 545 additions and 76 deletions.
16 changes: 11 additions & 5 deletions editor/src/components/editor/actions/actions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,10 @@ import { canCondenseJSXElementChild } from '../../../utils/can-condense'
import { getNavigatorTargetsFromEditorState } from '../../navigator/navigator-utils'
import { getParseCacheOptions } from '../../../core/shared/parse-cache-utils'
import { styleP } from '../../inspector/inspector-common'
import { getUpdateOperationResult } from '../../../core/shared/import/import-operation-service'
import {
getUpdateOperationResult,
notifyImportStatusToDiscord,
} from '../../../core/shared/import/import-operation-service'
import { updateRequirements } from '../../../core/shared/import/project-health-check/utopia-requirements-service'
import {
applyValuesAtPath,
Expand Down Expand Up @@ -2191,12 +2194,15 @@ export const UPDATE_FNS = {
}
},
UPDATE_IMPORT_STATUS: (action: UpdateImportStatus, editor: EditorModel): EditorModel => {
const newImportState = {
...editor.importState,
importStatus: action.importStatus,
}
// side effect ☢️
notifyImportStatusToDiscord(newImportState, editor.projectName)
return {
...editor,
importState: {
...editor.importState,
importStatus: action.importStatus,
},
importState: newImportState,
}
},
UPDATE_PROJECT_REQUIREMENTS: (
Expand Down
49 changes: 2 additions & 47 deletions editor/src/components/editor/import-wizard/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import type {
ImportOperation,
} from '../../../core/shared/import/import-operation-types'
import { ImportOperationResult } from '../../../core/shared/import/import-operation-types'
import { assertNever } from '../../../core/shared/utils'
import { Icn, Icons, useColorTheme } from '../../../uuiui'
import { GithubSpinner } from '../../../components/navigator/left-pane/github-pane/github-spinner'
import { getImportOperationTextAsJsx } from './import-wizard-helpers'

export function OperationLine({ operation }: { operation: ImportOperation }) {
const operationRunningStatus = React.useMemo(() => {
Expand Down Expand Up @@ -46,7 +46,7 @@ export function OperationLine({ operation }: { operation: ImportOperation }) {
>
<OperationLineContent textColor={textColor}>
<OperationIcon runningStatus={operationRunningStatus} result={operation.result} />
<div>{getImportOperationText(operation)}</div>
<div>{getImportOperationTextAsJsx(operation)}</div>
<div>
<TimeFromInSeconds operation={operation} runningStatus={operationRunningStatus} />
</div>
Expand Down Expand Up @@ -246,48 +246,3 @@ function OperationLineContent({
</div>
)
}

function getImportOperationText(operation: ImportOperation): React.ReactNode {
if (operation.text != null) {
return operation.text
}
switch (operation.type) {
case 'loadBranch':
if (operation.branchName != null) {
return (
<span>
Fetching branch{' '}
<strong>
{operation.githubRepo?.owner}/{operation.githubRepo?.repository}@
{operation.branchName}
</strong>
</span>
)
} else {
return (
<span>
Fetching repository{' '}
<strong>
{operation.githubRepo?.owner}/{operation.githubRepo?.repository}
</strong>
</span>
)
}
case 'fetchDependency':
return `Fetching ${operation.dependencyName}@${operation.dependencyVersion}`
case 'parseFiles':
return 'Parsing files'
case 'refreshDependencies':
return 'Fetching dependencies'
case 'checkRequirementsPreParse':
return 'Validating code'
case 'checkRequirementsPostParse':
return 'Checking Utopia requirements'
case 'checkRequirementAndFixPreParse':
return operation.text
case 'checkRequirementAndFixPostParse':
return operation.text
default:
assertNever(operation)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as React from 'react'
import {
ImportOperationResult,
type ImportOperation,
} from '../../../core/shared/import/import-operation-types'
import { assertNever } from '../../../core/shared/utils'

export function getImportOperationText(operation: ImportOperation): string {
if (operation.text != null) {
return operation.text
}
switch (operation.type) {
case 'loadBranch':
const action =
operation.result == ImportOperationResult.Error ||
operation.result == ImportOperationResult.CriticalError
? 'Error Fetching'
: 'Fetching'
if (operation.branchName != null) {
return `${action} branch **${operation.githubRepo?.owner}/${operation.githubRepo?.repository}@${operation.branchName}**`
} else {
return `${action} repository **${operation.githubRepo?.owner}/${operation.githubRepo?.repository}**`
}
case 'fetchDependency':
return `Fetching ${operation.dependencyName}@${operation.dependencyVersion}`
case 'parseFiles':
return 'Parsing files'
case 'refreshDependencies':
return 'Fetching dependencies'
case 'checkRequirementsPreParse':
return 'Validating code'
case 'checkRequirementsPostParse':
return 'Checking Utopia requirements'
case 'checkRequirementAndFixPreParse':
return operation.text
case 'checkRequirementAndFixPostParse':
return operation.text
default:
assertNever(operation)
}
}

export function getImportOperationTextAsJsx(operation: ImportOperation): React.ReactNode {
const text = getImportOperationText(operation)
const nodes = text.split('**').map((part, index) => {
return index % 2 == 0 ? part : <strong key={part}>{part}</strong>
})
return <span>{nodes}</span>
}
32 changes: 24 additions & 8 deletions editor/src/components/editor/import-wizard/import-wizard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { unless, when } from '../../../utils/react-conditionals'
import {
getTotalImportStatusAndResult,
hideImportWizard,
notifyImportStatusToDiscord,
updateProjectImportStatus,
} from '../../../core/shared/import/import-operation-service'
import { OperationLine } from './components'
Expand Down Expand Up @@ -45,11 +46,6 @@ export const ImportWizard = React.memo(() => {
e.stopPropagation()
}, [])

const totalImportResult: TotalImportResult = React.useMemo(
() => getTotalImportStatusAndResult(importState),
[importState],
)

if (projectId == null) {
return null
}
Expand Down Expand Up @@ -130,7 +126,7 @@ export const ImportWizard = React.memo(() => {
gap: 10,
}}
>
<ActionButtons importResult={totalImportResult} />
<ActionButtons />
</div>
</div>,
)}
Expand All @@ -139,7 +135,23 @@ export const ImportWizard = React.memo(() => {
})
ImportWizard.displayName = 'ImportWizard'

function ActionButtons({ importResult }: { importResult: TotalImportResult }) {
function ActionButtons() {
const importState = useEditorState(
Substores.github,
(store) => store.editor.importState,
'ImportWizard importState',
)

const projectName = useEditorState(
Substores.restOfEditor,
(store) => store.editor.projectName,
'ImportWizard projectName',
)

const importResult: TotalImportResult = React.useMemo(
() => getTotalImportStatusAndResult(importState),
[importState],
)
const colorTheme = useColorTheme()
const dispatch = useDispatch()
const result = importResult.result
Expand Down Expand Up @@ -174,6 +186,10 @@ function ActionButtons({ importResult }: { importResult: TotalImportResult }) {
}
}, [dispatch, hideWizard, importResult.importStatus])
const importADifferentProject = React.useCallback(() => {
if (importResult.importStatus.status !== 'done') {
// force a notification to discord that the import was exited in the middle
notifyImportStatusToDiscord(importState, projectName, true)
}
dispatch(
[
setImportWizardOpen(false),
Expand All @@ -182,7 +198,7 @@ function ActionButtons({ importResult }: { importResult: TotalImportResult }) {
],
'everyone',
)
}, [dispatch])
}, [dispatch, importResult.importStatus.status, importState, projectName])
const textStyle = {
color: textColor,
fontSize: 14,
Expand Down
17 changes: 17 additions & 0 deletions editor/src/components/editor/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ import { assertNever } from '../../core/shared/utils'
import { checkOnlineState } from './online-status'
import type { GithubOperationContext } from '../../core/shared/github/operations/github-operation-context'
import { GithubEndpoints } from '../../core/shared/github/endpoints'
import type { DiscordEndpointPayload } from 'utopia-shared/src/types'

export { fetchProjectList, fetchShowcaseProjects, getLoginState } from '../../common/server'

Expand Down Expand Up @@ -761,3 +762,19 @@ export function getBranchProjectContents(operationContext: GithubOperationContex
return response.json()
}
}

export async function sendDiscordMessage(payload: DiscordEndpointPayload) {
try {
const response = await fetch(`/internal/discord/webhook`, {
method: 'POST',
credentials: 'include',
mode: MODE,
body: JSON.stringify(payload),
})
if (!response.ok) {
console.error(`Send Discord message failed (${response.status}): ${response.statusText}`)
}
} catch (e) {
console.error(`Send Discord message failed: ${e}`)
}
}
12 changes: 7 additions & 5 deletions editor/src/components/github/github-repository-clone-flow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,10 @@ import { OperationContext } from '../../core/shared/github/operations/github-ope
import { notice } from '../common/notice'
import { isFeatureEnabled } from '../../utils/feature-switches'
import {
notifyOperationCriticalError,
notifyOperationFinished,
startImportProcess,
updateProjectImportStatus,
} from '../../core/shared/import/import-operation-service'
import { ImportOperationResult } from '../../core/shared/import/import-operation-types'

Expand Down Expand Up @@ -131,11 +133,11 @@ async function cloneGithubRepo(
if (repositoryEntry == null) {
if (isFeatureEnabled('Import Wizard')) {
startImportProcess(dispatch)
notifyOperationFinished(
dispatch,
{ type: 'loadBranch', branchName: githubRepo.branch ?? undefined, githubRepo: githubRepo },
ImportOperationResult.CriticalError,
)
notifyOperationCriticalError(dispatch, {
type: 'loadBranch',
branchName: githubRepo.branch ?? undefined,
githubRepo: githubRepo,
})
} else {
dispatch([showToast(notice('Cannot find repository', 'ERROR'))])
}
Expand Down
14 changes: 4 additions & 10 deletions editor/src/core/shared/github/operations/load-branch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ import { GithubOperations } from '.'
import { assertNever } from '../../utils'
import { updateProjectContentsWithParseResults } from '../../parser-projectcontents-utils'
import {
notifyOperationCriticalError,
notifyOperationFinished,
notifyOperationStarted,
pauseImport,
startImportProcess,
updateProjectImportStatus,
} from '../../import/import-operation-service'
import {
RequirementResolutionResult,
Expand Down Expand Up @@ -165,21 +167,13 @@ export const updateProjectWithBranchContent =
switch (responseBody.type) {
case 'FAILURE':
if (isFeatureEnabled('Import Wizard')) {
notifyOperationFinished(
dispatch,
{ type: 'loadBranch' },
ImportOperationResult.CriticalError,
)
notifyOperationCriticalError(dispatch, { type: 'loadBranch' })
}
throw githubAPIError(operation, responseBody.failureReason)
case 'SUCCESS':
if (responseBody.branch == null) {
if (isFeatureEnabled('Import Wizard')) {
notifyOperationFinished(
dispatch,
{ type: 'loadBranch' },
ImportOperationResult.CriticalError,
)
notifyOperationCriticalError(dispatch, { type: 'loadBranch' })
}
throw githubAPIError(operation, `Could not find branch ${branchName}`)
}
Expand Down
Loading

0 comments on commit a4cf331

Please sign in to comment.