mirror of
https://github.com/strapi/strapi.git
synced 2025-08-24 16:49:28 +00:00
feat(content-releases): add publish webhook (#19515)
* feat(content-releases): add publish webhook * apply Marks feedback
This commit is contained in:
parent
8d3598965d
commit
4af8963f68
@ -1 +1,5 @@
|
|||||||
module.exports = ({ env }) => ({});
|
module.exports = ({ env }) => ({
|
||||||
|
future: {
|
||||||
|
contentReleasesScheduling: env('STRAPI_FUTURE_CONTENT_RELEASES_SCHEDULING', false),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
@ -1,11 +1,21 @@
|
|||||||
import { Events } from '../../../../../../../../admin/src/pages/Settings/pages/Webhooks/components/Events';
|
import { Events } from '../../../../../../../../admin/src/pages/Settings/pages/Webhooks/components/Events';
|
||||||
|
|
||||||
const events = {
|
const eeTables = {
|
||||||
|
'review-workflows': {
|
||||||
'review-workflows': ['review-workflows.updateEntryStage'],
|
'review-workflows': ['review-workflows.updateEntryStage'],
|
||||||
|
},
|
||||||
|
releases: {
|
||||||
|
releases: ['releases.publish'],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const getHeaders = () => {
|
const getHeaders = (table: keyof typeof eeTables) => {
|
||||||
return [{ id: 'review-workflows.updateEntryStage', defaultMessage: 'Stage Change' }];
|
switch (table) {
|
||||||
|
case 'review-workflows':
|
||||||
|
return () => [{ id: 'review-workflows.updateEntryStage', defaultMessage: 'Stage Change' }];
|
||||||
|
case 'releases':
|
||||||
|
return () => [{ id: 'releases.publish', defaultMessage: 'Publish' }];
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const EventsTableEE = () => {
|
const EventsTableEE = () => {
|
||||||
@ -13,8 +23,12 @@ const EventsTableEE = () => {
|
|||||||
<Events.Root>
|
<Events.Root>
|
||||||
<Events.Headers />
|
<Events.Headers />
|
||||||
<Events.Body />
|
<Events.Body />
|
||||||
<Events.Headers getHeaders={getHeaders} />
|
{(Object.keys(eeTables) as Array<keyof typeof eeTables>).map((table) => (
|
||||||
<Events.Body providedEvents={events} />
|
<>
|
||||||
|
<Events.Headers getHeaders={getHeaders(table)} />
|
||||||
|
<Events.Body providedEvents={eeTables[table]} />
|
||||||
|
</>
|
||||||
|
))}
|
||||||
</Events.Root>
|
</Events.Root>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -84,6 +84,9 @@ describe('bootstrap', () => {
|
|||||||
log: {
|
log: {
|
||||||
error: jest.fn(),
|
error: jest.fn(),
|
||||||
},
|
},
|
||||||
|
webhookStore: {
|
||||||
|
addAllowedEvent: jest.fn(),
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||||
import type { LoadedStrapi, Entity as StrapiEntity } from '@strapi/types';
|
import type { LoadedStrapi, Entity as StrapiEntity } from '@strapi/types';
|
||||||
|
|
||||||
import { RELEASE_ACTION_MODEL_UID } from './constants';
|
import { ALLOWED_WEBHOOK_EVENTS, RELEASE_ACTION_MODEL_UID } from './constants';
|
||||||
import { getService } from './utils';
|
import { getService } from './utils';
|
||||||
|
|
||||||
const { features } = require('@strapi/strapi/dist/utils/ee');
|
const { features } = require('@strapi/strapi/dist/utils/ee');
|
||||||
@ -69,6 +69,10 @@ export const bootstrap = async ({ strapi }: { strapi: LoadedStrapi }) => {
|
|||||||
|
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Object.entries(ALLOWED_WEBHOOK_EVENTS).forEach(([key, value]) => {
|
||||||
|
strapi.webhookStore.addAllowedEvent(key, value);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -45,3 +45,7 @@ export const ACTIONS = [
|
|||||||
pluginName: 'content-releases',
|
pluginName: 'content-releases',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const ALLOWED_WEBHOOK_EVENTS = {
|
||||||
|
RELEASES_PUBLISH: 'releases.publish',
|
||||||
|
};
|
||||||
|
@ -26,6 +26,9 @@ const baseStrapiMock = {
|
|||||||
isEnabled: jest.fn().mockReturnValue(true),
|
isEnabled: jest.fn().mockReturnValue(true),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
eventHub: {
|
||||||
|
emit: jest.fn(),
|
||||||
|
},
|
||||||
telemetry: {
|
telemetry: {
|
||||||
send: jest.fn().mockReturnValue(true),
|
send: jest.fn().mockReturnValue(true),
|
||||||
},
|
},
|
||||||
|
@ -4,7 +4,7 @@ import type { LoadedStrapi, EntityService, UID, Schema } from '@strapi/types';
|
|||||||
|
|
||||||
import _ from 'lodash/fp';
|
import _ from 'lodash/fp';
|
||||||
|
|
||||||
import { RELEASE_ACTION_MODEL_UID, RELEASE_MODEL_UID } from '../constants';
|
import { ALLOWED_WEBHOOK_EVENTS, RELEASE_ACTION_MODEL_UID, RELEASE_MODEL_UID } from '../constants';
|
||||||
import type {
|
import type {
|
||||||
GetReleases,
|
GetReleases,
|
||||||
CreateRelease,
|
CreateRelease,
|
||||||
@ -48,7 +48,19 @@ const getGroupName = (queryValue?: ReleaseActionGroupBy) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
|
const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => {
|
||||||
|
const dispatchWebhook = (
|
||||||
|
event: string,
|
||||||
|
{ isPublished, release, error }: { isPublished: boolean; release?: Release; error?: unknown }
|
||||||
|
) => {
|
||||||
|
strapi.eventHub.emit(event, {
|
||||||
|
isPublished,
|
||||||
|
error,
|
||||||
|
release,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {
|
async create(releaseData: CreateRelease.Request['body'], { user }: { user: UserInfo }) {
|
||||||
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
const releaseWithCreatorFields = await setCreatorFields({ user })(releaseData);
|
||||||
|
|
||||||
@ -201,7 +213,9 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
|
|||||||
releaseData: UpdateRelease.Request['body'],
|
releaseData: UpdateRelease.Request['body'],
|
||||||
{ user }: { user: UserInfo }
|
{ user }: { user: UserInfo }
|
||||||
) {
|
) {
|
||||||
const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(releaseData);
|
const releaseWithCreatorFields = await setCreatorFields({ user, isEdition: true })(
|
||||||
|
releaseData
|
||||||
|
);
|
||||||
|
|
||||||
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
const { validateUniqueNameForPendingRelease, validateScheduledAtIsLaterThanNow } = getService(
|
||||||
'release-validation',
|
'release-validation',
|
||||||
@ -315,7 +329,9 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
async countActions(query: EntityService.Params.Pick<typeof RELEASE_ACTION_MODEL_UID, 'filters'>) {
|
async countActions(
|
||||||
|
query: EntityService.Params.Pick<typeof RELEASE_ACTION_MODEL_UID, 'filters'>
|
||||||
|
) {
|
||||||
return strapi.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
return strapi.entityService.count(RELEASE_ACTION_MODEL_UID, query);
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -409,12 +425,17 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async getAllComponents() {
|
async getAllComponents() {
|
||||||
const contentManagerComponentsService = strapi.plugin('content-manager').service('components');
|
const contentManagerComponentsService = strapi
|
||||||
|
.plugin('content-manager')
|
||||||
|
.service('components');
|
||||||
|
|
||||||
const components = await contentManagerComponentsService.findAllComponents();
|
const components = await contentManagerComponentsService.findAllComponents();
|
||||||
|
|
||||||
const componentsMap = components.reduce(
|
const componentsMap = components.reduce(
|
||||||
(acc: { [key: Schema.Component['uid']]: Schema.Component }, component: Schema.Component) => {
|
(
|
||||||
|
acc: { [key: Schema.Component['uid']]: Schema.Component },
|
||||||
|
component: Schema.Component
|
||||||
|
) => {
|
||||||
acc[component.uid] = component;
|
acc[component.uid] = component;
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
@ -466,6 +487,7 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
|
|||||||
},
|
},
|
||||||
|
|
||||||
async publish(releaseId: PublishRelease.Request['params']['id']) {
|
async publish(releaseId: PublishRelease.Request['params']['id']) {
|
||||||
|
try {
|
||||||
// We need to pass the type because entityService.findOne is not returning the correct type
|
// We need to pass the type because entityService.findOne is not returning the correct type
|
||||||
const releaseWithPopulatedActionEntries = (await strapi.entityService.findOne(
|
const releaseWithPopulatedActionEntries = (await strapi.entityService.findOne(
|
||||||
RELEASE_MODEL_UID,
|
RELEASE_MODEL_UID,
|
||||||
@ -615,7 +637,7 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
// When the transaction fails it throws an error, when it is successful proceed to updating the release
|
// When the transaction fails it throws an error, when it is successful proceed to updating the release
|
||||||
const release = await strapi.entityService.update(RELEASE_MODEL_UID, releaseId, {
|
const release = (await strapi.entityService.update(RELEASE_MODEL_UID, releaseId, {
|
||||||
data: {
|
data: {
|
||||||
/*
|
/*
|
||||||
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
|
* The type returned from the entity service: Partial<Input<"plugin::content-releases.release">> looks like it's wrong
|
||||||
@ -623,11 +645,34 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
|
|||||||
// @ts-expect-error see above
|
// @ts-expect-error see above
|
||||||
releasedAt: new Date(),
|
releasedAt: new Date(),
|
||||||
},
|
},
|
||||||
|
populate: {
|
||||||
|
actions: {
|
||||||
|
// @ts-expect-error is not expecting count but it is working
|
||||||
|
count: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})) as Release;
|
||||||
|
|
||||||
|
if (strapi.features.future.isEnabled('contentReleasesScheduling')) {
|
||||||
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
||||||
|
isPublished: true,
|
||||||
|
release,
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
strapi.telemetry.send('didPublishContentRelease');
|
strapi.telemetry.send('didPublishContentRelease');
|
||||||
|
|
||||||
return release;
|
return release;
|
||||||
|
} catch (error) {
|
||||||
|
if (strapi.features.future.isEnabled('contentReleasesScheduling')) {
|
||||||
|
dispatchWebhook(ALLOWED_WEBHOOK_EVENTS.RELEASES_PUBLISH, {
|
||||||
|
isPublished: false,
|
||||||
|
error,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateAction(
|
async updateAction(
|
||||||
@ -681,6 +726,7 @@ const createReleaseService = ({ strapi }: { strapi: LoadedStrapi }) => ({
|
|||||||
|
|
||||||
return deletedAction;
|
return deletedAction;
|
||||||
},
|
},
|
||||||
});
|
};
|
||||||
|
};
|
||||||
|
|
||||||
export default createReleaseService;
|
export default createReleaseService;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user