fikaproductions / fika-gatsby-source-cockpit

This is a Gatsby version 2.\*.\* source plugin that feeds the GraphQL tree with Cockpit Headless CMS collections and singletons data. Actually, it supports querying raw texts (and any trivial field types), Markdown, images, galleries, assets, sets, repeaters, layout(-grid)s (currently only without nested images/assets), objects, linked collections and internationalization.
GNU General Public License v3.0
23 stars 22 forks source link

Undefined returned when trying to access gallery #36

Closed Hypothesis-github closed 5 years ago

Hypothesis-github commented 5 years ago
Gallery {
            value {
                publicURL
                childImageSharp {
                    fluid {
                      ...GatsbyImageSharpFluid
                    }
                  }

I am not able to log anything out of GatsbyImageSharpFluid

as it return undefined

is there any example in which I can follow to access my images in gallery ?

fluid

Hypothesis-github commented 5 years ago

The problem was these lines in CockpitService.js


        if (path.startsWith('/')) {
          path = `${this.baseUrl}${path}`
        } else if (!path.startsWith('http')) {
          path = `${this.baseUrl}/${path}`
        }

        imageField.value = path
        existingImages[path] = null
      } else {
        const galleryField = field

        galleryField.value.forEach(galleryImageField => {
          let path = galleryImageField.path

          if (path == null) {
            return
          }

          trimGalleryImageField(galleryImageField)

          if (path.startsWith('/')) {
            path = `${this.baseUrl}${path}`
          } else if (!path.startsWith('http')) {
            path = `${this.baseUrl}/${path}`

My baseUrl is not just an http://domain.com but it is rather http://domain.com/cockpit/ so I noticed the images are not being download properly because the base url has /cockpit/ and the path adds another /cockpit to it. so I had to manually add my url and replace all path =${baseUrl}${path}to `path = `${'http://mydomain.com'}${path}

I will try to trim the array and edit this piece.

Hypothesis-github commented 5 years ago

in CockpitService.js

I changed it as such :

const mime = require('mime')
const request = require('request-promise')
const slugify = require('slugify')
const hash = require('string-hash')
var URL = require('url').URL

const {
  METHODS,
  MARKDOWN_IMAGE_REGEXP,
  MARKDOWN_ASSET_REGEXP,
} = require('./constants')
const getFieldsOfTypes = require('./helpers.js').getFieldsOfTypes

module.exports = class CockpitService {
  constructor(baseUrl, token, locales, whiteListedCollectionNames = []) {
    this.baseUrl = baseUrl
    this.myURL = new URL(this.baseUrl)
    this.token = token
    this.locales = locales
    this.whiteListedCollectionNames = whiteListedCollectionNames
  }

  async fetch(endpoint, method, lang = null) {
    return request({
      uri: `${this.baseUrl}/api${endpoint}?token=${this.token}${
        lang ? `&lang=${lang}` : ''
      }`,
      method,
      json: true,
    })
  }

  async validateBaseUrl() {
    try {
      await this.fetch('', METHODS.GET)
    } catch (error) {
      throw new Error(
        'BaseUrl config parameter is invalid or there is no internet connection'
      )
    }
  }

  async validateToken() {
    try {
      await this.fetch('/collections/listCollections', METHODS.GET)
    } catch (error) {
      throw new Error('Token config parameter is invalid')
    }
  }

  async getCollectionNames() {
    return this.fetch('/collections/listCollections', METHODS.GET)
  }

  async getCollection(name) {
    const { fields: collectionFields, entries } = await this.fetch(
      `/collections/get/${name}`,
      METHODS.GET
    )

    const items = entries.map(entry =>
      createCollectionItem(name, collectionFields, entry)
    )

    for (let index = 0; index < this.locales.length; index++) {
      const { fields: collectionFields, entries } = await this.fetch(
        `/collections/get/${name}`,
        METHODS.GET,
        this.locales[index]
      )

      items.push(
        ...entries.map(entry =>
          createCollectionItem(
            name,
            collectionFields,
            entry,
            this.locales[index]
          )
        )
      )
    }

    return { items, name }
  }

  async getCollections() {
    const names = await this.getCollectionNames()
    const whiteListedNames = this.whiteListedCollectionNames

    return Promise.all(
      names
        .filter(
          name =>
            whiteListedNames === null ||
            (Array.isArray(whiteListedNames) &&
              whiteListedNames.length === 0) ||
            whiteListedNames.includes(name)
        )
        .map(name => this.getCollection(name))
    )
  }

  normalizeResources(collections) {
    const existingImages = {}
    const existingAssets = {}
    const existingMarkdowns = {}
    const existingLayouts = {}

    collections.forEach(collection => {
      collection.items.forEach(item => {
        this.normalizeCollectionItemImages(item, existingImages)
        this.normalizeCollectionItemAssets(item, existingAssets)
        this.normalizeCollectionItemMarkdowns(
          item,
          existingImages,
          existingAssets,
          existingMarkdowns
        )
        this.normalizeCollectionItemLayouts(
          item,
          existingImages,
          existingAssets,
          existingMarkdowns,
          existingLayouts
        )
      })
    })

    return {
      images: existingImages,
      assets: existingAssets,
      markdowns: existingMarkdowns,
      layouts: existingLayouts,
    }
  }

  normalizeCollectionItemImages(item, existingImages) {
    getFieldsOfTypes(item, ['image', 'gallery']).forEach(field => {

      if (!Array.isArray(field.value)) {
        const imageField = field
        let path = imageField.value.path

        if (path == null) {
          return
        }

        if (path.startsWith('/')) {
          path = `${this.myURL.origin}${path}`
        } if (!path.startsWith('http')) {
          path = `${this.myURL.origin}/${path}`
        }

        imageField.value = path
        existingImages[path] = null
      } else {
        const galleryField = field

        galleryField.value.forEach(galleryImageField => {
          let path = galleryImageField.path

          if (path == null) {
            return
          }

          trimGalleryImageField(galleryImageField)

          if (path.startsWith('/')) {
            path = `${this.myURL.origin}${path}`
          } if (!path.startsWith('http')) {
            path = `${this.myURL.origin}/${path}`
          }

          galleryImageField.value = path
          existingImages[path] = null
        })
      }
    })

    // Check the child items of the collection for any images
    if (Array.isArray(item.children)) {
      item.children.forEach(child => {
        this.normalizeCollectionItemImages(child, existingImages)
      })
    }
  }

  normalizeCollectionItemAssets(item, existingAssets) {
    getFieldsOfTypes(item, ['asset']).forEach(assetField => {
      let path = assetField.value.path

      trimAssetField(assetField)

      path = `${this.baseUrl}/storage/uploads${path}`

      assetField.value = path
      existingAssets[path] = null
    })

    if (Array.isArray(item.children)) {
      item.children.forEach(child => {
        this.normalizeCollectionItemAssets(child, existingAssets)
      })
    }
  }

  normalizeCollectionItemMarkdowns(
    item,
    existingImages,
    existingAssets,
    existingMarkdowns
  ) {
    getFieldsOfTypes(item, ['markdown']).forEach(markdownField => {
      existingMarkdowns[markdownField.value] = null
      extractImagesFromMarkdown(markdownField.value, existingImages)
      extractAssetsFromMarkdown(markdownField.value, existingAssets)
    })

    if (Array.isArray(item.children)) {
      item.children.forEach(child => {
        this.normalizeCollectionItemMarkdowns(
          child,
          existingImages,
          existingAssets,
          existingMarkdowns
        )
      })
    }
  }

  normalizeCollectionItemLayouts(
    item,
    existingImages,
    existingAssets,
    existingMarkdowns,
    existingLayouts
  ) {
    getFieldsOfTypes(item, ['layout', 'layout-grid']).forEach(layoutField => {
      const stringifiedLayout = JSON.stringify(layoutField.value)
      const layoutHash = hash(stringifiedLayout)
      existingLayouts[layoutHash] = layoutField.value
      // TODO: this still needs to be implemented for layout fields
      // extractImagesFromMarkdown(markdownField.value, existingImages)
      // extractAssetsFromMarkdown(markdownField.value, existingAssets)
    })

    if (Array.isArray(item.children)) {
      item.children.forEach(child => {
        this.normalizeCollectionItemLayouts(
          child,
          existingImages,
          existingAssets,
          existingMarkdowns,
          existingLayouts
        )
      })
    }
  }
}

const trimAssetField = assetField => {
  delete assetField.value._id
  delete assetField.value.path
  delete assetField.value.title
  delete assetField.value.mime
  delete assetField.value.size
  delete assetField.value.image
  delete assetField.value.video
  delete assetField.value.audio
  delete assetField.value.archive
  delete assetField.value.document
  delete assetField.value.code
  delete assetField.value.created
  delete assetField.value.modified
  delete assetField.value._by

  Object.keys(assetField.value).forEach(attribute => {
    assetField[attribute] = assetField.value[attribute]
    delete assetField.value[attribute]
  })
}

const trimGalleryImageField = galleryImageField => {
  galleryImageField.type = 'image'

  delete galleryImageField.meta.asset
  delete galleryImageField.path
}

const createCollectionItem = (
  collectionName,
  collectionFields,
  collectionEntry,
  locale = null,
  level = 1
) => {
  const item = {
    cockpitId: collectionEntry._id,
    cockpitCreated: new Date(collectionEntry._created * 1000),
    cockpitModified: new Date(collectionEntry._modified * 1000),
    // TODO: Replace with Users... once implemented (GitHub Issue #15)
    cockpitBy: collectionEntry._by,
    cockpitModifiedBy: collectionEntry._mby,
    lang: locale == null ? 'any' : locale,
    level: level,
  }

  Object.keys(collectionFields).reduce((accumulator, collectionFieldName) => {
    const collectionFieldValue = collectionEntry[collectionFieldName]
    const collectionFieldConfiguration = collectionFields[collectionFieldName]
    const field = createCollectionField(
      collectionName,
      collectionFieldValue,
      collectionFieldConfiguration
    )

    if (field !== null) {
      accumulator[collectionFieldName] = field
    }

    return accumulator
  }, item)

  if (collectionEntry.hasOwnProperty('children')) {
    item.children = collectionEntry.children.map(childEntry => {
      return createCollectionItem(
        collectionFields,
        childEntry,
        locale,
        level + 1
      )
    })
  }

  return item
}

const createCollectionField = (
  collectionName,
  collectionFieldValue,
  collectionFieldConfiguration
) => {
  const collectionFieldType = collectionFieldConfiguration.type

  if (
    !(
      Array.isArray(collectionFieldValue) && collectionFieldValue.length === 0
    ) &&
    collectionFieldValue != null &&
    collectionFieldValue !== ''
  ) {
    const itemField = {
      type: collectionFieldType,
    }

    if (collectionFieldType === 'repeater') {
      const repeaterFieldOptions = collectionFieldConfiguration.options || {}

      if (typeof repeaterFieldOptions.field !== 'undefined') {
        itemField.value = collectionFieldValue.map(repeaterEntry =>
          createCollectionField(
            collectionName,
            repeaterEntry.value,
            repeaterFieldOptions.field
          )
        )
      } else if (typeof repeaterFieldOptions.fields !== 'undefined') {
        itemField.value = collectionFieldValue.map(repeaterEntry =>
          repeaterFieldOptions.fields.reduce(
            (accumulator, currentFieldConfiguration) => {
              if (
                typeof currentFieldConfiguration.name === 'undefined' &&
                currentFieldConfiguration.label === repeaterEntry.field.label
              ) {
                const generatedNameProperty = slugify(
                  currentFieldConfiguration.label,
                  { lower: true }
                )
                console.warn(
                  `\nRepeater field without 'name' attribute used in collection '${collectionName}'. ` +
                    `Using value '${generatedNameProperty}' for name (generated from the label).`
                )
                currentFieldConfiguration.name = generatedNameProperty
                repeaterEntry.field.name = generatedNameProperty
              }

              if (currentFieldConfiguration.name === repeaterEntry.field.name) {
                accumulator.valueType = currentFieldConfiguration.name
                accumulator.value[
                  currentFieldConfiguration.name
                ] = createCollectionField(
                  collectionName,
                  repeaterEntry.value,
                  currentFieldConfiguration
                )
              }

              return accumulator
            },
            { type: 'set', value: {} }
          )
        )
      }
    } else if (collectionFieldType === 'set') {
      const setFieldOptions = collectionFieldConfiguration.options || {}

      itemField.value = setFieldOptions.fields.reduce(
        (accumulator, currentFieldConfiguration) => {
          const currentFieldName = currentFieldConfiguration.name
          accumulator[currentFieldName] = createCollectionField(
            collectionName,
            collectionFieldValue[currentFieldName],
            currentFieldConfiguration
          )

          return accumulator
        },
        {}
      )
    } else {
      itemField.value = collectionFieldValue
    }

    return itemField
  }

  return null
}

const extractImagesFromMarkdown = (markdown, existingImages) => {
  let unparsedMarkdown = markdown
  let match

  while ((match = MARKDOWN_IMAGE_REGEXP.exec(unparsedMarkdown))) {
    unparsedMarkdown = unparsedMarkdown.substring(match.index + match[0].length)
    existingImages[match[1]] = null
  }
}

const extractAssetsFromMarkdown = (markdown, existingAssets) => {
  let unparsedMarkdown = markdown
  let match

  while ((match = MARKDOWN_ASSET_REGEXP.exec(unparsedMarkdown))) {
    unparsedMarkdown = unparsedMarkdown.substring(match.index + match[0].length)
    const mediaType = mime.getType(match[1])

    if (mediaType && mediaType !== 'text/html') {
      existingAssets[match[1]] = null
    }
  }
}