Skip to content
Snippets Groups Projects
Commit c3118718 authored by Julien MARGAIL's avatar Julien MARGAIL
Browse files

rename package namespace, up readme

parent 1f1ff224
No related branches found
No related tags found
No related merge requests found
const ms = require('ms');
const chalk = require('chalk');
const { Directus } = require('@directus/sdk');
const { sourceNodes, createSchemaCustomization } = require('gatsby-source-graphql/gatsby-node');
const { createRemoteFileNode } = require('gatsby-source-filesystem');
const ms = require("ms");
const chalk = require("chalk");
const { Directus } = require("@directus/sdk");
const {
sourceNodes,
createSchemaCustomization,
} = require("gatsby-source-graphql/gatsby-node");
const { createRemoteFileNode } = require("gatsby-source-filesystem");
/**
* Validate plugin options
*/
exports.pluginOptionsSchema = ({ Joi }) => {
return Joi.object().keys({
url: Joi.string().required(),
auth: Joi.object()
.keys({
token: Joi.string(),
email: Joi.string(),
password: Joi.string(),
})
.with('email', 'password')
.with('password', 'email')
.xor('token', 'email'),
type: Joi.object()
.keys({
name: Joi.string(),
field: Joi.string(),
})
.optional(),
dev: Joi.object().keys({
refresh: [Joi.number(), Joi.string()],
}),
graphql: Joi.object(),
});
return Joi.object().keys({
url: Joi.string().required(),
auth: Joi.object()
.keys({
token: Joi.string(),
email: Joi.string(),
password: Joi.string(),
})
.with("email", "password")
.with("password", "email")
.xor("token", "email"),
type: Joi.object()
.keys({
name: Joi.string(),
field: Joi.string(),
})
.optional(),
dev: Joi.object().keys({
refresh: [Joi.number(), Joi.string()],
}),
graphql: Joi.object(),
});
};
/**
* Gatsby source implementation.
*/
exports.sourceNodes = async (gatsbyOptions, pluginOptions) => {
await plugin.setOptions(pluginOptions);
await plugin.setOptions(pluginOptions);
const optionsSystem = plugin.getOptionsSystem();
const options = plugin.getOptions();
const optionsSystem = plugin.getOptionsSystem();
const options = plugin.getOptions();
const createNode = gatsbyOptions.actions.createNode;
const createNode = gatsbyOptions.actions.createNode;
// Avoid type conflict with gatsby-source-graphql
gatsbyOptions.actions.createNode = (node) => {
if (node.internal.type === 'GraphQLSource') {
if (node.typeName === optionsSystem.typeName) node.internal.type = 'DirectusSystemGraphQLSource';
if (node.typeName === options.typeName) node.internal.type = 'DirectusGraphQLSource';
}
// Avoid type conflict with gatsby-source-graphql
gatsbyOptions.actions.createNode = (node) => {
if (node.internal.type === "GraphQLSource") {
if (node.typeName === optionsSystem.typeName)
node.internal.type = "DirectusSystemGraphQLSource";
if (node.typeName === options.typeName)
node.internal.type = "DirectusGraphQLSource";
}
return createNode(node);
};
return createNode(node);
};
await sourceNodes(gatsbyOptions, optionsSystem);
await sourceNodes(gatsbyOptions, options);
await sourceNodes(gatsbyOptions, optionsSystem);
await sourceNodes(gatsbyOptions, options);
};
exports.createSchemaCustomization = async (gatsby, pluginOptions) => {
await plugin.setOptions(pluginOptions);
await plugin.setOptions(pluginOptions);
await createSchemaCustomization(gatsby, plugin.getOptionsSystem());
await createSchemaCustomization(gatsby, plugin.getOptions());
await createSchemaCustomization(gatsby, plugin.getOptionsSystem());
await createSchemaCustomization(gatsby, plugin.getOptions());
};
/**
* Gatsby file implementation.
*/
exports.createResolvers = async ({ actions, cache, createNodeId, createResolvers, store, reporter }, pluginOptions) => {
await plugin.setOptions(pluginOptions);
const { createNode } = actions;
const { headers } = await plugin.getOptions();
const { Authorization } = await headers();
const fileResolver = {
imageFile: {
type: `File`,
async resolve(source) {
if (!source || !source.id) return null;
let filename_download = plugin.fileCache.get(source.id);
if (!filename_download) {
if (source.filename_download) filename_download = source.filename_download;
else ({ filename_download } = await plugin.directus.files.readOne(source.id));
plugin.fileCache.set(source.id, filename_download);
}
const nameParts = filename_download.split('.');
const ext = nameParts.length > 1 ? `.${nameParts.pop()}` : '';
const name = nameParts.join('.');
return createRemoteFileNode({
url: `${plugin.url}assets/${source.id}`,
store,
cache,
createNode,
createNodeId,
httpHeaders: { Authorization },
reporter,
ext,
name,
});
},
},
};
await createResolvers({
DirectusData_directus_files: fileResolver,
DirectusSystemData_directus_files: fileResolver,
});
exports.createResolvers = async (
{ actions, cache, createNodeId, createResolvers, store, reporter },
pluginOptions
) => {
await plugin.setOptions(pluginOptions);
const { createNode } = actions;
const { headers } = await plugin.getOptions();
const { Authorization } = await headers();
const fileResolver = {
imageFile: {
type: `File`,
async resolve(source) {
if (!source || !source.id) return null;
let filename_download = plugin.fileCache.get(source.id);
if (!filename_download) {
if (source.filename_download)
filename_download = source.filename_download;
else
({ filename_download } = await plugin.directus.files.readOne(
source.id
));
plugin.fileCache.set(source.id, filename_download);
}
const nameParts = filename_download.split(".");
const ext = nameParts.length > 1 ? `.${nameParts.pop()}` : "";
const name = nameParts.join(".");
return createRemoteFileNode({
url: `${plugin.url}assets/${source.id}`,
store,
cache,
createNode,
createNodeId,
httpHeaders: { Authorization },
reporter,
ext,
name,
});
},
},
};
await createResolvers({
DirectusData_directus_files: fileResolver,
DirectusSystemData_directus_files: fileResolver,
});
};
class Plugin {
constructor() {
// eslint-disable-next-line no-undef
this.fileCache = new Map();
this.directus = null;
this.options = null;
this.urlGraphqlSystem = '';
this.urlGraphql = '';
this.url = '';
this.refreshInterval = 0;
this.authPromise = null;
}
async setOptions(options) {
const { url, dev, auth } = options;
if (isEmpty(url)) error('"url" must be defined');
if (this.directus) return this.authPromise;
const hasAuth = !!auth;
const hasToken = !isEmpty(auth?.token);
const hasEmail = !isEmpty(auth?.email);
const hasPassword = !isEmpty(auth?.password);
const hasCredentials = hasEmail && hasPassword;
if (hasAuth) {
if (!hasToken && !hasCredentials) error('"auth.token" or ("auth.email" and "auth.password") must be defined');
} else warning('no "auth" option were defined. Resources will be fetched with public role');
try {
const baseUrl = new URL(url);
const basePath = baseUrl.pathname;
baseUrl.pathname = basePath;
this.url = baseUrl.toString();
baseUrl.pathname = basePath + '/graphql';
this.urlGraphql = baseUrl.toString();
baseUrl.pathname = basePath + '/graphql/system';
this.urlGraphqlSystem = baseUrl.toString();
} catch (err) {
error('"url" should be a valid URL');
}
try {
this.directus = new Directus(this.url);
if (hasToken) this.authPromise = await this.directus.auth.static(auth.token);
if (hasCredentials)
this.authPromise = await this.directus.auth.login({ email: auth?.email, password: auth?.password });
} catch (err) {
error(`authentication failed with: ${err.message}\nAre credentials valid?`);
}
this.refreshInterval = typeof dev?.refresh === 'string' ? ms(dev.refresh) / 1000 : dev?.refresh || 15;
if (Number.isNaN(this.refreshInterval))
error('"dev.refresh" should be a number in seconds or a string with ms format, i.e. 5s, 5m, 5h, ...');
this.options = options;
return this.authPromise;
}
getOptions() {
const internalOptions = ['url', 'dev', 'auth', 'type'];
const gatsbyPluginOptions = Object.fromEntries(
Object.entries(this.options).flatMap(([key, value]) => (internalOptions.includes(key) ? [] : [[key, value]]))
);
return {
...this.options.graphql,
...gatsbyPluginOptions,
url: this.urlGraphql,
typeName: this.options?.type?.name || 'DirectusData',
fieldName: this.options?.type?.field || 'directus',
headers: this.headers.bind(this),
};
}
getOptionsSystem() {
const options = this.getOptions();
return {
...options,
url: this.urlGraphqlSystem,
typeName: this.options?.type?.system_name || 'DirectusSystemData',
fieldName: this.options?.type?.system_field || 'directus_system',
};
}
async headers() {
let headers = {};
if (typeof this.options?.headers === 'object') {
Object.assign(headers, this.options.headers || {});
} else if (typeof this.options?.headers === 'function') {
Object.assign(headers, (await this.options.headers()) || {});
}
if (this.directus.auth.token) {
Object.assign(headers, {
Authorization: `Bearer ${this.directus.auth.token}`,
});
}
return headers;
}
constructor() {
// eslint-disable-next-line no-undef
this.fileCache = new Map();
this.directus = null;
this.options = null;
this.urlGraphqlSystem = "";
this.urlGraphql = "";
this.url = "";
this.refreshInterval = 0;
this.authPromise = null;
}
async setOptions(options) {
const { url, dev, auth } = options;
if (isEmpty(url)) error('"url" must be defined');
if (this.directus) return this.authPromise;
const hasAuth = !!auth;
const hasToken = !isEmpty(auth?.token);
const hasEmail = !isEmpty(auth?.email);
const hasPassword = !isEmpty(auth?.password);
const hasCredentials = hasEmail && hasPassword;
if (hasAuth) {
if (!hasToken && !hasCredentials)
error(
'"auth.token" or ("auth.email" and "auth.password") must be defined'
);
} else
warning(
'no "auth" option were defined. Resources will be fetched with public role'
);
try {
// Forked here to preserve basePath
const baseUrl = new URL(url);
const basePath = baseUrl.pathname;
baseUrl.pathname = basePath;
this.url = baseUrl.toString();
baseUrl.pathname = basePath + "/graphql";
this.urlGraphql = baseUrl.toString();
baseUrl.pathname = basePath + "/graphql/system";
this.urlGraphqlSystem = baseUrl.toString();
//
} catch (err) {
error('"url" should be a valid URL');
}
try {
this.directus = new Directus(this.url);
if (hasToken)
this.authPromise = await this.directus.auth.static(auth.token);
if (hasCredentials)
this.authPromise = await this.directus.auth.login({
email: auth?.email,
password: auth?.password,
});
} catch (err) {
error(
`authentication failed with: ${err.message}\nAre credentials valid?`
);
}
this.refreshInterval =
typeof dev?.refresh === "string"
? ms(dev.refresh) / 1000
: dev?.refresh || 15;
if (Number.isNaN(this.refreshInterval))
error(
'"dev.refresh" should be a number in seconds or a string with ms format, i.e. 5s, 5m, 5h, ...'
);
this.options = options;
return this.authPromise;
}
getOptions() {
const internalOptions = ["url", "dev", "auth", "type"];
const gatsbyPluginOptions = Object.fromEntries(
Object.entries(this.options).flatMap(([key, value]) =>
internalOptions.includes(key) ? [] : [[key, value]]
)
);
return {
...this.options.graphql,
...gatsbyPluginOptions,
url: this.urlGraphql,
typeName: this.options?.type?.name || "DirectusData",
fieldName: this.options?.type?.field || "directus",
headers: this.headers.bind(this),
};
}
getOptionsSystem() {
const options = this.getOptions();
return {
...options,
url: this.urlGraphqlSystem,
typeName: this.options?.type?.system_name || "DirectusSystemData",
fieldName: this.options?.type?.system_field || "directus_system",
};
}
async headers() {
let headers = {};
if (typeof this.options?.headers === "object") {
Object.assign(headers, this.options.headers || {});
} else if (typeof this.options?.headers === "function") {
Object.assign(headers, (await this.options.headers()) || {});
}
if (this.directus.auth.token) {
Object.assign(headers, {
Authorization: `Bearer ${this.directus.auth.token}`,
});
}
return headers;
}
}
class Log {
static log(level, message) {
let color = level === 'error' ? 'red' : level === 'warning' ? 'yellow' : 'white';
// eslint-disable-next-line no-console
console.log(chalk.cyan('gatsby-source-directus'), ':', chalk[color](message));
}
static error(message) {
Log.log('error', message);
}
static warning(message) {
Log.log('error', message);
}
static log(level, message) {
let color =
level === "error" ? "red" : level === "warning" ? "yellow" : "white";
// eslint-disable-next-line no-console
console.log(
chalk.cyan("gatsby-source-directus"),
":",
chalk[color](message)
);
}
static error(message) {
Log.log("error", message);
}
static warning(message) {
Log.log("error", message);
}
}
function isEmpty(value) {
if (value?.constructor === String) return value.length === 0;
if (value?.constructor === String) return value.length === 0;
return true;
return true;
}
function error(message) {
Log.error(message);
Log.error(message);
const error = new Error(`gatsby-source-directus: ${message}`);
error.stack = undefined;
const error = new Error(`gatsby-source-directus: ${message}`);
error.stack = undefined;
throw error;
throw error;
}
function warning(message) {
Log.warning(message);
Log.warning(message);
}
const plugin = new Plugin();
{
"name": "@directus/gatsby-source-directus",
"version": "9.14.1",
"description": "Source plugin for pulling data into Gatsby from a Directus API.",
"author": "João Biondo <wolfulus@gmail.com>",
"license": "MIT",
"keywords": [
"gatsby",
"gatsby-plugin",
"directus"
],
"dependencies": {
"@directus/sdk": "9.14.1",
"gatsby-source-filesystem": "4.13.0",
"gatsby-source-graphql": "4.13.0",
"ms": "2.1.3"
},
"repository": "directus/gatsby-source-directus",
"bugs": {
"url": "https://github.com/directus/directus/issues"
},
"gitHead": "24621f3934dc77eb23441331040ed13c676ceffd"
"name": "@onegeo/gatsby-source-directus",
"version": "9.14.1",
"description": "Source plugin for pulling data into Gatsby from a Directus API.",
"author": "João Biondo <wolfulus@gmail.com>",
"license": "MIT",
"keywords": [
"gatsby",
"gatsby-plugin",
"directus"
],
"dependencies": {
"@directus/sdk": "9.14.1",
"gatsby-source-filesystem": "4.13.0",
"gatsby-source-graphql": "4.13.0",
"ms": "2.1.3"
},
"repository": "onegeo/gatsby-source-directus",
"bugs": {
"url": "https://github.com/directus/directus/issues"
},
"gitHead": "24621f3934dc77eb23441331040ed13c676ceffd"
}
......@@ -2,10 +2,12 @@
Source plugin for pulling data into Gatsby from a Directus API.
Forked from https://github.com/directus/gatsby-source-directus
## Install
```
npm install --save @directus/gatsby-source-directus
npm install --save @onegeo/gatsby-source-directus
```
## Usage
......@@ -15,31 +17,31 @@ npm install --save @directus/gatsby-source-directus
```js
module.exports = {
// ... some gatsby configuration
plugins: [
// ... some gatsby plugins
// You can take advantage of the following plugins with gatsby-source-directus
// `gatsby-plugin-image`,
// `gatsby-transformer-sharp`,
// `gatsby-plugin-sharp`,
// Finally our plugin
{
resolve: '@directus/gatsby-source-directus',
options: {
url: `https://myproject.directus.cloud`, // Fill with your Directus instance address
auth: {
token: 'my_secret_token', // You can use a static token from an user
// Or you can use the credentials of an user
// email: "johndoe@directus.cloud",
// password: "mysecretpassword",
},
},
},
],
// ... some gatsby configuration
plugins: [
// ... some gatsby plugins
// You can take advantage of the following plugins with gatsby-source-directus
// `gatsby-plugin-image`,
// `gatsby-transformer-sharp`,
// `gatsby-plugin-sharp`,
// Finally our plugin
{
resolve: "@onegeo/gatsby-source-directus",
options: {
url: `https://myproject.directus.cloud/directus`, // Fill with your Directus instance address
auth: {
token: "my_secret_token", // You can use a static token from an user
// Or you can use the credentials of an user
// email: "johndoe@directus.cloud",
// password: "mysecretpassword",
},
},
},
],
};
```
......@@ -47,42 +49,42 @@ module.exports = {
```graphql
query {
# if you change `type.name`, remember to also rename following field
directus {
# the collection you want to query
articles {
# the fields you want to query from above collection
title
files {
# since this is a M2M relationship, we need to reference the junction field
directus_files_id {
# `id` is required to be fetched in order to be used with `gatsby-transformer-sharp`
id
imageFile {
# when using the plugin 'gatsby-transformer-sharp', you can query images with transformations
childImageSharp {
gatsbyImageData(width: 200)
}
}
}
}
}
}
# it's also possible to query system collections
directus_system {
users {
email
}
files {
id
imageFile {
childImageSharp {
gatsbyImageData(width: 200)
}
}
}
}
# if you change `type.name`, remember to also rename following field
directus {
# the collection you want to query
articles {
# the fields you want to query from above collection
title
files {
# since this is a M2M relationship, we need to reference the junction field
directus_files_id {
# `id` is required to be fetched in order to be used with `gatsby-transformer-sharp`
id
imageFile {
# when using the plugin 'gatsby-transformer-sharp', you can query images with transformations
childImageSharp {
gatsbyImageData(width: 200)
}
}
}
}
}
}
# it's also possible to query system collections
directus_system {
users {
email
}
files {
id
imageFile {
childImageSharp {
gatsbyImageData(width: 200)
}
}
}
}
}
```
......@@ -135,14 +137,14 @@ The default way to query data is to fetch items from `directus` field.
```graphql
query {
directus {
items {
my_collection {
some_field
other_field
}
}
}
directus {
items {
my_collection {
some_field
other_field
}
}
}
}
```
......@@ -150,28 +152,28 @@ If you specify the `type.field`, you must query from that field instead.
```graphql
query {
# In this case `type.field` is "blog"
blog {
items {
posts {
id
title
slug
status
}
}
}
# While in this case `type.field` is "portal"
portal {
items {
pages {
id
title
slug
status
}
}
}
# In this case `type.field` is "blog"
blog {
items {
posts {
id
title
slug
status
}
}
}
# While in this case `type.field` is "portal"
portal {
items {
pages {
id
title
slug
status
}
}
}
}
```
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment