Skip to content

Commit

Permalink
Outcommented implementation of #70 Convert urlset to urlSetConnection
Browse files Browse the repository at this point in the history
  • Loading branch information
ComLock committed Mar 6, 2024
1 parent 7179b8f commit 195660a
Show file tree
Hide file tree
Showing 12 changed files with 289 additions and 53 deletions.
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dependencies {
include "com.enonic.xp:lib-portal:${xpVersion}"

include 'com.enonic.lib:lib-cache:2.2.0'
include 'com.enonic.lib:lib-graphql:2.1.0' // /lib/graphql-connection
include "com.enonic.lib:lib-xslt:2.1.1"
}

Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/guillotine/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ export const TRACE = false;
export const enum GraphQLTypeName {
SITEMAP = 'Sitemap',
SITEMAP_URL = 'Sitemap_Url',
SITEMAP_URLSET_CONNECTION = 'Sitemap_UrlsetConnection',
SITEMAP_URLSET_EDGE = 'Sitemap_UrlsetEdge'
}

// In fields names first letter should be lowercase
export const SITEMAP_FIELD_NAME = 'sitemap';
export const URLSET_FIELD_NAME = 'urlset';
export const URLSET_CONNECTION_FIELD_NAME = 'urlsetConnection';
31 changes: 14 additions & 17 deletions src/main/resources/guillotine/guillotine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,24 @@ import {ObjectTypeName} from '@enonic-types/guillotine';
import {
SITEMAP_FIELD_NAME,
URLSET_FIELD_NAME,
// URLSET_CONNECTION_FIELD_NAME,
GraphQLTypeName,
} from '/guillotine/constants';
import {buildObjectTypeSiteMap} from '/guillotine/types/buildObjectTypeSiteMap';
import {buildObjectTypeSiteMapUrl} from '/guillotine/types/buildObjectTypeSiteMapUrl';
import {buildSitemapResolver} from './resolvers/sitemap';
// import {UrlsetConnection} from '/guillotine/types/UrlsetConnection';
// import {UrlsetEdge} from '/guillotine/types/UrlsetEdge';
import {Sitemap} from './types/Sitemap';
import {SitemapUrl} from './types/SitemapUrl';
import {sitemap} from './resolvers/sitemap';
import {urlset} from '/guillotine/resolvers/urlset';
// import {urlsetConnection} from '/guillotine/resolvers/urlsetConnection';


export const extensions = (graphQL: GraphQL): Extensions => ({
types: {
[GraphQLTypeName.SITEMAP]: buildObjectTypeSiteMap(graphQL),
[GraphQLTypeName.SITEMAP_URL]: buildObjectTypeSiteMapUrl(graphQL),
[GraphQLTypeName.SITEMAP]: Sitemap(graphQL),
[GraphQLTypeName.SITEMAP_URL]: SitemapUrl(graphQL),
// [GraphQLTypeName.SITEMAP_URLSET_CONNECTION]: UrlsetConnection(graphQL),
// [GraphQLTypeName.SITEMAP_URLSET_EDGE]: UrlsetEdge(graphQL),
},
creationCallbacks: {
[ObjectTypeName.portal_Site]: (params) => {
Expand All @@ -29,23 +35,14 @@ export const extensions = (graphQL: GraphQL): Extensions => ({
},
});
},
[GraphQLTypeName.SITEMAP]: (params) => {
params.addFields({
[URLSET_FIELD_NAME]: {
args: {
count: graphQL.GraphQLInt
},
type: graphQL.list(graphQL.reference(GraphQLTypeName.SITEMAP_URL)),
},
});
},
},
resolvers: {
[ObjectTypeName.portal_Site]: {
[SITEMAP_FIELD_NAME]: buildSitemapResolver(graphQL),
[SITEMAP_FIELD_NAME]: sitemap(graphQL),
},
[GraphQLTypeName.SITEMAP]: {
[URLSET_FIELD_NAME]: urlset(graphQL),
}
// [URLSET_CONNECTION_FIELD_NAME]: urlsetConnection(graphQL),
},
}
});
2 changes: 1 addition & 1 deletion src/main/resources/guillotine/resolvers/sitemap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
import {getSiteConfigFromSite} from '/guillotine/getSiteConfigFromSite';


