Skip to content

Commit

Permalink
Merge pull request KelvinTegelaar#2314 from JohnDuprey/dev
Browse files Browse the repository at this point in the history
Partner Center webhooks
  • Loading branch information
JohnDuprey authored Apr 11, 2024
2 parents 6ce20e0 + 5d0fc9e commit 2df3c74
Show file tree
Hide file tree
Showing 3 changed files with 288 additions and 9 deletions.
6 changes: 5 additions & 1 deletion src/components/layout/AppFooter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,11 @@ const AppFooter = () => {
<CLink className="me-2" href="https://rewst.io/" target="_blank">
<CImage src={rewst} alt="Rewst" />
</CLink>
<CLink className="me-2" href="https://www.augmentt.com" target="_blank">
<CLink
className="me-2"
href="https://www.augmentt.com/?utm_source=cipp&utm_medium=referral&utm_campaign=2024"
target="_blank"
>
<CImage src={augmentt} alt="Augmentt" />
</CLink>
<CLink className="me-2" href="https://ninjaone.com" target="_blank">
Expand Down
24 changes: 16 additions & 8 deletions src/views/cipp/app-settings/CIPPSettings.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { SettingsLicenses } from 'src/views/cipp/app-settings/SettingsLicenses.j
import { SettingsExtensions } from 'src/views/cipp/app-settings/SettingsExtensions.jsx'
import { SettingsMaintenance } from 'src/views/cipp/app-settings/SettingsMaintenance.jsx'
import { SettingsExtensionMappings } from 'src/views/cipp/app-settings/SettingsExtensionMappings.jsx'
import { SettingsPartner } from 'src/views/cipp/app-settings/SettingsPartner.jsx'

/**
* This function returns the settings page content for CIPP.
Expand All @@ -35,15 +36,18 @@ export default function CIPPSettings() {
Notifications
</CNavItem>
<CNavItem active={active === 5} onClick={() => setActive(5)} href="#">
Licenses
Partner Webhooks
</CNavItem>
<CNavItem active={active === 6} onClick={() => setActive(6)} href="#">
Maintenance
Licenses
</CNavItem>
<CNavItem active={active === 7} onClick={() => setActive(7)} href="#">
Extensions
Maintenance
</CNavItem>
<CNavItem active={active === 8} onClick={() => setActive(8)} href="#">
Extensions
</CNavItem>
<CNavItem active={active === 9} onClick={() => setActive(9)} href="#">
Extension Mappings
</CNavItem>
</CNav>
Expand All @@ -68,22 +72,26 @@ export default function CIPPSettings() {
<SettingsNotifications />
</CTabPane>
<CTabPane visible={active === 5} className="mt-3">
<CippLazy visible={active === 5}>
<SettingsLicenses />
</CippLazy>
<CippLazy visible={active === 5}></CippLazy>
<SettingsPartner />
</CTabPane>
<CTabPane visible={active === 6} className="mt-3">
<CippLazy visible={active === 6}>
<SettingsMaintenance />
<SettingsLicenses />
</CippLazy>
</CTabPane>
<CTabPane visible={active === 7} className="mt-3">
<CippLazy visible={active === 7}>
<SettingsExtensions />
<SettingsMaintenance />
</CippLazy>
</CTabPane>
<CTabPane visible={active === 8} className="mt-3">
<CippLazy visible={active === 8}>
<SettingsExtensions />
</CippLazy>
</CTabPane>
<CTabPane visible={active === 9} className="mt-3">
<CippLazy visible={active === 9}>
<SettingsExtensionMappings />
</CippLazy>
</CTabPane>
Expand Down
267 changes: 267 additions & 0 deletions src/views/cipp/app-settings/SettingsPartner.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,267 @@
import {
useGenericGetRequestQuery,
useLazyGenericGetRequestQuery,
useLazyGenericPostRequestQuery,
} from 'src/store/api/app.js'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import {
CBadge,
CButton,
CCallout,
CCard,
CCardBody,
CCardHeader,
CCardTitle,
CCol,
CForm,
CLink,
CRow,
CSpinner,
} from '@coreui/react'
import { Form } from 'react-final-form'
import { RFFSelectSearch } from 'src/components/forms/index.js'
import React, { useEffect } from 'react'
import { CippCallout } from 'src/components/layout/index.js'
import { CippCodeBlock } from 'src/components/utilities'
import { CellDate } from 'src/components/tables'

/**
* Sets the notification settings.
* @returns {JSX.Element} The notification settings component.
*/
export function SettingsPartner() {
const webhookConfig = useGenericGetRequestQuery({
path: '/api/ExecPartnerWebhook',
params: { Action: 'ListSubscription' },
})
const webhookEvents = useGenericGetRequestQuery({
path: '/api/ExecPartnerWebhook',
params: { Action: 'ListEventTypes' },
})
const [submitWebhook, webhookCreateResult] = useLazyGenericPostRequestQuery()
const [sendTest, sendTestResult] = useLazyGenericGetRequestQuery()
const [checkTest, checkTestResult] = useLazyGenericGetRequestQuery()

const onSubmit = (values) => {
const shippedValues = {
EventType: values?.EventType?.map((event) => event.value),
}
submitWebhook({
path: '/api/ExecPartnerWebhook?Action=CreateSubscription',
values: shippedValues,
}).then((res) => {
webhookConfig.refetch()
})
}

useEffect(() => {
if (
sendTestResult.isSuccess &&
sendTestResult?.data?.Results?.correlationId &&
!checkTestResult?.data?.Results?.results
) {
setTimeout(
checkTest({
path: '/api/ExecPartnerWebhook',
params: {
Action: 'ValidateTest',
CorrelationId: sendTestResult?.data?.Results?.correlationId,
},
}),
1000,
)
}
}, [sendTestResult, checkTest, checkTestResult])

return (
<CCard className="h-100">
<CCardHeader></CCardHeader>
<CCardBody>
<>
<CButton
size="sm"
onClick={() => webhookConfig.refetch()}
className="mb-2"
disabled={webhookConfig.isFetching}
>
{webhookConfig.isFetching ? (
<>
<CSpinner className="me-2" size="sm" /> Loading...
</>
) : (
<>
<FontAwesomeIcon icon="sync" className="me-2" />
Refresh
</>
)}
</CButton>

{!webhookConfig.isFetching && webhookConfig.error && (
<CippCallout color="danger">Error loading data</CippCallout>
)}
{webhookConfig.isSuccess && (
<>
<h3 className="underline mb-5"> Webhook Configuration</h3>
<CRow>
<CCol sm={12} md={6} lg={8} className="mb-3">
Subscribe to Microsoft Partner center webhooks to enable automatic tenant
onboarding and alerting. Updating the settings will replace any existing webhook
subscription with one pointing to CIPP. Refer to the{' '}
<CLink
href="https://learn.microsoft.com/en-us/partner-center/developer/partner-center-webhooks"
target="_blank"
>
Microsoft Partner Center documentation
</CLink>{' '}
for more information on the webhook types.
</CCol>
</CRow>
<CRow>
<CCol sm={12} md={6} className="mb-3">
<p className="fw-lighter">Webhook URL</p>
<CippCodeBlock
code={webhookConfig?.data?.Results?.webhookUrl ?? 'No webhook URL found'}
language="plain"
showLineNumbers={false}
/>
</CCol>
<CCol sm={12} md={6} className="mb-3">
<p className="fw-lighter">Last Updated</p>
<CellDate
cell={webhookConfig?.data?.Results?.lastModifiedTimestamp}
format="short"
/>
</CCol>
<CCol sm={12} md={12} className="mb-3">
<p className="fw-lighter">Subscribed Events</p>
<Form
onSubmit={onSubmit}
initialValues={{
EventType: webhookConfig?.data?.Results?.webhookEvents.map((event) => ({
label: event,
value: event,
})),
}}
render={({ handleSubmit }) => (
<>
<CForm onSubmit={handleSubmit}>
<RFFSelectSearch
name="EventType"
label="Event Types"
values={webhookEvents.data?.Results?.map((event) => ({
name: event,
value: event,
}))}
multi={true}
refreshFunction={() => webhookEvents.refetch()}
helpText="Select the events you want to receive notifications for."
/>
<CButton
type="submit"
color="primary"
className="my-3"
disabled={webhookCreateResult.isFetching}
>
{webhookCreateResult.isFetching ? (
<>
<CSpinner size="sm" className="me-2" />
Saving...
</>
) : (
'Save'
)}
</CButton>
</CForm>
</>
)}
/>
{webhookCreateResult.isSuccess && (
<CippCallout color="info" dismissible>
{webhookCreateResult?.data?.Results}
</CippCallout>
)}
</CCol>
</CRow>
<h3 className="underline mb-5">Webhook Test</h3>
<CRow>
<CCol sm={12} md={12} className="mb-3">
<CButton
className="me-3"
onClick={() =>
sendTest({
path: '/api/ExecPartnerWebhook',
params: { Action: 'SendTest' },
})
}
>
{sendTestResult.isFetching ? (
<>
<CSpinner size="sm" className="me-2" />
Running Test...
</>
) : (
'Start Test'
)}
</CButton>
{checkTestResult.isFetching && !checkTestResult?.data?.Results?.result && (
<>
<CSpinner size="sm" className="me-2" /> Waiting for results
</>
)}
</CCol>
</CRow>
<CRow>
{checkTestResult.isSuccess && (
<>
<CCol sm={12} md={4} className="mb-3">
<p className="fw-lighter">Status</p>
{checkTestResult?.data?.Results.status}
</CCol>
{Array.isArray(checkTestResult?.data?.Results?.results) && (
<>
<CCol sm={12} md={4} className="mb-3">
<p className="fw-lighter">Status Code</p>
<FontAwesomeIcon
icon={
checkTestResult?.data?.Results?.results[0].responseCode == 200
? 'check-circle'
: 'times-circle'
}
color={
checkTestResult?.data?.Results?.results[0].responseCode == 200
? 'green'
: 'red'
}
className="me-2"
/>
{checkTestResult?.data?.Results?.results[0].responseCode}
</CCol>
{checkTestResult?.data?.Results?.results[0].responseMessage !== '' && (
<CCol sm={12} md={4} className="mb-3">
<p className="fw-lighter">Response Message</p>
{checkTestResult.data.Results.results[0].responseMessage}
</CCol>
)}
<CCol sm={12} md={4} className="mb-3">
<p className="fw-lighter">Date/Time</p>
<CellDate
cell={Date(
Date.parse(
checkTestResult?.data?.Results?.results[0].dateTimeUtc + 'Z',
),
).toLocaleString()}
format="short"
/>
</CCol>
</>
)}
</>
)}
</CRow>
</>
)}
</>
</CCardBody>
</CCard>
)
}

0 comments on commit 2df3c74

Please sign in to comment.