Add wiki instructions path hierarchy walk-up
When looking up wiki docs for a screen, now walks up the path hierarchy to find the most specific match. For example, for path 'Catalog/Product/EditProduct/Assocs', it tries: 1. Catalog/Product/EditProduct/Assocs (most specific) 2. Catalog/Product/EditProduct (inherits if no specific doc) 3. Catalog/Product 4. Catalog This allows parent screen docs to apply to all child tabs/subscreens unless overridden with more specific documentation. Updated both getWikiInstructions (used by execute#ScreenAsMcpTool) and loadWikiContent (used by moqui_browse_screens) to use this approach.
Showing
1 changed file
with
95 additions
and
48 deletions
| ... | @@ -617,38 +617,64 @@ def extractSummary = { wikiText -> | ... | @@ -617,38 +617,64 @@ def extractSummary = { wikiText -> |
| 617 | } | 617 | } |
| 618 | 618 | ||
| 619 | // Helper function to load wiki instructions for a screen | 619 | // Helper function to load wiki instructions for a screen |
| 620 | // Walks up the path hierarchy to find the most specific wiki doc available | ||
| 621 | // e.g., for "Catalog/Product/EditProduct/Assocs", tries in order: | ||
| 622 | // 1. Catalog/Product/EditProduct/Assocs (most specific) | ||
| 623 | // 2. Catalog/Product/EditProduct | ||
| 624 | // 3. Catalog/Product | ||
| 625 | // 4. Catalog | ||
| 620 | def getWikiInstructions = { lookupPath -> | 626 | def getWikiInstructions = { lookupPath -> |
| 621 | try { | 627 | if (!lookupPath) return null |
| 622 | def wikiPage = ec.entity.find("moqui.resource.wiki.WikiPage") | 628 | |
| 623 | .condition("wikiSpaceId", "MCP_SCREEN_DOCS") | 629 | // Normalize path - remove leading/trailing slashes |
| 624 | .condition("pagePath", lookupPath) | 630 | def normalizedPath = lookupPath.replaceAll('^/+', '').replaceAll('/+$', '') |
| 625 | .useCache(true) | 631 | if (!normalizedPath) return null |
| 626 | .one() | 632 | |
| 633 | // Build list of paths from most specific to least specific | ||
| 634 | def pathsToTry = [] | ||
| 635 | def segments = normalizedPath.split('/') | ||
| 636 | for (int i = segments.length; i > 0; i--) { | ||
| 637 | pathsToTry.add(segments[0..i-1].join('/')) | ||
| 638 | } | ||
| 639 | |||
| 640 | ec.logger.debug("Wiki lookup: trying paths ${pathsToTry} for ${lookupPath}") | ||
| 641 | |||
| 642 | for (def tryPath in pathsToTry) { | ||
| 643 | try { | ||
| 644 | def wikiPage = ec.entity.find("moqui.resource.wiki.WikiPage") | ||
| 645 | .condition("wikiSpaceId", "MCP_SCREEN_DOCS") | ||
| 646 | .condition("pagePath", tryPath) | ||
| 647 | .useCache(true) | ||
| 648 | .one() | ||
| 627 | 649 | ||
| 628 | if (!wikiPage) return null | 650 | if (!wikiPage) continue |
| 629 | 651 | ||
| 630 | def wikiSpace = ec.entity.find("moqui.resource.wiki.WikiSpace") | 652 | def wikiSpace = ec.entity.find("moqui.resource.wiki.WikiSpace") |
| 631 | .condition("wikiSpaceId", wikiPage.wikiSpaceId) | 653 | .condition("wikiSpaceId", wikiPage.wikiSpaceId) |
| 632 | .one() | 654 | .one() |
| 633 | 655 | ||
| 634 | if (!wikiSpace) return null | 656 | if (!wikiSpace) continue |
| 635 | 657 | ||
| 636 | // Build the resource location for the page | 658 | // Build the resource location for the page |
| 637 | def pageLocation = wikiSpace.rootPageLocation | 659 | def pageLocation = wikiSpace.rootPageLocation |
| 638 | if (!pageLocation.endsWith('/')) { | 660 | if (!pageLocation.endsWith('/')) { |
| 639 | pageLocation += '/' | 661 | pageLocation += '/' |
| 640 | } | 662 | } |
| 641 | pageLocation += wikiPage.pagePath + '.md' | 663 | pageLocation += wikiPage.pagePath + '.md' |
| 642 | 664 | ||
| 643 | // Get the resource reference and text content | 665 | // Get the resource reference and text content |
| 644 | def pageRef = ec.resource.getLocationReference(pageLocation) | 666 | def pageRef = ec.resource.getLocationReference(pageLocation) |
| 645 | def wikiText = pageRef?.getText() | 667 | def wikiText = pageRef?.getText() |
| 646 | 668 | ||
| 647 | if (wikiText) { | 669 | if (wikiText) { |
| 648 | return wikiText | 670 | if (tryPath != normalizedPath) { |
| 671 | ec.logger.debug("Wiki lookup: found inherited docs at ${tryPath} for ${lookupPath}") | ||
| 672 | } | ||
| 673 | return wikiText | ||
| 674 | } | ||
| 675 | } catch (Exception e) { | ||
| 676 | ec.logger.debug("Could not load wiki instructions for ${tryPath}: ${e.message}") | ||
| 649 | } | 677 | } |
| 650 | } catch (Exception e) { | ||
| 651 | ec.logger.debug("Could not load wiki instructions for ${screenPath}: ${e.message}") | ||
| 652 | } | 678 | } |
| 653 | return null | 679 | return null |
| 654 | } | 680 | } |
| ... | @@ -1833,16 +1859,10 @@ def wikiInstructions = getWikiInstructions(inputScreenPath) | ... | @@ -1833,16 +1859,10 @@ def wikiInstructions = getWikiInstructions(inputScreenPath) |
| 1833 | currentPath = currentPath.split("\\?")[0] | 1859 | currentPath = currentPath.split("\\?")[0] |
| 1834 | } | 1860 | } |
| 1835 | 1861 | ||
| 1836 | // Helper function to load wiki content | 1862 | // Helper to load wiki content for a specific path (no hierarchy walk) |
| 1837 | def loadWikiContent = { path -> | 1863 | def loadWikiContentForPath = { simplePath -> |
| 1838 | ec.logger.info("BrowseScreens: loadWikiContent CALLED for ${path}") | ||
| 1839 | try { | 1864 | try { |
| 1840 | def simplePath = (path == "root") ? "root" : path | 1865 | ec.logger.debug("BrowseScreens: Looking up wiki instructions for ${simplePath}") |
| 1841 | if (simplePath.contains("?")) { | ||
| 1842 | simplePath = simplePath.split("\\?")[0] | ||
| 1843 | } | ||
| 1844 | |||
| 1845 | ec.logger.info("BrowseScreens: Looking up wiki instructions for ${simplePath}") | ||
| 1846 | 1866 | ||
| 1847 | def wikiPage = ec.entity.find("moqui.resource.wiki.WikiPage") | 1867 | def wikiPage = ec.entity.find("moqui.resource.wiki.WikiPage") |
| 1848 | .condition("wikiSpaceId", "MCP_SCREEN_DOCS") | 1868 | .condition("wikiSpaceId", "MCP_SCREEN_DOCS") |
| ... | @@ -1851,51 +1871,78 @@ def wikiInstructions = getWikiInstructions(inputScreenPath) | ... | @@ -1851,51 +1871,78 @@ def wikiInstructions = getWikiInstructions(inputScreenPath) |
| 1851 | .one() | 1871 | .one() |
| 1852 | 1872 | ||
| 1853 | if (wikiPage) { | 1873 | if (wikiPage) { |
| 1854 | ec.logger.info("BrowseScreens: Found wikiPage: ${wikiPage.pagePath}") | 1874 | ec.logger.debug("BrowseScreens: Found wikiPage: ${wikiPage.pagePath}") |
| 1855 | def wikiSpace = ec.entity.find("moqui.resource.wiki.WikiSpace") | 1875 | def wikiSpace = ec.entity.find("moqui.resource.wiki.WikiSpace") |
| 1856 | .condition("wikiSpaceId", wikiPage.wikiSpaceId) | 1876 | .condition("wikiSpaceId", wikiPage.wikiSpaceId) |
| 1857 | .one() | 1877 | .one() |
| 1858 | 1878 | ||
| 1859 | if (wikiSpace) { | 1879 | if (wikiSpace) { |
| 1860 | ec.logger.info("BrowseScreens: Found wikiSpace: ${wikiSpace.wikiSpaceId}") | ||
| 1861 | |||
| 1862 | def dbResource = ec.entity.find("moqui.resource.DbResource") | 1880 | def dbResource = ec.entity.find("moqui.resource.DbResource") |
| 1863 | .condition("parentResourceId", "WIKI_MCP_SCREEN_DOCS") | 1881 | .condition("parentResourceId", "WIKI_MCP_SCREEN_DOCS") |
| 1864 | .condition("filename", wikiPage.pagePath + ".md") | 1882 | .condition("filename", wikiPage.pagePath + ".md") |
| 1865 | .one() | 1883 | .one() |
| 1866 | 1884 | ||
| 1867 | if (dbResource) { | 1885 | if (dbResource) { |
| 1868 | ec.logger.info("BrowseScreens: Found dbResource: ${dbResource.resourceId}") | ||
| 1869 | def dbResourceFile = ec.entity.find("moqui.resource.DbResourceFile") | 1886 | def dbResourceFile = ec.entity.find("moqui.resource.DbResourceFile") |
| 1870 | .condition("resourceId", dbResource.resourceId) | 1887 | .condition("resourceId", dbResource.resourceId) |
| 1871 | .condition("versionName", wikiPage.publishedVersionName) | 1888 | .condition("versionName", wikiPage.publishedVersionName) |
| 1872 | .one() | 1889 | .one() |
| 1873 | 1890 | ||
| 1874 | ec.logger.info("BrowseScreens: dbResourceFile query result: ${dbResourceFile ? 'found' : 'null'}") | ||
| 1875 | |||
| 1876 | if (!dbResourceFile) { | 1891 | if (!dbResourceFile) { |
| 1877 | dbResourceFile = ec.entity.find("moqui.resource.DbResourceFile") | 1892 | dbResourceFile = ec.entity.find("moqui.resource.DbResourceFile") |
| 1878 | .condition("resourceId", dbResource.resourceId) | 1893 | .condition("resourceId", dbResource.resourceId) |
| 1879 | .one() | 1894 | .one() |
| 1880 | ec.logger.info("BrowseScreens: dbResourceFile fallback query result: ${dbResourceFile ? 'found' : 'null'}") | ||
| 1881 | } | ||
| 1882 | |||
| 1883 | if (dbResourceFile) { | ||
| 1884 | ec.logger.info("BrowseScreens: dbResourceFile.fileData: ${dbResourceFile.fileData ? 'exists, size=' + dbResourceFile.fileData.length() : 'null'}") | ||
| 1885 | } | 1895 | } |
| 1886 | 1896 | ||
| 1887 | if (dbResourceFile && dbResourceFile.fileData) { | 1897 | if (dbResourceFile && dbResourceFile.fileData) { |
| 1888 | def content = new String(dbResourceFile.fileData.getBytes(new Long(1).longValue(), new Long(dbResourceFile.fileData.length()).intValue()), "UTF-8") | 1898 | def content = new String(dbResourceFile.fileData.getBytes(new Long(1).longValue(), new Long(dbResourceFile.fileData.length()).intValue()), "UTF-8") |
| 1889 | ec.logger.info("BrowseScreens: Found wiki instructions for ${simplePath}, length: ${content?.length()}") | 1899 | ec.logger.debug("BrowseScreens: Found wiki instructions for ${simplePath}, length: ${content?.length()}") |
| 1890 | return content | 1900 | return content |
| 1891 | } | 1901 | } |
| 1892 | } else { | ||
| 1893 | ec.logger.warn("BrowseScreens: No dbResource found for ${wikiPage.pagePath}.md") | ||
| 1894 | } | 1902 | } |
| 1895 | } | 1903 | } |
| 1896 | } | 1904 | } |
| 1897 | } catch (Exception e) { | 1905 | } catch (Exception e) { |
| 1898 | ec.logger.warn("BrowseScreens: Error getting wiki instructions: ${e.message}") | 1906 | ec.logger.debug("BrowseScreens: Error getting wiki instructions for ${simplePath}: ${e.message}") |
| 1907 | } | ||
| 1908 | return null | ||
| 1909 | } | ||
| 1910 | |||
| 1911 | // Helper function to load wiki content with path hierarchy walk | ||
| 1912 | // Walks up the path hierarchy to find the most specific wiki doc available | ||
| 1913 | // e.g., for "Catalog/Product/EditProduct/Assocs", tries in order: | ||
| 1914 | // 1. Catalog/Product/EditProduct/Assocs (most specific) | ||
| 1915 | // 2. Catalog/Product/EditProduct | ||
| 1916 | // 3. Catalog/Product | ||
| 1917 | // 4. Catalog | ||
| 1918 | def loadWikiContent = { path -> | ||
| 1919 | ec.logger.info("BrowseScreens: loadWikiContent CALLED for ${path}") | ||
| 1920 | |||
| 1921 | if (!path || path == "root") { | ||
| 1922 | // For root, try exact match only | ||
| 1923 | return loadWikiContentForPath("root") | ||
| 1924 | } | ||
| 1925 | |||
| 1926 | def simplePath = path | ||
| 1927 | if (simplePath.contains("?")) { | ||
| 1928 | simplePath = simplePath.split("\\?")[0] | ||
| 1929 | } | ||
| 1930 | // Normalize - remove leading/trailing slashes | ||
| 1931 | simplePath = simplePath.replaceAll('^/+', '').replaceAll('/+$', '') | ||
| 1932 | |||
| 1933 | if (!simplePath) return null | ||
| 1934 | |||
| 1935 | // Build list of paths from most specific to least specific | ||
| 1936 | def segments = simplePath.split('/') | ||
| 1937 | for (int i = segments.length; i > 0; i--) { | ||
| 1938 | def tryPath = segments[0..i-1].join('/') | ||
| 1939 | def content = loadWikiContentForPath(tryPath) | ||
| 1940 | if (content) { | ||
| 1941 | if (tryPath != simplePath) { | ||
| 1942 | ec.logger.info("BrowseScreens: Found inherited wiki docs at ${tryPath} for ${simplePath}") | ||
| 1943 | } | ||
| 1944 | return content | ||
| 1945 | } | ||
| 1899 | } | 1946 | } |
| 1900 | return null | 1947 | return null |
| 1901 | } | 1948 | } | ... | ... |
-
Please register or sign in to post a comment