diff --git a/.woodpecker/buildSlackNotify.sh b/.woodpecker/buildSlackNotify.sh index 026138f..a3eb9c0 100644 --- a/.woodpecker/buildSlackNotify.sh +++ b/.woodpecker/buildSlackNotify.sh @@ -4,8 +4,10 @@ set -x COMMIT_SHORT_SHA=$(echo $CI_COMMIT_SHA | cut -c1-8) +STATUS=${1} -if [ "$CI_STEP_STATUS" = "success" ]; then + +if [ "$STATUS" = "success" ]; then MESSAGE="Did a build without issues on \`$CI_REPO_NAME/$CI_COMMIT_BRANCH\`. Commit: _${CI_COMMIT_MESSAGE}_ (<$CI_COMMIT_URL|$COMMIT_SHORT_SHA>)" curl -s -X POST -H "Content-Type: application/json" -d '{ diff --git a/.woodpecker/release.yml b/.woodpecker/release.yml index 138bc4b..dbd6916 100644 --- a/.woodpecker/release.yml +++ b/.woodpecker/release.yml @@ -16,25 +16,42 @@ steps: commands: - infisical export --domain https://vault.devforth.io/api --format=dotenv-export --env="prod" > /woodpecker/deploy.vault.env - release: + build: image: node:20 when: - event: push commands: - apt update && apt install -y rsync - - export $(cat /woodpecker/deploy.vault.env | xargs) + - . /woodpecker/deploy.vault.env - npm clean-install - /bin/bash ./.woodpecker/buildRelease.sh - npm audit signatures + + release: + image: node:20 + when: + - event: + - push + branch: + - main + commands: + - . /woodpecker/deploy.vault.env - npx semantic-release slack-on-failure: + image: curlimages/curl when: - event: push - status: [failure, success] - - event: push + status: [failure] + commands: + - . /woodpecker/deploy.vault.env + - /bin/sh ./.woodpecker/buildSlackNotify.sh failure + + slack-on-success: image: curlimages/curl + when: + - event: push + status: [success] commands: - - export $(cat /woodpecker/deploy.vault.env | xargs) - - /bin/sh ./.woodpecker/buildSlackNotify.sh - + - . /woodpecker/deploy.vault.env + - /bin/sh ./.woodpecker/buildSlackNotify.sh success \ No newline at end of file diff --git a/custom/InlineList.vue b/custom/InlineList.vue index 2de8e79..b4c2b93 100644 --- a/custom/InlineList.vue +++ b/custom/InlineList.vue @@ -364,9 +364,11 @@ onMounted( async () => { } const foreighResourceId = props.meta.foreignResourceId; listResource.value = (await callAdminForthApi({ - path: `/plugin/${props.meta.pluginInstanceId}/get_resource`, + path: `/get_resource`, method: 'POST', - body: {}, + body: { + resourceId: foreighResourceId, + }, })).resource; if (listResource.value?.options?.allowedActions?.create && listResourceRefColumn.value && !listResourceRefColumn.value.showIn.create) { diff --git a/index.ts b/index.ts index f6197c6..7a3ea50 100644 --- a/index.ts +++ b/index.ts @@ -11,9 +11,12 @@ import { interpretResource, ActionCheckSource } from "adminforth"; export default class ForeignInlineListPlugin extends AdminForthPlugin { foreignResource: AdminForthResource; + copyOfForeignResource: AdminForthResource; options: PluginOptions; adminforth: IAdminForth; + activationOrder: number = -10000000; + constructor(options: PluginOptions) { super(options, import.meta.url); this.options = options; @@ -24,81 +27,6 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { } setupEndpoints(server: IHttpServer) { - process.env.HEAVY_DEBUG && console.log(`🪲 ForeignInlineListPlugin.setupEndpoints, registering: '/plugin/${this.pluginInstanceId}/get_resource'`); - server.endpoint({ - method: 'POST', - path: `/plugin/${this.pluginInstanceId}/get_resource`, - handler: async ({ body, adminUser }) => { - const resource = this.adminforth.config.resources.find((res) => this.options.foreignResourceId === res.resourceId); - if (!resource) { - return { error: `Resource ${this.options.foreignResourceId} not found` }; - } - // exclude "plugins" key - const resourceCopy = clone({ ...resource, plugins: undefined }); - - if (this.options.modifyTableResourceConfig) { - this.options.modifyTableResourceConfig(resourceCopy); - } - - const { allowedActions } = await interpretResource(adminUser, resourceCopy, {}, ActionCheckSource.DisplayButtons, this.adminforth); - - return { - resource: { - ...resourceCopy, - options: { - ...resourceCopy.options, - allowedActions, - }, - } - }; - } - }); - server.endpoint({ - method: 'POST', - path: `/plugin/${this.pluginInstanceId}/start_bulk_action`, - handler: async ({ body, adminUser, tr }) => { - const { resourceId, actionId, recordIds } = body; - const resource = this.adminforth.config.resources.find((res) => res.resourceId == resourceId); - if (!resource) { - return { error: await tr(`Resource {resourceId} not found`, 'errors', { resourceId }) }; - } - - const resourceCopy = JSON.parse(JSON.stringify({ ...resource, plugins: undefined })); - - - if (this.options.modifyTableResourceConfig) { - this.options.modifyTableResourceConfig(resourceCopy); - } - - const { allowedActions } = await interpretResource( - adminUser, - resourceCopy, - { requestBody: body }, - ActionCheckSource.BulkActionRequest, - this.adminforth - ); - - const action = resourceCopy.options.bulkActions.find((act) => act.id == actionId); - if (!action) { - return { error: await tr(`Action {actionId} not found`, 'errors', { actionId }) }; - } - - if (action.allowed) { - const execAllowed = await action.allowed({ adminUser, resourceCopy, selectedIds: recordIds, allowedActions }); - if (!execAllowed) { - return { error: await tr(`Action "{actionId}" not allowed`, 'errors', { actionId: action.label }) }; - } - } - const response = await action.action({selectedIds: recordIds, adminUser, resourceCopy, tr}); - - return { - actionId, - recordIds, - resourceId, - ...response - } - } - }) server.endpoint({ method: 'POST', path: `/plugin/${this.pluginInstanceId}/get_default_filters`, @@ -119,19 +47,25 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { }) } - async modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource) { + async modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource, allPluginInstances?: {pi: AdminForthPlugin, resource: AdminForthResource}[]) { super.modifyResourceConfig(adminforth, resourceConfig); - this.adminforth = adminforth; - // get resource with foreignResourceId + this.adminforth = adminforth; this.foreignResource = adminforth.config.resources.find((resource) => resource.resourceId === this.options.foreignResourceId); + if (!this.foreignResource) { const similar = suggestIfTypo(adminforth.config.resources.map((res) => res.resourceId), this.options.foreignResourceId); throw new Error(`ForeignInlineListPlugin: Resource with ID "${this.options.foreignResourceId}" not found. ${similar ? `Did you mean "${similar}"?` : ''}`); } + const idOfNewCopy = `${this.foreignResource.resourceId}_inline_list__from_${this.resourceConfig.resourceId}__`; + let foreignResourceIdForColumn; + if (this.options.__inlineListParentResourceId && this.options.__inlineListParentResourceId === this.pluginOptions.foreignResourceId && this.resourceConfig.resourceId.includes('_inline_list__from_')) { + foreignResourceIdForColumn = this.options.__inlineListParentResourceId; + } else { + foreignResourceIdForColumn = idOfNewCopy; + } - const defaultSort = this.foreignResource.options?.defaultSort; const newColumn = { name: `foreignInlineList_${this.foreignResource.resourceId}`, label: 'Foreign Inline List', @@ -151,15 +85,7 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { ...this.options, pluginInstanceId: this.pluginInstanceId, disableForeignListResourceRefColumn: this.options.disableForeignListResourceRefColumn, - ...(defaultSort - ? { - defaultSort: { - field: defaultSort.columnName, - direction: defaultSort.direction, - } - } - : {} - ) + foreignResourceId: foreignResourceIdForColumn } } }, @@ -202,5 +128,67 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { } else { resourceConfig.columns.push(newColumn); } + + // get resource with foreignResourceId + this.copyOfForeignResource = clone({ ...this.foreignResource, plugins: [] }); + + // if we install on plugin which is already a copy, adjust foreignResource references + if (this.resourceConfig.resourceId.includes('_inline_list__from_')) { + const originalResourceIdPart = this.resourceConfig.resourceId.split('_inline_list__from_')[0]; + // find column in copied resource which is foreignResource.resourceId equal to originalResourceIdPart + // and change it to point to this.resourceConfig.resourceId + const foreignRefColumn = this.copyOfForeignResource.columns.find(col => col.foreignResource?.resourceId === originalResourceIdPart); + if (foreignRefColumn) { + foreignRefColumn.foreignResource.resourceId = this.resourceConfig.resourceId; + } + } + + // if foreignInlineList_ column already created, remove it + this.copyOfForeignResource.columns = this.copyOfForeignResource.columns.filter(col => !col.name.startsWith('foreignInlineList_')); + // we should not cate about modifications made by other plugins, while activationOrder of this plugin is very low (negative) + + this.copyOfForeignResource.resourceId = idOfNewCopy; + adminforth.config.resources.push(this.copyOfForeignResource); + + if (this.options.modifyTableResourceConfig) { + this.options.modifyTableResourceConfig(this.copyOfForeignResource); + } + + let shouldRefColumnBeUpdated = false; + // now we need to create a copy of all plugins of foreignResource, + for (const plugin of this.foreignResource.plugins || []) { + const options = { + ...plugin.pluginOptions, + __inlineListParentResourceId: this.resourceConfig.resourceId, + }; + // call constructor + if ( plugin.constructor.name === 'ForeignInlineListPlugin' ) { + + if (plugin.pluginOptions.foreignResourceId === this.foreignResource.resourceId && !this.resourceConfig.resourceId.includes('_inline_list__from_')) { + // TODO delete copyOfForeignResource from adminforth.config.resources, because we are don't use this copy anymore + plugin.pluginOptions.foreignResourceId = idOfNewCopy; + const pluginCopy = new (plugin.constructor as any)(options); + this.copyOfForeignResource.plugins.push(pluginCopy); + const currentResourceForeignRefColumn = this.resourceConfig.columns.find(col => col.foreignResource?.resourceId === this.resourceConfig.resourceId); + if (currentResourceForeignRefColumn) { + currentResourceForeignRefColumn.foreignResource.resourceId = idOfNewCopy; + } + shouldRefColumnBeUpdated = true; + } else if (!plugin.pluginOptions.foreignResourceId.includes('_inline_list__from_')) { + const pluginCopy = new (plugin.constructor as any)(options); + this.copyOfForeignResource.plugins.push(pluginCopy); + } + } else { + const pluginCopy = new (plugin.constructor as any)(options); + this.copyOfForeignResource.plugins.push(pluginCopy); + } + } + + // activate plugins for the copyOfForeignResource + for (const plugin of this.copyOfForeignResource.plugins.sort((a, b) => a.activationOrder - b.activationOrder)) { + // if there already is a plugin with same instanceUniqueRepresentation, skip + process.env.HEAVY_DEBUG && console.log('Activating plugin for foreign inline list copy:', plugin.constructor.name); + allPluginInstances.push({pi: plugin, resource: this.copyOfForeignResource}); + } } } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b34a572..1740f96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ "typescript": "^5.7.3" }, "peerDependencies": { - "adminforth": "^2.13.0-next.51" + "adminforth": "^2.16.1-next.4" } }, "../..": { @@ -1076,9 +1076,9 @@ } }, "node_modules/adminforth": { - "version": "2.13.0-next.51", - "resolved": "https://registry.npmjs.org/adminforth/-/adminforth-2.13.0-next.51.tgz", - "integrity": "sha512-hNWloIvFNNEwaWAfDaUJsF3MZZBKZKAF14LHkXG6cd2wBO/kbWrvdbw1t2WK/U18/hVUJ7QlI0/e+8P8TXGcSg==", + "version": "2.16.1-next.4", + "resolved": "https://registry.npmjs.org/adminforth/-/adminforth-2.16.1-next.4.tgz", + "integrity": "sha512-1E4FElDFO/423V7uJ4zqUk7TFQnUaErqgV2UtXqsJXqqD/4LVurMC1rXDpl6Ni+bUucbnvFHpKSGaRxe6Lj8rA==", "hasInstallScript": true, "license": "ISC", "peer": true, diff --git a/package.json b/package.json index 466ec9f..344cb3c 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,6 @@ "clone": "^2.1.2" }, "peerDependencies": { - "adminforth": "^2.13.0-next.51" + "adminforth": "^2.16.1-next.4" } } diff --git a/types.ts b/types.ts index 27b5254..894c2b1 100644 --- a/types.ts +++ b/types.ts @@ -34,4 +34,7 @@ export type PluginOptions = { defaultFilters?: (record: any) => FilterParams[]; disableForeignListResourceRefColumn?: boolean; + + // Internal use only. + __inlineListParentResourceId?: string; }