export const buildSitemapResolver = (graphQL: GraphQL): Resolver<
export const sitemap = (graphQL: GraphQL): Resolver<
{}, // args
{}, // localContext
Site<SitemapXmlSiteConfig>,
Expand Down
28 changes: 15 additions & 13 deletions src/main/resources/guillotine/resolvers/urlset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ export const urlset = (graphQL: GraphQL): Resolver<
SiteMapResolverData,
{}
> => (env) => {
// const startMs = PROFILING ? currentTimeMillis() : 0;

TRACE && log.debug('%s resolver env: %s', URLSET_FIELD_NAME, toStr(env));
const {
args,
Expand All @@ -71,41 +73,41 @@ export const urlset = (graphQL: GraphQL): Resolver<
maxItems = 10000,
} = siteConfig || {}; // Handle null (aka no config)

const maxItemsInt = Math.min(
MAX_ITEMS_LIMIT,
parseInt(maxItems as string, 10)
);
const maxItemsInt = parseInt(maxItems as string, 10);

const {
count = maxItemsInt
} = args;
const limitedCount = count < 0
? MAX_ITEMS_LIMIT
: Math.min(count, MAX_ITEMS_LIMIT);

const {
branch,
project,
} = localContext;

// 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
}, () => {
const {
count = maxItemsInt
} = args;

const {
changefreq,
priority,
result
} = queryForSitemapContent({
count,
count: limitedCount,
site,
siteConfig
})
});

// Go through the results and add the corresponding settings for each match.
const items: SitemapUrl[] = [];
Expand All @@ -132,5 +134,5 @@ export const urlset = (graphQL: GraphQL): Resolver<
// }

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


import {toStr} from '@enonic/js-utils/value/toStr';
import {
decodeCursor,
encodeCursor,
// @ts-expect-error No types yet.
} from '/lib/graphql-connection';
import {
// DEBUG,
TRACE,
URLSET_CONNECTION_FIELD_NAME
} from '/guillotine/constants';
import {
DEFAULT_PRIORITY,
DEFAULT_UPDATE_PERIOD,
} from '/lib/app-sitemapxml/constants';
import {queryForSitemapContent} from '/lib/app-sitemapxml/queryForSitemapContent';


declare interface SitemapUrl {
changefreq?: tChangeFreq
lastmod?: string
priority?: tPriority
path: string
}

declare type Cursor = string|number // becomes string after Guillotine has done it's thing

declare interface Edge {
cursor: Cursor
node: SitemapUrl

}

declare type Edges = Edge[]

declare interface PageInfo {
startCursor: Cursor
endCursor: Cursor
hasNext: boolean
}

const MAX_COUNT_PER_PAGE = 1000;


