UNPKG

druxt-schema

Version:

Drupal Content Entity schema generator for Druxt with support for View and Form displays.

354 lines (347 loc) 11.3 kB
import consola from 'consola'; import { resolve } from 'path'; import { DrupalJsonApiParams } from 'drupal-jsonapi-params'; import { DruxtClient } from 'druxt'; import { mapActions } from 'vuex'; class Schema { constructor(config, { druxtSchema, data }) { if (config.resourceFields) { this.resourceFields = config.resourceFields; delete config.resourceFields; } this.config = { entityType: "node", bundle: null, mode: "default", schemaType: "view", filter: [], ...config }; if (!this.id && this.config.resourceType) { this.id = [this.config.resourceType, this.config.mode, this.config.schemaType].join("--"); } if (!this.id && this.config.bundle) { this.id = [this.config.entityType, this.config.bundle, this.config.mode, this.config.schemaType].join("--"); } this.isValid = true; if ((this.config.filter || []).length > 0) { this.isValid = false; for (const filter of this.config.filter) { const match = this.id.match(filter); if (match) { this.isValid = true; break; } } } this.displayId = [this.config.entityType, this.config.bundle, this.config.mode].join("."); this.resourceType = [this.config.entityType, this.config.bundle].join("--"); this.data = {}; if (typeof data !== "undefined") { this.data[data.type] = data; } this.fields = {}; this.druxtSchema = druxtSchema; } async generate() { return this[this.config.schemaType](); } async getResources(resource, query) { if (this.data[resource]) return this.data[resource]; this.data[resource] = await this.druxtSchema.druxt.getCollection(resource, query); return this.data[resource]; } async form() { const entityFormDisplay = await this.getResources("entity_form_display--entity_form_display", { "filter[drupal_internal__id]": this.displayId }).then((res) => Array.isArray(res.data) ? res.data[0] : res); if (!entityFormDisplay) return false; const fieldConfig = await this.getResources("field_config--field_config", { "filter[entity_type]": this.config.entityType, "filter[bundle]": this.config.bundle }); if (!fieldConfig) return false; const fieldStorageConfig = await this.getResources("field_storage_config--field_storage_config", { "filter[entity_type]": this.config.entityType }); if (!fieldStorageConfig) return false; for (const field in entityFormDisplay.attributes.content) { const display = { id: null, label: null, type: null, weight: null, settings: {}, third_party_settings: {}, ...entityFormDisplay.attributes.content[field] }; let config = { attributes: {}, ...fieldConfig.data.find((element) => element.attributes.field_name === field) }; config = { description: null, label: null, required: false, settings: {}, ...config.attributes }; let storage = { attributes: {}, ...fieldStorageConfig.data.find((element) => element.attributes.field_name === field) }; storage = { cardinality: null, settings: {}, ...storage.attributes }; let fieldName = field; if (this.resourceFields && this.resourceFields[field] && this.resourceFields[field].publicName !== field) { fieldName = this.resourceFields[field].publicName; } this.fields[fieldName] = { id: fieldName, description: config.description, label: { text: config.label, position: display.label }, cardinality: storage.cardinality, required: config.required, type: display.type, weight: display.weight, settings: { config: config.settings, display: display.settings, storage: storage.settings }, thirdPartySettings: display.third_party_settings }; } this.schema = { id: this.id, resourceType: this.resourceType, fields: Object.values(this.fields).sort((a, b) => a.weight - b.weight), groups: [], config: this.config }; return this.schema; } async view() { const entityViewDisplay = await this.getResources("entity_view_display--entity_view_display", { "filter[drupal_internal__id]": this.displayId }).then((res) => Array.isArray(res.data) ? res.data[0] : res); if (!entityViewDisplay) return false; const fieldConfig = await this.getResources("field_config--field_config", { "filter[entity_type]": this.config.entityType, "filter[bundle]": this.config.bundle }); if (!fieldConfig) return false; for (const field in entityViewDisplay.attributes.content) { const display = { id: null, label: null, type: null, weight: null, settings: {}, third_party_settings: {}, ...entityViewDisplay.attributes.content[field] }; let config = { attributes: {}, ...fieldConfig.data.find((element) => element.attributes.field_name === field) }; config = { description: null, label: null, required: false, settings: {}, ...config.attributes }; let fieldName = field; if (this.resourceFields && this.resourceFields[field] && this.resourceFields[field].publicName !== field) { fieldName = this.resourceFields[field].publicName; } this.fields[fieldName] = { id: fieldName, description: config.description, label: { text: config.label, position: display.label }, required: config.required, type: display.type, weight: display.weight, settings: { config: config.settings, display: display.settings }, thirdPartySettings: display.third_party_settings }; } this.schema = { id: this.id, resourceType: this.resourceType, fields: Object.values(this.fields).sort((a, b) => a.weight - b.weight), groups: [], config: this.config }; return this.schema; } } class DruxtSchema { constructor(baseUrl, options = {}) { if (!baseUrl) throw new Error("The 'baseUrl' parameter is required."); this.options = { auth: { type: false }, schema: { filter: [] }, ...options }; this.druxt = new DruxtClient(baseUrl, this.options); } async get() { const index = await this.druxt.getIndex(); const displays = (await Promise.all([ ...["view", "form"].map(async (schemaType) => { const resourceType = `entity_${schemaType}_display--entity_${schemaType}_display`; const query = new DrupalJsonApiParams().addSort("drupal_internal__id"); const result = await this.druxt.getCollectionAll(resourceType, query); try { result.forEach((collection) => this.druxt.checkPermissions({ data: collection })); } catch (err) { this.druxt.error(err); } return result.map((collection) => collection.data.filter((data) => !!data.attributes.status).map((data) => ({ entityType: data.attributes.targetEntityType, bundle: data.attributes.bundle, mode: data.attributes.mode, schemaType, filter: this.options.schema.filter, ...index[[data.attributes.targetEntityType, data.attributes.bundle].join("--")] }))); }) ])).flat(2); const schemas = Object.fromEntries((await Promise.all(displays.map((config) => this.getSchema(config)))).filter((o) => o).map((schema) => [schema.id, schema.schema])); return { index, schemas }; } async getSchema(config, options = {}) { const schema = new Schema(config, { druxtSchema: this, ...options }); if (!schema.isValid) { return false; } await schema.generate(); return schema; } } const DruxtSchemaNuxtModule = function(moduleOptions = {}) { const options = { baseUrl: moduleOptions.baseUrl, ...(this.options || {}).druxt || {}, schema: { ...((this.options || {}).druxt || {}).schema || {}, ...moduleOptions } }; this.addPlugin({ src: resolve(__dirname, "../templates/plugin.js"), fileName: "druxt-schema.js", options }); this.options.store = true; this.addPlugin({ src: resolve(__dirname, "../templates/store.js"), fileName: "store/druxt-schema.js", options }); this.nuxt.hook("builder:prepared", async () => { const druxtSchema = new DruxtSchema(options.baseUrl, { ...options, proxy: { ...options.proxy || {}, api: false } }); const { schemas } = await druxtSchema.get(); if (!Object.entries(schemas).length) { throw new Error("No Druxt Schema files generated.\n Have you created any content types yet?"); } for (const name in schemas) { const schema = schemas[name]; if (typeof schema === "undefined") continue; this.addTemplate({ src: resolve(__dirname, "../templates/schema.json"), fileName: `schemas/${name}.json`, options: { schema } }); } consola.success("Druxt schema files generated"); }); }; DruxtSchemaNuxtModule.meta = require("../package.json"); const DruxtSchemaMixin = { props: { mode: { type: String, default: "default" }, schemaType: { type: String, default: void 0 }, type: { type: String, required: true } }, async fetch() { this.schema = await this.getSchema({ resourceType: this.type, mode: this.mode, schemaType: this.schemaType || "view" }); }, data: () => ({ schema: false }), methods: { ...mapActions({ getSchema: "druxtSchema/get" }) } }; const DruxtSchemaStore = ({ store }) => { if (typeof store === "undefined") { throw new TypeError("Vuex store not found."); } const namespace = "druxtSchema"; const module = { namespaced: true, state: () => ({ schemas: {} }), mutations: { addSchema(state, { id, schema }) { state.schemas[id] = schema; } }, actions: { async get({ state, commit }, resource = {}) { resource = { id: null, resourceType: null, entityType: "node", bundle: null, mode: "default", schemaType: "view", ...resource }; if (!resource.id && resource.resourceType) { resource.id = [resource.resourceType, resource.mode, resource.schemaType].join("--"); } if (!resource.id && resource.bundle) { resource.id = [resource.entityType, resource.bundle, resource.mode, resource.schemaType].join("--"); } if (!resource.id) { return false; } if (!state.schemas[resource.id]) { const schema = await this.$druxtSchema.import(resource.id); commit("addSchema", { id: resource.id, schema }); } return state.schemas[resource.id]; } } }; store.registerModule(namespace, module, { preserveState: Boolean(store.state[namespace]) }); }; export { DruxtSchema, DruxtSchemaMixin, DruxtSchemaStore, DruxtSchemaNuxtModule as default };