Skip to content

Commit

Permalink
Fix #69: Rename url to path and do not prefix with baseUrl
Browse files Browse the repository at this point in the history
Fix #72 Move sitemap field under portalSite Content ObjectType
Fix #73: sitemap should return null in Guillotine when the app is not installed on the nearest site
  • Loading branch information
ComLock authored and alansemenov committed Mar 7, 2024
1 parent 1104f6a commit d3eb8b9
Show file tree
Hide file tree
Showing 12 changed files with 356 additions and 243 deletions.
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"devDependencies": {
"@enonic-types/global": "^7.14.0",
"@enonic-types/guillotine": "^0.0.2",
"@enonic-types/guillotine": "^7.1.0-B1",
"@enonic-types/lib-content": "^7.14.0",
"@enonic-types/lib-context": "^7.14.0",
"@enonic-types/lib-portal": "^7.14.0",
Expand Down
13 changes: 13 additions & 0 deletions src/main/resources/guillotine/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export const DEBUG = false;
export const TRACE = false;
// const PROFILING = false;

// In type names first letter should be uppercase
export const enum GraphQLTypeName {
SITEMAP = 'Sitemap',
SITEMAP_URL = 'Sitemap_Url',
}

// In fields names first letter should be lowercase
export const SITEMAP_FIELD_NAME = 'sitemap';
export const URLSET_FIELD_NAME = 'urlset';
9 changes: 9 additions & 0 deletions src/main/resources/guillotine/guillotine.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export declare interface SiteMapResolverData {
baseUrl: string|null
}

export declare interface SiteMapResolverLocaleContext {
siteJson: string
siteConfigJson: string
[x: string]: string
}
224 changes: 18 additions & 206 deletions src/main/resources/guillotine/guillotine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,86 +2,27 @@ import type {
Extensions,
GraphQL,
} from '@enonic-types/guillotine';
import type {Site} from '@enonic-types/lib-content';
import type {
SitemapXmlSiteConfig,
tChangeFreq,
tPriority,
} from '/types';


import {ObjectTypeName} from '@enonic-types/guillotine';
import {
get as getContentByKey,
} from '/lib/xp/content';
import {
get as getContext,
run as runInContext
} from '/lib/xp/context';
import {getSiteConfigFromSite} from '/guillotine/getSiteConfigFromSite';
// import {safeMs} from '/guillotine/safeMs';
import {queryForSitemapContent} from '/lib/app-sitemapxml/queryForSitemapContent';


const DEBUG = false;
const TRACE = false;
// const PROFILING = false;

// @ts-ignore
// const {currentTimeMillis} = Java.type('java.lang.System') as {
// currentTimeMillis: () => number
// }


// In type names first letter should be uppercase
const enum GraphQLTypeName {
SITEMAP = 'Sitemap',
SITEMAP_URL = 'SitemapUrl',
}

// In fields names first letter should be lowercase
const SITEMAP_FIELD_NAME = 'sitemap';
const URLSET_FIELD_NAME = 'urlset';


interface SitemapUrl {
changefreq?: tChangeFreq
lastmod?: string
priority?: tPriority
url: string
}
SITEMAP_FIELD_NAME,
URLSET_FIELD_NAME,
GraphQLTypeName,
} from '/guillotine/constants';
import {buildObjectTypeSiteMap} from '/guillotine/types/buildObjectTypeSiteMap';
import {buildObjectTypeSiteMapUrl} from '/guillotine/types/buildObjectTypeSiteMapUrl';
import {buildSitemapResolver} from './resolvers/sitemap';
import {urlset} from '/guillotine/resolvers/urlset';


