From 96f161da5c4da61ea8f01a986fd4b206af76759e Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Tue, 23 Dec 2025 13:24:36 +0200 Subject: [PATCH 1/5] fix: prevent foreignResourceId from matching resourceConfig resourceId and add logging for resource copy creation --- index.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/index.ts b/index.ts index 9914caa..11c47dc 100644 --- a/index.ts +++ b/index.ts @@ -57,6 +57,9 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { 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}"?` : ''}`); } + if (this.foreignResource.resourceId === resourceConfig.resourceId) { + throw new Error(`TEMPORARY ERROR ForeignInlineListPlugin: foreignResourceId "${this.options.foreignResourceId}" cannot be the same as the resource where the plugin is installed ("${resourceConfig.resourceId}")`); + } const idOfNewCopy = `${this.foreignResource.resourceId}_inline_list__from_${this.resourceConfig.resourceId}__`; @@ -124,6 +127,7 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { } // get resource with foreignResourceId + console.log('Creating copy of foreign resource', this.foreignResource.resourceId, 'as', idOfNewCopy); this.copyOfForeignResource = clone({ ...this.foreignResource, plugins: [] }); // if we install on plugin which is already a copy, adjust foreignResource references @@ -162,6 +166,7 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { if (plugin.modifyResourceConfig) { await plugin.modifyResourceConfig(adminforth, this.copyOfForeignResource); } + console.log(`🤮🤮🤮🤮🤮🤮🤮🤮🤮🤮🤮Activating plugin ${plugin.pluginInstanceId} for resource ${this.copyOfForeignResource.resourceId}`); if (plugin.setupEndpoints) { await plugin.setupEndpoints(adminforth.express); } From aa98b33d340213d14b43c6994bc52afa65c24fd8 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Tue, 23 Dec 2025 16:47:12 +0200 Subject: [PATCH 2/5] fix: update API call to use simplified path and include resourceId in request body --- custom/InlineList.vue | 6 ++- index.ts | 107 ++++-------------------------------------- 2 files changed, 12 insertions(+), 101 deletions(-) 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 8047663..5b9bd7a 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,7 @@ 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 - } - } - }) + console.log("Setting up endpoints for plugin", this.pluginInstanceId); server.endpoint({ method: 'POST', path: `/plugin/${this.pluginInstanceId}/get_default_filters`, @@ -122,20 +51,15 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { async modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource) { super.modifyResourceConfig(adminforth, resourceConfig); this.adminforth = adminforth; - - // get resource with foreignResourceId 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}"?` : ''}`); } - if (this.foreignResource.resourceId === resourceConfig.resourceId) { - throw new Error(`TEMPORARY ERROR ForeignInlineListPlugin: foreignResourceId "${this.options.foreignResourceId}" cannot be the same as the resource where the plugin is installed ("${resourceConfig.resourceId}")`); - } const idOfNewCopy = `${this.foreignResource.resourceId}_inline_list__from_${this.resourceConfig.resourceId}__`; - const defaultSort = this.foreignResource.options?.defaultSort; const newColumn = { name: `foreignInlineList_${this.foreignResource.resourceId}`, label: 'Foreign Inline List', @@ -155,15 +79,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: idOfNewCopy } } }, @@ -208,7 +124,6 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { } // get resource with foreignResourceId - console.log('Creating copy of foreign resource', this.foreignResource.resourceId, 'as', idOfNewCopy); this.copyOfForeignResource = clone({ ...this.foreignResource, plugins: [] }); // if we install on plugin which is already a copy, adjust foreignResource references @@ -245,15 +160,9 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { for (const plugin of this.copyOfForeignResource.plugins.sort((a, b) => a.activationOrder - b.activationOrder)) { // if there already is a plugin with same instanceUniqueRepresentation, skip if (plugin.modifyResourceConfig) { - await plugin.modifyResourceConfig(adminforth, this.copyOfForeignResource); - } - console.log(`🤮🤮🤮🤮🤮🤮🤮🤮🤮🤮🤮Activating plugin ${plugin.pluginInstanceId} for resource ${this.copyOfForeignResource.resourceId}`); - if (plugin.setupEndpoints) { - await plugin.setupEndpoints(adminforth.express); - } - if (plugin.validateConfigAfterDiscover) { - await plugin.validateConfigAfterDiscover(adminforth, this.copyOfForeignResource); + plugin.modifyResourceConfig(adminforth, this.copyOfForeignResource); } + this.adminforth.activatedPlugins.push(plugin); } From 72535fdf75d0f091ebaf9c817b48f50b6eaa7377 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Tue, 23 Dec 2025 17:52:14 +0200 Subject: [PATCH 3/5] fix: add console logs for debugging resource configuration and endpoint setup --- custom/InlineList.vue | 1 + index.ts | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/custom/InlineList.vue b/custom/InlineList.vue index b4c2b93..024c8ed 100644 --- a/custom/InlineList.vue +++ b/custom/InlineList.vue @@ -177,6 +177,7 @@ const listResourceRefColumn = computed(() => { if (!listResource.value || props.meta.disableForeignListResourceRefColumn) { return null; } + console.log("Finding ref column in listResource", listResource.value.resourceId, "for resource", props.resource.resourceId, "in", listResource.value); return listResource.value.columns.find(c => c.foreignResource?.polymorphicResources ? c.foreignResource.polymorphicResources.find((pr) => pr.resourceId === props.resource.resourceId) : c.foreignResource?.resourceId === props.resource.resourceId); diff --git a/index.ts b/index.ts index 5b9bd7a..6eaf544 100644 --- a/index.ts +++ b/index.ts @@ -27,7 +27,6 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { } setupEndpoints(server: IHttpServer) { - console.log("Setting up endpoints for plugin", this.pluginInstanceId); server.endpoint({ method: 'POST', path: `/plugin/${this.pluginInstanceId}/get_default_filters`, @@ -50,6 +49,8 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { async modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource) { super.modifyResourceConfig(adminforth, resourceConfig); + console.log("Modifying resource config for ForeignInlineListPlugin", this.resourceConfig.resourceId); + this.adminforth = adminforth; this.foreignResource = adminforth.config.resources.find((resource) => resource.resourceId === this.options.foreignResourceId); @@ -152,8 +153,21 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { for (const plugin of this.foreignResource.plugins || []) { const options = plugin.pluginOptions; // call constructor - const pluginCopy = new (plugin.constructor as any)(options); - this.copyOfForeignResource.plugins.push(pluginCopy); + if ( plugin.constructor.name === 'ForeignInlineListPlugin' ) { + + if (plugin.pluginOptions.foreignResourceId === this.foreignResource.resourceId && !this.resourceConfig.resourceId.includes('_inline_list__from_')) { + console.log("Found nested ForeignInlineListPlugin"); + plugin.pluginOptions.foreignResourceId = idOfNewCopy; + const pluginCopy = new (plugin.constructor as any)(options); + this.copyOfForeignResource.plugins.push(pluginCopy); + } else if (plugin.pluginOptions.foreignResourceId === this.copyOfForeignResource.resourceId && this.resourceConfig.resourceId.includes('_inline_list__from_')) { + console.log("Adjusting nested ForeignInlineListPlugin foreignResourceId"); + break; + } + } else { + const pluginCopy = new (plugin.constructor as any)(options); + this.copyOfForeignResource.plugins.push(pluginCopy); + } } // activate plugins for the copyOfForeignResource From 4eed275fb56b9d2fbfd4360833971d0afecf4b78 Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Wed, 24 Dec 2025 10:51:25 +0200 Subject: [PATCH 4/5] fix: remove console logs and update foreign resource ID handling in ForeignInlineListPlugin --- custom/InlineList.vue | 1 - index.ts | 23 +++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/custom/InlineList.vue b/custom/InlineList.vue index 024c8ed..b4c2b93 100644 --- a/custom/InlineList.vue +++ b/custom/InlineList.vue @@ -177,7 +177,6 @@ const listResourceRefColumn = computed(() => { if (!listResource.value || props.meta.disableForeignListResourceRefColumn) { return null; } - console.log("Finding ref column in listResource", listResource.value.resourceId, "for resource", props.resource.resourceId, "in", listResource.value); return listResource.value.columns.find(c => c.foreignResource?.polymorphicResources ? c.foreignResource.polymorphicResources.find((pr) => pr.resourceId === props.resource.resourceId) : c.foreignResource?.resourceId === props.resource.resourceId); diff --git a/index.ts b/index.ts index 6eaf544..cb9fba2 100644 --- a/index.ts +++ b/index.ts @@ -49,7 +49,6 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { async modifyResourceConfig(adminforth: IAdminForth, resourceConfig: AdminForthResource) { super.modifyResourceConfig(adminforth, resourceConfig); - console.log("Modifying resource config for ForeignInlineListPlugin", this.resourceConfig.resourceId); this.adminforth = adminforth; this.foreignResource = adminforth.config.resources.find((resource) => resource.resourceId === this.options.foreignResourceId); @@ -149,6 +148,7 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { 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; @@ -156,14 +156,19 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { if ( plugin.constructor.name === 'ForeignInlineListPlugin' ) { if (plugin.pluginOptions.foreignResourceId === this.foreignResource.resourceId && !this.resourceConfig.resourceId.includes('_inline_list__from_')) { - console.log("Found nested ForeignInlineListPlugin"); + // 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); - } else if (plugin.pluginOptions.foreignResourceId === this.copyOfForeignResource.resourceId && this.resourceConfig.resourceId.includes('_inline_list__from_')) { - console.log("Adjusting nested ForeignInlineListPlugin foreignResourceId"); - break; - } + 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); @@ -179,6 +184,12 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { this.adminforth.activatedPlugins.push(plugin); } + const currentResourceForeignRefColumnWithComponent = this.copyOfForeignResource.columns.find(col => col.name === 'foreignInlineList_' + idOfNewCopy); + if (currentResourceForeignRefColumnWithComponent && shouldRefColumnBeUpdated) { + // if we are creating a copy for resource, which is refferes to itself, we need to update foreignResourceId in component meta + //@ts-ignore + currentResourceForeignRefColumnWithComponent.components.showRow.meta.foreignResourceId = this.resourceConfig.resourceId; + } } } \ No newline at end of file From 1e4b4895d8b1cfe70cd129a8bf941ac1aeb230dd Mon Sep 17 00:00:00 2001 From: yaroslav8765 Date: Thu, 25 Dec 2025 15:13:52 +0200 Subject: [PATCH 5/5] fix: update modifyResourceConfig method to accept allPluginInstances parameter and adjust peer dependency version for adminforth --- index.ts | 8 +++----- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/index.ts b/index.ts index cb9fba2..6a873a0 100644 --- a/index.ts +++ b/index.ts @@ -47,7 +47,7 @@ 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; @@ -178,10 +178,8 @@ export default class ForeignInlineListPlugin extends AdminForthPlugin { // 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 - if (plugin.modifyResourceConfig) { - plugin.modifyResourceConfig(adminforth, this.copyOfForeignResource); - } - this.adminforth.activatedPlugins.push(plugin); + process.env.HEAVY_DEBUG && console.log('Activating plugin for foreign inline list copy:', plugin.constructor.name); + allPluginInstances.push({pi: plugin, resource: this.copyOfForeignResource}); } const currentResourceForeignRefColumnWithComponent = this.copyOfForeignResource.columns.find(col => col.name === 'foreignInlineList_' + idOfNewCopy); 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" } }