bentonam / fakeit

Generates JSON documents based on models defined in YAML and adds them to a Couchbase Bucket
MIT License
86 stars 21 forks source link

Add attachments? #159

Closed saschwarz closed 6 years ago

saschwarz commented 6 years ago

I didn't see a built-in way to add attachments? Can I just add properties under an _attachments property?

More specifically, I'm looking to add Blob images as attachments and I'm not sure how I'd require and execute the necessary code to fake them. Any advice would be appreciated!

tjbenton commented 6 years ago

Given the following folder structure this is how you can handle something like this.

images
  ├── image-1.png
  ├── image-1@2x.png
  ├── image-2.png
  ├── image-2@2x.png
  ├── image-3.png
  ├── image-3@2x.png
  └── image-4.png
models
  └── images.yaml

models/images.yaml

name: Images
key: _id
type: object
data:
  count: 1
  pre_run: |
    const fs = require('fs')
    globals.images = new Map()
    globals.images.set('1x', [])
    globals.images.set('2x', [])
    globals.images = fs.readdirSync(`${this.root}/../images`)
      .reduce((prev, next) => {
        const type = next.includes('@2x') ? '2x' : '1x'
        prev.get(type).push(next)
        return prev
      }, globals.images)

  pre_build: |
    const index = chance.integer({ min: 0, max: globals.images.get('1x').length - 1 })
    globals.current = new Map()
    globals.current.set('1x', globals.images.get('1x')[index])
    globals.current.set('2x', globals.images.get('2x')[index])

properties:
  _id:
    type: string
    description: The document id
    data:
      build: `images_${document_index}`
  src:
    type: object
    description: the src
    properties:
      1x:
        type: string
        data:
          build: globals.current.get('1x')
      2x:
        type: string
        data:
          build: globals.current.get('2x')

Each document would end up looking like this

{
  "_id": "images_0",
  "src": {
    "1x": "image-3.png",
    "2x": "image-3@2x.png"
  }
}

In the model there's a pre_run that will only run once before all the documents are generated and that's where you read your assets directory and set them on the global object. Currently Fakeit only supports synchronous functions, so you would have to use a sync function for anything that would be looking at the files on your system. Then the pre_build function runs before each document that is built so you can use it to set the current image to use in the document.

Make sure you have the latest version of fakeit at 1.2.1

saschwarz commented 6 years ago

Thank you so much for such a thorough solution and explanation! It was just what I needed. In my case, I need to load the actual images into the Couchbase _attachments property. Here's my solution in case anyone else needs something like this:

name: Maps
type: object
key: _id
data:
  pre_run: |
    const fs = require('fs')
    const path = require('path')
    const dir = `${this.root}/images/maps`;
    # load all map PNG images as base64 strings into global.images at start up
    globals.images = fs.readdirSync(dir).reduce(function(list, file) {
        const name = path.join(dir, file);
        return list.concat([fs.readFileSync(name).toString('base64')]);
    }, []);
properties:
  _id:
    type: string
    description: The document id built by the prefixed map/ and the users uuid
    data:
      pre_build: globals.id = faker.random.uuid()
      build: `map/${globals.id}`
  _attachments:
    type: object
    properties:
      # in my case 'original' is the property name for my PNG images
      original:
        type: object
        properties:
          content_type:
            type: string
            data:
              value: 'image/png'
          data:
            type: string
            data:
              build: faker.random.arrayElement(globals.images)