export const extensions = (graphQL: GraphQL): Extensions => ({
types: {
[GraphQLTypeName.SITEMAP]: {
description: 'Sitemap',
fields: {
urlset: {
type: graphQL.list(graphQL.reference(GraphQLTypeName.SITEMAP_URL)),
}
}
},
[GraphQLTypeName.SITEMAP_URL]: {
description: 'Sitemap URL item',
fields: {
url: {
type: graphQL.nonNull(graphQL.GraphQLString),
},
changefreq: {
type: graphQL.GraphQLString
},
lastmod: {
type: graphQL.DateTime,
},
priority: {
type: graphQL.GraphQLString
},
}
}
[GraphQLTypeName.SITEMAP]: buildObjectTypeSiteMap(graphQL),
[GraphQLTypeName.SITEMAP_URL]: buildObjectTypeSiteMapUrl(graphQL),
},
creationCallbacks: {
[ObjectTypeName.HeadlessCms]: (params) => {
[ObjectTypeName.portal_Site]: (params) => {
params.addFields({
[SITEMAP_FIELD_NAME]: {
type: graphQL.reference(GraphQLTypeName.SITEMAP),
Expand All @@ -97,143 +38,14 @@ export const extensions = (graphQL: GraphQL): Extensions => ({
type: graphQL.list(graphQL.reference(GraphQLTypeName.SITEMAP_URL)),
},
});
}
},
},
resolvers: {
[ObjectTypeName.HeadlessCms]: {
[SITEMAP_FIELD_NAME]: (env) => {
TRACE && log.debug(`resolvers ${SITEMAP_FIELD_NAME} env: ${JSON.stringify(env, null, 4)}`);
const {
args,
localContext,
// source
} = env;
const {
branch,
project,
siteKey // NOTE: Can be undefined when x-guillotine-sitekey is missing
} = localContext;
TRACE && log.debug(`resolvers ${SITEMAP_FIELD_NAME} siteKey: ${siteKey}`);
if (!siteKey) {
return null;
}
const context = getContext();
const {
authInfo: {
principals = [] // Handle undefined
} = {}
} = context;
return runInContext({
branch,
repository: `com.enonic.cms.${project}`,
principals: principals || [] // Handle null
}, () => {
const site = getContentByKey<Site<SitemapXmlSiteConfig>>({key: siteKey});
TRACE && log.debug(`resolvers ${SITEMAP_FIELD_NAME} site: ${JSON.stringify(site, null, 4)}`);
if (!site) {
return null;
}
const siteConfig = getSiteConfigFromSite({
applicationKey: app.name,
site,
});
return {
_site: site,
_siteConfig: siteConfig,
};
}); // runInContext
} // SITEMAP_FIELD_NAME
}, // HeadlessCms
[ObjectTypeName.portal_Site]: {
[SITEMAP_FIELD_NAME]: buildSitemapResolver(graphQL),
},
[GraphQLTypeName.SITEMAP]: {
[URLSET_FIELD_NAME]: (env) => {
TRACE && log.debug(`resolvers ${URLSET_FIELD_NAME} env: ${JSON.stringify(env, null, 4)}`);

const {
args,
localContext,
source
} = env;
TRACE && log.debug(`resolvers ${URLSET_FIELD_NAME} source: ${JSON.stringify(source, null, 4)}`);

const {
_site,
_siteConfig
} = source as {
_site: Site<SitemapXmlSiteConfig>,
_siteConfig: SitemapXmlSiteConfig
};

const {
branch,
project,
siteKey // NOTE: Can be undefined when x-guillotine-sitekey is missing
} = localContext;
TRACE && log.debug(`resolvers ${URLSET_FIELD_NAME} siteKey: ${siteKey}`);

if (!siteKey) {
return null;
}
// const startMs = PROFILING ? currentTimeMillis() : 0;
const context = getContext();
const {
authInfo: {
principals = [] // Handle undefined
} = {}
} = context;
return runInContext({
branch,
repository: `com.enonic.cms.${project}`,
principals: principals || [] // Handle null
}, () => {
TRACE && log.debug(`resolvers ${URLSET_FIELD_NAME} _siteConfig: ${JSON.stringify(_siteConfig, null, 4)}`);
const {
overrideDomain = '',
} = _siteConfig || {};

const {
count
} = args;

const {
changefreq,
priority,
result
} = queryForSitemapContent({
count,
site: _site,
siteConfig: _siteConfig
})

// Go through the results and add the corresponding settings for each match.
const items: SitemapUrl[] = [];
for (let i = 0; i < result.hits.length; i++) {
if (result.hits[i].type) {
TRACE && log.debug(`resolvers ${URLSET_FIELD_NAME} result.hits[${i}].type: ${result.hits[i].type}`);
const path = result.hits[i]._path.replace(_site._path, '') || '/';
items.push({
changefreq: changefreq[result.hits[i].type],
lastmod: result.hits[i].modifiedTime,
priority: priority[result.hits[i].type],
url: overrideDomain ? `${overrideDomain}${path}`.replace(/\/\//g, '/') : path
});
}
}

DEBUG && log.debug(`resolvers ${URLSET_FIELD_NAME} first item: ${JSON.stringify(items[0], null, 4)}`);
TRACE && log.debug(`resolvers ${URLSET_FIELD_NAME} all items: ${JSON.stringify(items, null, 4)}`);

// if (PROFILING) {
// const endMs = currentTimeMillis();
// const durationMs = endMs - startMs;
// log.debug(`resolvers ${URLSET_FIELD_NAME} durationMs: ${safeMs(durationMs)}`);
// }

return items;
// return {
// urlset: items
// };
}); // runInContext
}, // SITEMAP_XML
}, // HEADLESS_CMS
} // resolvers
[URLSET_FIELD_NAME]: urlset(graphQL),
}
}
});
94 changes: 94 additions & 0 deletions src/main/resources/guillotine/resolvers/sitemap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import type {Site} from '@enonic-types/lib-content';
import type {
DataFetcherResult,
GraphQL,
Resolver,
} from '@enonic-types/guillotine';
import type {
SiteMapResolverData,
SiteMapResolverLocaleContext
} from '/guillotine/guillotine.d';
import type {
SitemapXmlSiteConfig,
// tChangeFreq,
// tPriority,
} from '/types';

import {get as getContentByKey} from '/lib/xp/content';
import {
get as getContext,
run as runInContext
} from '/lib/xp/context';
// import {
// DEFAULT_PRIORITY,
// DEFAULT_UPDATE_PERIOD
// } from '/lib/app-sitemapxml/constants';
import {
// DEBUG,
SITEMAP_FIELD_NAME,
TRACE,
} from '/guillotine/constants';
import {getSiteConfigFromSite} from '/guillotine/getSiteConfigFromSite';


export const buildSitemapResolver = (graphQL: GraphQL): Resolver<
{}, // args
{}, // localContext
Site<SitemapXmlSiteConfig>,
DataFetcherResult<SiteMapResolverData,SiteMapResolverLocaleContext>|null
> => (env) => {
TRACE && log.debug(`resolvers ${SITEMAP_FIELD_NAME} env: ${JSON.stringify(env, null, 4)}`);
const {
// args,
localContext,
source: siteWithoutSiteConfig
} = env;
TRACE && log.debug(`resolvers ${SITEMAP_FIELD_NAME} siteWithoutSiteConfig: ${JSON.stringify(siteWithoutSiteConfig, null, 4)}`);

const {
branch,
project,
// siteKey // NOTE: Can be undefined when x-guillotine-sitekey is missing
} = localContext;
// TRACE && log.debug(`resolvers ${SITEMAP_FIELD_NAME} siteKey: ${siteKey}`);
// if (!siteKey) {
// return null;
// }
const context = getContext();
const {
authInfo: {
principals = [] // Handle undefined
} = {}
} = context;
return runInContext({
branch,
repository: `com.enonic.cms.${project}`,
principals: principals || [] // Handle null
}, () => {
const site = getContentByKey<Site<SitemapXmlSiteConfig>>({key: siteWithoutSiteConfig._path});
TRACE && log.debug(`resolvers ${SITEMAP_FIELD_NAME} site: ${JSON.stringify(site, null, 4)}`);
const siteConfig = getSiteConfigFromSite({
applicationKey: app.name,
site: site as Site<SitemapXmlSiteConfig>,
});
if (!siteConfig) {
return null;
}
const {
overrideDomain: baseUrl = null
} = siteConfig || {};
return graphQL.createDataFetcherResult<
SiteMapResolverData,
SiteMapResolverLocaleContext
>({
data: __.toScriptValue<SiteMapResolverData>({
baseUrl,
}),
localContext: {
siteJson: JSON.stringify(site),
siteConfigJson: JSON.stringify(siteConfig),
},
parentLocalContext: localContext,
});
}); // runInContext
};
Loading

0 comments on commit d3eb8b9

Please sign in to comment.