export const urlsetConnection = (graphQL: GraphQL): Resolver<
{
after?: string
first?: number
},
SiteMapResolverLocaleContext,
SiteMapResolverData,
{
edges: Edges,
pageInfo: PageInfo,
totalCount: number
}
> => (env) => {
TRACE && log.debug('%s resolver env: %s', URLSET_CONNECTION_FIELD_NAME, toStr(env));

const {
args,
localContext,
// source
} = env;

const site = JSON.parse(localContext.siteJson as string) as Site<SitemapXmlSiteConfig>;
TRACE && log.debug('%s resolver site: %s', URLSET_CONNECTION_FIELD_NAME, toStr(site));

const siteConfig = JSON.parse(localContext.siteConfigJson as string) as SitemapXmlSiteConfig;
TRACE && log.debug('%s resolver siteConfig: %s', URLSET_CONNECTION_FIELD_NAME, toStr(siteConfig));

const {
maxItems = 10000,
} = siteConfig || {}; // Handle null (aka no config)

const maxItemsInt = parseInt(maxItems as string, 10);

const {
after,
first = maxItemsInt,
} = args;

const start = after ? parseInt(decodeCursor(after), 10) + 1 : 0;
TRACE && log.debug('%s resolver after: %s', URLSET_CONNECTION_FIELD_NAME, after);
TRACE && log.debug('%s resolver start: %s', URLSET_CONNECTION_FIELD_NAME, start);

const count = first < 0
? MAX_COUNT_PER_PAGE
: Math.min(
MAX_COUNT_PER_PAGE,
first
);
TRACE && log.debug('%s resolver first: %s', URLSET_CONNECTION_FIELD_NAME, first);
TRACE && log.debug('%s resolver count: %s', URLSET_CONNECTION_FIELD_NAME, count);

const {
changefreq,
priority,
result
} = queryForSitemapContent({
count,
site,
siteConfig,
start
});

const {
hits,
total
} = result;
TRACE && log.debug('%s resolver hits: %s', URLSET_CONNECTION_FIELD_NAME, toStr(hits));

// 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('%s resolver result.hits[%s].type: %s', URLSET_CONNECTION_FIELD_NAME, i, result.hits[i].type);
const path = result.hits[i]._path.replace(site._path, '') || '/';
items.push({
changefreq: changefreq[result.hits[i].type] === DEFAULT_UPDATE_PERIOD ? undefined : changefreq[result.hits[i].type],
lastmod: result.hits[i].modifiedTime,
priority: priority[result.hits[i].type] === DEFAULT_PRIORITY ? undefined : priority[result.hits[i].type],
path
});
}
}

const edges: Edges = [];
for (let i = 0; i < items.length; i++) {
edges.push({
node: items[i],
cursor: encodeCursor(start + i)
});
}

const countInPage = edges.length;
const pageInfo: PageInfo = {
startCursor: start,
endCursor: start + (countInPage === 0 ? 0 : (countInPage - 1)),
hasNext: (start + countInPage) < total
}

return {
edges,
pageInfo,
totalCount: total
}
};
31 changes: 31 additions & 0 deletions src/main/resources/guillotine/types/Sitemap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type {GraphQL} from '@enonic-types/guillotine';


import {
// URLSET_CONNECTION_FIELD_NAME,
URLSET_FIELD_NAME,
GraphQLTypeName
} from '/guillotine/constants';


export const Sitemap = (graphQL: GraphQL) => ({
description: 'Sitemap',
fields: {
baseUrl: {
type: graphQL.GraphQLString,
},
[URLSET_FIELD_NAME]: {
args: {
count: graphQL.GraphQLInt
},
type: graphQL.list(graphQL.reference(GraphQLTypeName.SITEMAP_URL)),
},
// [URLSET_CONNECTION_FIELD_NAME]: {
// args: {
// after: graphQL.GraphQLString,
// first: graphQL.GraphQLInt,
// },
// type: graphQL.reference(GraphQLTypeName.SITEMAP_URLSET_CONNECTION),
// }
}
});
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {GraphQL} from '@enonic-types/guillotine';


export const buildObjectTypeSiteMapUrl = (graphQL: GraphQL) => ({
export const SitemapUrl = (graphQL: GraphQL) => ({
description: 'Sitemap URL item',
fields: {
changefreq: {
Expand Down
28 changes: 28 additions & 0 deletions src/main/resources/guillotine/types/UrlsetConnection.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type {
Extensions,
GraphQL,
} from '@enonic-types/guillotine';


import {ObjectTypeName} from '@enonic-types/guillotine';
import {GraphQLTypeName} from '/guillotine/constants';


type GetRecordValue<T> = T extends Record<string, infer U> ? U : never;
type Type = GetRecordValue<Extensions['types']>


export const UrlsetConnection = (graphQl: GraphQL): Type => ({
description: 'UrlsetConnection',
fields: {
edges: {
type: graphQl.list(graphQl.reference(GraphQLTypeName.SITEMAP_URLSET_EDGE))
},
pageInfo: {
type: graphQl.reference(ObjectTypeName.PageInfo)
},
totalCount: {
type: graphQl.nonNull(graphQl.GraphQLInt),
}
}
});
Loading

0 comments on commit 195660a

Please sign in to comment.