Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add Dynamic Pages #3451

Open
wants to merge 7 commits into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/admin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"@types/react": "17.0.39",
"@webiny/app-admin": "0.0.0",
"@webiny/app-admin-users-cognito": "0.0.0",
"@webiny/app-dynamic-pages": "0.0.0",
"@webiny/app-form-builder": "0.0.0",
"@webiny/app-headless-cms": "0.0.0",
"@webiny/app-page-builder": "0.0.0",
Expand Down
6 changes: 5 additions & 1 deletion apps/admin/src/plugins/pageBuilder/editorPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ import responsiveEditorMode from "@webiny/app-page-builder/editor/plugins/respon
import pageSettingsPlugins from "@webiny/app-page-builder/editor/plugins/pageSettings";
// default presets for grid
import { gridPresets } from "@webiny/app-page-builder/editor/plugins/gridPresets";
// Dynamic source element settings
import dynamicPagesPlugins from "@webiny/app-dynamic-pages/plugins/index";

export default [
// Elements
Expand Down Expand Up @@ -151,5 +153,7 @@ export default [
// Responsive editor mode
responsiveEditorMode(),
// Page settings
pageSettingsPlugins
pageSettingsPlugins,
// Dynamic source element settings
dynamicPagesPlugins()
];
8 changes: 8 additions & 0 deletions apps/api/graphql/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
createPageBuilderContext,
createPageBuilderGraphQL
} from "@webiny/api-page-builder/graphql";
import { createInternalCmsQuerying } from "@webiny/api-page-builder/plugins";
import { createStorageOperations as createPageBuilderStorageOperations } from "@webiny/api-page-builder-so-ddb";
import pageBuilderPrerenderingPlugins from "@webiny/api-page-builder/prerendering";
import pageBuilderImportExportPlugins from "@webiny/api-page-builder-import-export/graphql";
Expand Down Expand Up @@ -92,6 +93,13 @@ export const handler = createHandler({
documentClient
})
}),
createHeadlessCmsContext({
storageOperations: createHeadlessCmsStorageOperations({
documentClient
})
}),
createHeadlessCmsGraphQL(),
createInternalCmsQuerying(),
createApwGraphQL(),
createApwPageBuilderContext({
storageOperations: createApwSaStorageOperations({ documentClient })
Expand Down
1 change: 1 addition & 0 deletions packages/api-page-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@babel/runtime": "^7.22.6",
"@webiny/api": "0.0.0",
"@webiny/api-file-manager": "0.0.0",
"@webiny/api-headless-cms": "0.0.0",
"@webiny/api-i18n": "0.0.0",
"@webiny/api-prerendering-service": "0.0.0",
"@webiny/api-security": "0.0.0",
Expand Down
16 changes: 14 additions & 2 deletions packages/api-page-builder/src/graphql/crud/pageTemplates.crud.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,13 @@ const createSchema = zod.object({
description: zod.string().max(100),
layout: zod.string().max(100).optional(),
pageCategory: zod.string().max(100),
content: zod.any()
content: zod.any(),
dynamicSource: zod
.object({
modelId: zod.string().max(100),
entryId: zod.string().max(100).optional()
})
.optional()
});

const updateSchema = zod.object({
Expand All @@ -43,7 +49,13 @@ const updateSchema = zod.object({
description: zod.string().max(100).optional(),
layout: zod.string().max(100).optional(),
pageCategory: zod.string().max(100).optional(),
content: zod.any()
content: zod.any(),
dynamicSource: zod
.object({
modelId: zod.string().max(100),
entryId: zod.string().max(100).optional()
})
.optional()
});

const getDefaultContent = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const createPageTemplateGraphQL = new GraphQLSchemaPlugin<PbContext>({
createdBy: PbCreatedBy!
layout: String
pageCategory: String
dynamicSource: JSON
}

input PbCreatePageTemplateInput {
Expand All @@ -34,6 +35,7 @@ export const createPageTemplateGraphQL = new GraphQLSchemaPlugin<PbContext>({
tags: [String!]
layout: String
pageCategory: String
dynamicSource: JSON
content: JSON
}

Expand All @@ -45,6 +47,7 @@ export const createPageTemplateGraphQL = new GraphQLSchemaPlugin<PbContext>({
pageCategory: String
content: JSON
tags: [String!]
dynamicSource: JSON
}

input PbCreateTemplateFromPageInput {
Expand Down
184 changes: 184 additions & 0 deletions packages/api-page-builder/src/plugins/cmsQueryingPlugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
import { ContextPlugin } from "@webiny/api";
import WebinyError from "@webiny/error";
import { getSchema } from "@webiny/api-headless-cms/graphql/getSchema";
import { ApiEndpoint, CmsContext } from "@webiny/api-headless-cms/types";
import { GraphQLRequestBody } from "@webiny/handler-graphql/types";
import processRequestBody from "@webiny/handler-graphql/processRequestBody";
import {
ErrorResponse,
GraphQLSchemaPlugin,
NotFoundResponse,
Response
} from "@webiny/handler-graphql";
import { buildDynamicPageDataQuery } from "~/utils/buildDynamicPageDataQuery";

export type Filter = {
filters: {
path: string;
condition: string;
value: string;
}[];
filterCondition: string;
};

export type Sort = {
path: string;
direction: string;
};
declare module "@webiny/api-headless-cms/types" {
interface HeadlessCms {
getExecutableSchema: (
type: ApiEndpoint
) => Promise<
(
input: GraphQLRequestBody | GraphQLRequestBody[]
) => ReturnType<typeof processRequestBody>
>;
}
}

export const createInternalCmsQuerying = () => {
return [
// First we need to expose a utility to access the executable CMS schema.
new ContextPlugin<CmsContext>(context => {
const getLastModifiedTime = () => {
return context.cms.getModelLastChange();
};

const getTenant = () => {
return context.tenancy.getCurrentTenant();
};

const getLocale = () => {
const locale = context.i18n.getContentLocale();
if (!locale) {
throw new WebinyError(
"Missing locale on context.i18n locale in File Manager API.",
"LOCALE_ERROR"
);
}
return locale;
};

context.cms.getExecutableSchema = async (type: ApiEndpoint) => {
const originalType = context.cms.type;
context.cms.type = type;
const schema = await context.security.withoutAuthorization(() => {
return getSchema({
context,
getTenant,
getLastModifiedTime,
getLocale,
type
});
});

context.cms.type = originalType;

return async (input: GraphQLRequestBody | GraphQLRequestBody[]) => {
return await context.security.withoutAuthorization(() =>
processRequestBody(input, schema, context)
);
};
};
}),
// Now we expose a new GraphQL query, which allows us to execute an arbitrary query, sent as an input.
new GraphQLSchemaPlugin<CmsContext>({
typeDefs: /* GraphQL */ `
type GetDynamicPageDataResponse {
data: JSON
error: CmsError
}

input GetDynamicPageDataFilterItemInput {
path: String!
condition: String!
value: String!
}

input GetDynamicPageDataFilterInput {
filters: [GetDynamicPageDataFilterItemInput!]!
filterCondition: String
}

input GetDynamicPageDataSortInput {
path: String!
direction: String!
}

input GetDynamicPageDataWhereInput {
entryId: String
}

extend type Query {
getDynamicPageData(
modelId: String!
paths: [String!]
filter: GetDynamicPageDataFilterInput
sort: [GetDynamicPageDataSortInput!]
limit: Number
where: GetDynamicPageDataWhereInput
isPreviewEndpoint: Boolean
): GetDynamicPageDataResponse
}
`,
resolvers: {
Query: {
async getDynamicPageData(_, args, context) {
const { modelId, paths, filter, sort, limit, where, isPreviewEndpoint } =
args as {
modelId: string;
paths: string[];
filter?: Filter;
sort?: Sort[];
limit?: number;
where?: {
entryId?: string;
};
isPreviewEndpoint?: boolean;
};

try {
const model = await context.security.withoutAuthorization(async () => {
return await context.cms.getModel(modelId);
});

if (!model) {
return new NotFoundResponse("Model not found.");
}

const query = await context.security.withoutAuthorization(async () => {
return await buildDynamicPageDataQuery({
context,
model,
paths,
filter,
sort,
limit,
additionalWhere: where
});
});

// Get a schema for a desired CMS endpoint: `read`, `manage`, or `preview`.
const querySchema = await context.cms.getExecutableSchema(
isPreviewEndpoint ? "preview" : "read"
);
const data = await querySchema({
// Since the operation name must match the one below, let's wrap the query into a named operation.
query: `query RunQuery ${query}`,
// Variables can be passed programmatically, from GraphQL, or from block settings.
variables: {},
// GraphQL expects an operation name, so let's have a dummy name like this.
operationName: "RunQuery"
});

return new Response(data);
} catch (err) {
return new ErrorResponse(err);
}
}
}
}
})
];
};
1 change: 1 addition & 0 deletions packages/api-page-builder/src/plugins/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./ContentCompressionPlugin";
export * from "./JsonpackContentCompressionPlugin";
export * from "./PageBuilderPageValidationModifierPlugin";
export * from "./cmsQueryingPlugin";
13 changes: 12 additions & 1 deletion packages/api-page-builder/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -899,11 +899,22 @@ export interface PageTemplate {
createdBy: CreatedBy;
tenant: string;
locale: string;
dynamicSource?: {
modelId: string;
entryId?: string;
};
}

export type PageTemplateInput = Pick<
PageTemplate,
"title" | "description" | "content" | "slug" | "tags" | "layout" | "pageCategory"
| "title"
| "description"
| "content"
| "slug"
| "tags"
| "layout"
| "pageCategory"
| "dynamicSource"
> & { id?: string };

/**
Expand Down