diff --git a/docs/api.adoc b/docs/api.adoc deleted file mode 100644 index 82c85ba4..00000000 --- a/docs/api.adoc +++ /dev/null @@ -1,648 +0,0 @@ -= Guillotine API - -This documentation lists and describes all static GraphQL types - -== Common arguments - -|=== -|Name | Remark | Default value - -|key -|Designs a content path or content ID -| - -|offset -|Start index -|0 - -|first -|Number of content to fetch -|10 - -|sort -|Sorting expression -|"_score DESC" -|=== - -== Object Types -=== Query - -==== Fields -|=== -|Name(Arguments):Type | Description - -|guillotine: <> -|Type gathering all content retrieval requests -|=== - -=== HeadlessCms - -Gathers all content retrieval requests. - -==== Remarks - -* Most requests expect a key as argument. If the key is not specified, then the matched content will be the one corresponding to the current execution context - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|get(key: ID): <> -|Fetches a content - -|getChildren( - key: ID, - offset: Int, - first: Int, - sort: String - ): [<>] -|Fetches the children of a content - -|getChildrenConnection( - key: ID, - after: String, - first: Int, - sort: String - ): <> -| Fetches the children of a content and return the result as a connection - -|getPermissions(key: ID): <> -|Fetches the permissions on a content - -|getSite: portal_Site -|Fetches the parent site of the content corresponding to the current execution context - -|query( - query: String, - offset: Int, - first: Int, - sort: String, - contentTypes: [String] - ): [<>] -|Queries contents - -|queryConnection( - query: String!, - after: String, - first: Int, - sort: String, - contentTypes: [String] - ): <> -|Queries contents and returns the result as a connection - -|getType(name: String!): <> -|Retrieves the properties and icon of the specified content type - -|getTypes: [<>] -|Retrieves the list of all content types available for the current site -|=== - -=== Content - -Interface of all contents. - -==== Fields -|=== -|Name(Arguments):Type | Description - -|_id: ID! -|Content ID - -|_name: String! -|Content name - -|_path: String! -|Content path - -|creator: <> -|Content creator - -|modifier: <> -|Last content modifier - -|createdTime: String -|Content creation time - -|modifiedTime: String -|Last content modification time - -|owner: <> -|Content owner - -|type: String -|Content type - -|displayName: String -|Content display name - -|hasChildren: Boolean -|true if the content has children content - -|language: String -|Content language - -|valid: Boolean -|true if the content is valid - -|x: [<>] -|Content extra data - -|pageAsJson: String -|Page specific information - -|pageTemplate: <> -|Related page template content - -|components: [<>] -|Flattened list of page components - -|attachments: [<>] -|Content attachments - -|publish: <> -|Publication information - -|pageUrl(type: <>, params: String): String -|Util field to generate a URL pointing to the content - -|site: portal_Site -|Link to the nearest site - -|parent: <> -|Link to the content parent - -|children( -offset: Int, -first: Int, -sort: String -): [<>] -|Link to the direct child contents - -|childrenConnection( -after: String, -first: Int, -sort: String -): <> -|Connection to the direct child contents - -|permissions: <> -|Content permissions -|=== - -==== Direct Known Subtypes - -Direct Known Subtypes: -portal_Site -, base_Folder -, media_Code -, media_Executable -, media_Text -, media_Vector -, media_Spreadsheet -, media_Data -, base_Structured -, base_Shortcut -, base_Media -, media_Document -, media_Video -, media_Presentation -, media_Archive -, media_Audio -, portal_PageTemplate -, media_Unknown -, portal_TemplateFolder -, media_Image -, base_Unstructured -, portal_Fragment - -=== ContentConnection - -To ease the pagination of relationship to a content, the type ContentConnection has been created to represent this connection. -It allows you to : - -* Paginate through the list of contents. -* Ask for information about the connection itself, like totalCount or pageInfo. -* Ask for information about the edge itself, like cursor. - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|totalCount: Int! -|Total number of related content - -|edges: [<>] -|Edge to the related content - -|pageInfo: <> -|Paging information - -|=== - -=== ContentEdge - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|node: <>! -|Related content - -|cursor: String! -|Edge cursor - -|=== - -=== PageInfo - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|startCursor: String! -|Start cursor of the pagination - -|endCursor: String! -|End cursor of the pagination - -|hasNext: Boolean! -|Has more related contents at end cursor - -|=== - -=== Permissions - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|inheritsPermissions: Boolean -|Inherit permissions from parent content - -|permissions: [<>] -|Permissions - -|=== - -=== AccessControlEntry - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|principal: <> -|Principal key - -|allow: [<>] -|Allowed permissions - -|deny: [<>] -|Denied permissions - -|=== - -=== PrincipalKey - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|value: String -|Principal key value - -|type: <> -|Principal type - -|idProvider: String -|ID Provider name - -|principalId: String -|Principal ID inside this user store - -|=== - -=== ContentType - -Representation of a content type definition - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|name: String -|Content type name - -|displayName: String -|Content type display name - -|description: String -|Content type description - -|superType: String -|Parent content type - -|abstract: Boolean -|true if the content type is abstract - -|final: Boolean -|true if the content type cannot be used as super type - -|allowChildContent: Boolean -|true if content can be added under a content of this type - -|contentDisplayNameScript: String -| - -|icon: Icon -| - -|form: [<>] -|Content type fields schema - -|getInstances( -offset: Int -, first: Int -, query: String -, sort: String -): [<>] -|Util field returning the contents of this type - -|getInstanceConnection( -after: String -, first: Int -, query: String -, sort: String -): <> -|Util field returning the contents of this type as a connection - -|=== - -=== FormItem - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|formItemType: <> -|Form item type - -|name: String -|Form item name - -|label: String -|Form item label - -|=== - -==== Direct Known Subtypes - -Direct Known Subtypes: FormInput -, FormOptionSet -, FormLayout -, FormItemSet - -=== ExtraData - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|name: String -|Mixin name - -|data: String -|Mixin value - -|=== - -=== Component - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|path: String -|Component path - -|type: <> -|Component type - -|page: <> -|Data for page components - -|layout: <> -|Data for layout components - -|image: <> -|Data for image components - -|part: <> -|Data for part components - -|text: <> -|Data for text components - -|fragment: <> -|Data for fragment components - -|=== - -=== PageComponentData - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|descriptor: String -|Controller descriptor - -|customized: Boolean -|True if the page is customized - -|configAsJson: String -|Component config - -|template: <> -|Related template content - -|=== - -=== LayoutComponentData - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|descriptor: String! -|Controller descriptor - -|configAsJson: String -|Component config - -|=== - -=== ImageComponentData - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|id: ID! -|Image key - -|caption: String -|Image caption - -|image: media_Image -|Related image content - -|=== - -=== PartComponentData - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|descriptor: String! -|Controller descriptor - -|configAsJson: String -|Component config - -|=== - -=== TextComponentData - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|value: String! -|Text value - -|=== - -=== FragmentComponentData - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|id: ID! -|Fragment key - -|fragment: Content -|Related fragment content - -|=== - -=== Attachment - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|name: String -|Attachment name - -|label: String -|Attachment label - -|size: Int -|Attachment size - -|mimeType: String -|Attachment mime type - -|=== - -=== PublishInfo - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|from: String -|Scheduled publication start time - -|to: String -|Scheduled publication end time - -|first: String -|First publication time - -|=== - -=== Icon - -==== Fields - -|=== -|Name(Arguments):Type | Description - -|mimeType: String -|Icon mime type - -|modifiedTime: String -|Icon last modification time - -|=== - -== Enum Types - -=== PrincipalType - -Enum values: user -, group -, role - -=== Permission - -Enum values: READ -, CREATE -, MODIFY -, DELETE -, PUBLISH -, READ_PERMISSIONS -, WRITE_PERMISSIONS - -=== FormItemType - -Enum values: ItemSet -, Layout -, Input -, OptionSet - -=== UrlTypeType - -Enum values: server -, absolute - -=== ComponentType - -Enum values: page -, layout -, image -, part -, text -, fragment \ No newline at end of file diff --git a/docs/custom.adoc b/docs/custom.adoc deleted file mode 100644 index 89a97d93..00000000 --- a/docs/custom.adoc +++ /dev/null @@ -1,144 +0,0 @@ -= Customizing the schema - -This section describes how to customize your schema deployment - -== Integrating Guillotine to an existing schema - -You may have an existing GraphQL schema and wish to integrate Guillotine to this schema. -In that case, use the functions `createContext` and `createHeadlessCmsType` instead of `createSchema` - -=== Example - -./services/graphql/graphql.js -[source,javascript] ----- -var guillotineLib = require('/lib/guillotine'); -var graphQlLib = require('/lib/graphql'); - -var schema = createSchema(); - -exports.post = function (req) { - var body = JSON.parse(req.body); - var result = graphQlLib.execute(schema, body.query, body.variables); - return { - contentType: 'application/json', - body: result - }; -}; - -function createSchema() { - var context = guillotineLib.createContext(); <1> - return graphQlLib.createSchema({ - query: createRootQueryType(context), - dictionary: context.dictionary <2> - }); -} - -function createRootQueryType(context) { - return graphQlLib.createObjectType({ - name: 'Query', - fields: { - guillotine: { - type: guillotineLib.createHeadlessCmsType(context), <3> - resolve: function () { - return {}; - } - } - } - }); -} ----- -<1> Creates the context necessary to create Guillotine types. -<2> Passes the dictionary to the schema creation. The use of a dictionary is necessary to define interface implementations. -<3> Creates the Guillotine Headless CMS type - - -== Extending the schema - -The default Guillotine schema provides fields based on the content type schemas. -But you might want to add/modify/delete fields (new retrieval methods, search in a separate database, virtual fields, ...). - -The guillotine schema is entirely configurable. -When creating a schema, you can define a listener for a GraphQL type. -This listener will be called before the type is created and given the possibility to modify the type creation parameters. - -[NOTE] -.GraphQL - Enonic XP Library -==== -A library has been implemented to facilitate the creation of a GraphQL service on Enonic XP: `lib-graphql`. -Guillotine uses this library internally. The comprehension of the library is not necessary if you are using the default schema of Guillotine. -But if you decide to extend the schema with custom types, we recommend you to learn more about it by following the link below: - -https://github.com/enonic/lib-graphql[Learn more about the GraphQL library...] -==== - -=== Example - -In this example, we have the types Author and Post generated. But we wish to apply the following modifications: - -* Author should have a new field "fullName" that is the concatenation of firstName and lastName -* Author data field "email" should require admin rights to be retrieved. -* Author data field "birthDate" should not be accessible through the GraphQL API. -* Author should have a new field "posts" returning all the blog posts written by an author - -./services/graphql/graphql.js -[source,javascript] ----- -var contentLib = require('/lib/xp/content'); -var guillotineLib = require('/lib/guillotine'); -var graphQlLib = require('/lib/graphql'); - -var schema = guillotineLib.createSchema({ - creationCallbacks: { - 'com_enonic_app_myapp_Author_Data': function(context, params){ <1> - params.fields.fullName = { <2> - type: graphQlLib.GraphQLString, - resolve: function (env) { - return env.source.firstName + ' ' + env.source.lastName; - } - }; - params.fields.email.resolve = function (env) { <3> - return authLib.hasRole('system.admin') ? env.source.email : null - }; - delete params.fields.birthDate; <4> - }, - 'com_enonic_app_myapp_Author': function(context, params){ <1> - params.fields.posts = { <5> - type: graphQlLib.list(graphQlLib.reference('com_enonic_app_myapp_Post')), - resolve: function (env) { - return contentLib.query({ - contentTypes: [app.name + ":Post"], - filters: { - hasValue: { - field: "data.author", - values: [env.source._id] - } - } - }).hits; - } - }; - } - } -); - -exports.post = function (req) { - var body = JSON.parse(req.body); - var result = graphQlLib.execute(schema, body.query, body.variables); - return { - contentType: 'application/json', - body: result - }; -}; ----- -<1> Passes a callback that will be called before the creation of the specified GraphQL type. -It receives the Guillotine context and the object type creation parameters. -<2> Adds a new string field "fullName" concatenating two other fields. -The resolution function will query contents of type post having the current author ID as field "data.author" -<3> Overwrites the resolution function of an existing field "email" -<4> Deletes an existing field "birthDate" -<5> Adds a new field "posts" returning a list of posts. - - -These are only examples. You could also modify the type 'Query' and add an entire new API next to the Headless CMS API. - - diff --git a/docs/images/configuration.png b/docs/images/configuration.png deleted file mode 100644 index 5b6fe0d6..00000000 Binary files a/docs/images/configuration.png and /dev/null differ diff --git a/docs/images/graphiql-example.png b/docs/images/graphiql-example.png deleted file mode 100644 index 17803a08..00000000 Binary files a/docs/images/graphiql-example.png and /dev/null differ diff --git a/docs/images/graphiql.png b/docs/images/graphiql.png deleted file mode 100644 index 6e30b16b..00000000 Binary files a/docs/images/graphiql.png and /dev/null differ diff --git a/docs/images/installation.png b/docs/images/installation.png deleted file mode 100644 index 47cf72cb..00000000 Binary files a/docs/images/installation.png and /dev/null differ diff --git a/docs/index.adoc b/docs/index.adoc deleted file mode 100644 index fc6b9074..00000000 --- a/docs/index.adoc +++ /dev/null @@ -1,305 +0,0 @@ -= Guillotine - Headless CMS - -Guillotine exposes the read-only part of the Enonic Content API, including access to the Enonic query language. -Guillotine dynamically analyzes all available content types and generates a GraphQL API specific to your site. -This gives you direct, typed and documented access to all content within the site. Including the ability to follow references, -child items and access media directly. - -== Usage - -The fastest way to set up Guillotine is to download the Guillotine application and associate it to your site. - -But depending on what you intend to achieve, you might prefer using the Guillotine library inside your application instead. -You will want to use the library if: - -* You want your application to be standalone and not require the installation of another application -* You want to extend or modify the GraphQL API generated by Guillotine - - -=== Guillotine application - -==== Step 1: Installation - -* Open the admin tool "Applications" -* Search for the "guillotine" application -* Click on "install" - -image::images/installation.png[Installation,768,384] - -==== Step 2: Configuration - -* Open the admin tool "Content Studio" -* Edit your site -* Add the application "Guillotine" - -You now have a GraphQL endpoint! - -image::images/configuration.png[Installation,768,576] - -=== Guillotine library - -==== Step 1: Include the library - -* Check that the Enonic repository is in the list of repositories - -[source,gradle] ----- -repositories { - maven { - url 'https://repo.enonic.com/public' - } -} ----- - -* Add the following dependency (where `` is the actual version to use): - -[source,gradle] ----- -dependencies { - include 'com.enonic.lib:lib-guillotine:' -} ----- - -==== Step 2: Create a GraphQL endpoint - -* Create an Enonic XP service file `/services/graphql/graphql.js` with the following content: - -./services/graphql/graphql.js -[source,javascript] ----- -var guillotineLib = require('/lib/guillotine'); // <1> -var graphQlLib = require('/lib/graphql'); // <1> - -var schema = guillotineLib.createSchema(); // <2> - -exports.post = function (req) { // <3> - var body = JSON.parse(req.body); // <4> - var result = graphQlLib.execute(schema, body.query, body.variables); // <5> - return { - contentType: 'application/json', - body: JSON.stringify(result) - }; -}; ----- -<1> Requires the Guillotine and GraphQL libraries. -The GraphQL library is already included with Guillotine and does not need to be added to your Gradle file -<2> Creates the GraphQL schema the first time the service is called. -<3> Handles POST requests -<4> Parses the JSON body to retrieve the GraphQL query and variables -<5> Executes the query and variables against the schema created - -You now have a GraphQL endpoint! - - -== Browse you API using GraphiQL - -The easiest way to manually explore a GraphQL API and test GraphQL queries is to use GraphiQL - -[NOTE] -==== -GraphQL is a query language developed by Facebook. -This technology allows Guillotine to provide a single and exhaustive API -while allowing you to retrieve all the content information you need in one request without any superfluous information - -http://graphql.org/learn[Learn more about GraphQL...] -==== - -=== Installation - -* Open the admin tool "Applications" -* Search for the "graphiql" application -* Click on "install" -* Open the admin tool "GraphiQL" - -image::images/graphiql.png[GraphiQL,1024,612] - -=== User Interface - -* The field 'Service Location' specifies the location of the GraphQL service. -* The left panel allows you to edit your graphQL query -* The center panel displays the result of the query execution -* The right panel is used to display a documentation generated from the GraphQL API. - -=== Usage example - -[NOTE] -==== -By default, 'Service Location' is prefilled with the URL of the service 'graphql' for one of your configured sites for the branch "draft". - -To determine the URL of your service: - -* If you are using the Guillotine application, the URL will be: [site-url]/_/service/com.enonic.app.guillotine/graphql -** Example: `+http://localhost:8080/site/default/draft/superhero/_/service/com.enonic.app.guillotine/graphql+` -* If you are using the Guillotine library, the URL will be: [site-url]/_/service/[application]/[service-name] -** Example: `+http://localhost:8080/site/default/draft/superhero/_/service/com.enonic.app.superhero/graphql+` - -==== - -* Write the query below inside the left panel. -+ -This query can be read as: Retrieve the display name and type of the current content. -* Click on the query execution button above -* The service response is displayed in the center panel - ----- -{ - guillotine { - get { - displayName - type - } - } -} ----- - -image::images/graphiql-example.png[Example,1024,612] - -== Accessing the API with Javascript - -To use your GraphQL service, your client will send all its requests to the same service. -The service is expecting to receive a POST request with inside its body: - -* A mandatory "query" String -* An optional "variables" Object - -.Example: Generate the service URL from a controller -[source,javascript] ----- -var portalLib = require('/lib/xp/portal'); -var graphqlServiceUrl = portalLib.serviceUrl({ - service: 'graphql', - application: 'com.enonic.app.guillotine' // <1> -}); ----- -<1> Remove this line if you are using the guillotine library - -.Example: Fetch data from a javascript client -[source,javascript] ----- -const query = `query($path:ID!){ - guillotine { - get(key:$path) { - displayName - type - } - } -}`; - -const variables = { - 'path': '/mysite/mycontentpath' -}; - -fetch('{{graphqlServiceUrl}}', { - method: 'POST', - body: JSON.stringify({ - query: query, - variables: variables - }), - credentials: 'same-origin' -}) - .then(response => response.json()) - .then(console.log); ----- - -== Using the API - -At the root of the default Guillotine schema is a type `Query` with a field `guillotine` of type `HeadlessCms`. -The `HeadlessCms` type gathers fields allowing to retrieve contents or related data. - -=== Content - -The type `Content` is an interface with multiple implementations generated from built-in content types but also from content types defined by your application. -All types implementing `Content` share the same fields at the exception of the field `data` defined for each implementation type. - -=== Relations - -Multiple relations are generated to allow to navigate between contents. -By default, each content has the following relations: - -* parent: Link to the parent content -* children: Link to the child contents -* site: Link to the nearest site content - -Moreover, every ContentSelector, MediaUploader, AttachmentUploader or ImageSelector defined in your content type form will -be converted to a link to the related content(s). - -.Query example: Retrieve the display name of the current content and the display name of its direct children ----- -{ - guillotine { - get { - displayName - children { - displayName - } - } - } -} ----- - - -.Query example: Retrieve the blog posts. For each post, return its display name and the display name of the related author ----- -{ - guillotine { - query(contentTypes:"com.enonic.app.myapp:post") { - displayName - ... on com_enonic_app_myapp_Post { - data { - author { - displayName - } - } - } - } - } -} ----- - -=== Image - -Enonic XP can edit images at runtime. -Guillotine uses this functionality by generating, on every image, a field "imageUrl" generating a URL pointing to the processed image. - - - -.*Example: Scaled Image URL* - Retrieve the image contents and generate absolute URLs to these images cropped to 800x200px ----- -{ - guillotine { - query(contentTypes:"media:image") { - displayName - ... on media_Image { - imageUrl(scale:"block(800,200)",type:absolute) - } - } - } -} ----- - -=== HTML - -HTML fields are generated with a parameter "processHtml" allowing to replace abstract internal links by generated URLs. - -.*Example: Process HTML* - Retrieve the Superhero blog posts. For each post, return its author display name, tags and processed content. ----- -{ - guillotine { - query(contentTypes:"com.enonic.app.myapp:post") { - ... on com_enonic_app_myapp_Post { - data { - author { - displayName - } - tags - post(processHtml:{type:absolute}) - } - } - } - } -} ----- - -== link:custom.adoc[Customizing the schema] - -== link:api.adoc[Guillotine API] \ No newline at end of file diff --git a/docs/menu.json b/docs/menu.json deleted file mode 100644 index f51a8c46..00000000 --- a/docs/menu.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "menuItems": [ - { - "title": "Guillotine API", - "document": "api" - }, - { - "title": "Customizing the schema", - "document": "custom" - } - ] -} \ No newline at end of file