Skip to content
This repository has been archived by the owner on Jul 4, 2023. It is now read-only.

Expose a programatic API #267

Closed
tamj0rd2 opened this issue Jun 17, 2020 · 3 comments
Closed

Expose a programatic API #267

tamj0rd2 opened this issue Jun 17, 2020 · 3 comments
Labels

Comments

@tamj0rd2
Copy link
Owner

It'd be cool if JS could be evaluated to create fixtures from the export of a javascript file.

@tamj0rd2 tamj0rd2 changed the title Allow js fixtures Allow js fixtures for dynamic responses Jun 29, 2020
@tamj0rd2 tamj0rd2 added the enhancement New feature or request label Jun 30, 2020
@tamj0rd2
Copy link
Owner Author

tamj0rd2 commented Jun 30, 2020

I could even write a programmatic version of ncdc. You'd pass it some io-ts (or similar) functions rather than just a type name.

That has a couple benefits

  • not having to invoke the typescript compiler (ideally someone would just run ncdc using io-ts) which would speed everything up
  • defined type guards can be reused at runtime for whatever purposes people may have
  • whoever uses the programmatic version will probably be using typescript which means that people would be able to have dynamic responses

Mysteries:

  • How would dynamic fixtures get reloaded? (maybe I can check how knex are doing this for their ts migration files)
  • How would JSDOC be supported? Does io-ts support it?

@tamj0rd2 tamj0rd2 added the help wanted Extra attention is needed label Jun 30, 2020
@tamj0rd2
Copy link
Owner Author

tamj0rd2 commented Jul 1, 2020

Or even something like this. Lets say a user has a ncdc.ts file in their project. The below kinda mirrors what we have in the docs:

interface Book {
  ISBN: string
  ISBN_13: string
  author: string
  title: string
  inventoryId: string
}

const isBook = (responseBody: unknown): asserts responseBody is Book => {
  if (typeof responseBody !== 'object') {
    throw new Error('Expected val to be of type string')
  }
  // more validation logic would go here
}

const config = {
  services: {
    bookService: {
      port: 3000,
      realUrl: 'https://example.com',
      tsconfig: './path/to/tsconfig.json',
      rateLimit: 300,
      resources: [
        {
          name: 'Book not found',
          serveOnly: true,
          request: {
            method: 'GET',
            endpoints: '/api/books/a-bad-id',
          },
          response: {
            code: 404,
          },
        },
        {
          name: 'Book',
          request: {
            method: 'GET',
            endpoints: ['/api/books/123', '/api/books/456'],
            serveEndpoint: '/api/books/:id',
          },
          response: {
            code: 200,
            serveBody: {
              ISBN: '9780141187761',
              ISBN_13: '978-0141187761',
              author: 'George Orwell',
              title: ' 1984 Nineteen Eighty-Four',
              inventoryId: 'item-87623',
            },
            // validator would replace the old "type" property
            validator: isBook,
          },
        },
      ],
    },
  },
}

export default config

/**
 * the new usage of the CLI would be:
 * ncdc serve blah.ts --watch
 * ncdc test blah.ts
 *
 * maybe there'd also be an option to choose the particular services, e.g
 * ncdc serve blah.ts filmService --watch
 * ncdc test blah.ts bookService
 */

Things this would change

  • Because validation functions are passed as type, the typescript compiler wouldn't be needed anymore. Invoking the typescript compiler is what takes 99% of the time.
  • Those validation functions can be used elsewhere in peoples code - nice for runtime validation of http requests for example
  • Using ts means someone can now have dynamic responses - really nice if someone wants to provide a function for serveBody
  • not having to use Concurrently to run 4 mocks APIs at the same time (basically, this fixes Allow serving/testing multiple configs #184) - this would be a pretty great performance gain too, specifically because we won't have 4 instances of the typescript compiler watching files
  • realUrl can now be provided via environment variables or any other way someone wants to
  • again, not sure how JSDOC would work after this - the answer is that it wouldn't. Users should provide their own validation functions that contain all of the validation logic required.
  • how do mocks get reloaded? Does someone need to restart ncdc every time they make a change? Maybe there would need to be a watchFiles glob array.

Things that stay the same

  • user would still interact with ncdc with the CLI for testing, but an additional programmatic serve API could be made available too
  • ncdc still needs access to (at least a subset of) the user's code
  • the user still needs to have any installed any dependencies that are required from within the ncdc.ts file
  • people can still import fixture files from disk if they want to

tamj0rd2 added a commit that referenced this issue Jul 2, 2020
@tamj0rd2 tamj0rd2 changed the title Allow js fixtures for dynamic responses Expose a programatic API Jul 2, 2020
@tamj0rd2 tamj0rd2 linked a pull request Jul 2, 2020 that will close this issue
tamj0rd2 added a commit that referenced this issue Jul 2, 2020
tamj0rd2 added a commit that referenced this issue Jul 2, 2020
BREAKING CHANGE: Deleted the generate command and related code

re #267
tamj0rd2 added a commit that referenced this issue Jan 21, 2021
@tamj0rd2
Copy link
Owner Author

Taking a different, simpler approach:

Rather than using some serious magic, with an ncdc.ts, people can just write their own script that create an NCDC instance which exposes methods to create/test/generate etc.

Here's the same Books service example from the docs again, but also with a Films service that would be hosted on port 4001.

#!/usr/bin/env -S npx ts-node -P ./scripts/tsconfig.json ./scripts/fakes.ts

/* eslint-disable @typescript-eslint/no-var-requires */
import { resolve } from 'path'
import { NCDC, Method } from 'ncdc'

async function start(): Promise<void> {
  const ncdc = new NCDC(
    [
      {
        name: 'Books service',
        port: 4000,
        baseUrl: 'https://example.com',
        resources: [
          {
            name: 'Book not found',
            serveOnly: true,
            request: {
              method: Method.GET,
              endpoints: ['/api/books/a-bad-id'],
            },
            response: {
              code: 404,
            },
          },
          {
            name: 'Book',
            serveOnly: false,
            request: {
              method: Method.GET,
              body: undefined,
              endpoints: ['/api/books/123', '/api/books/456'],
              serveEndpoint: '/api/books/*',
            },
            response: {
              code: 200,
              headers: { 'content-type': 'application/json' },
              type: 'Book',
              serveBody: {
                ISBN: '9780141187761',
                ISBN_13: '978-0141187761',
                author: 'George Orwell',
                title: '1984 Nineteen Eighty-Four',
                inventoryId: 'bitem-87623',
              },
            },
          },
        ],
      },
      {
        name: 'Films service',
        port: 4001,
        baseUrl: 'https://example.com',
        resources: [
          {
            name: 'Serve error',
            serveOnly: false,
            request: {
              endpoints: [],
              serveEndpoint: '/api/*',
              method: Method.GET,
            },
            response: {
              code: 401,
              body: 'nice meme, lol',
              type: 'string',
            },
          },
        ],
      },
    ],
    { tsconfigPath: getPath('./tsconfig.json') },
  )

  if (process.argv.includes('--serve')) {
    await ncdc.serve({ watch: true })
  } else if (process.argv.includes('--test')) {
    await ncdc.test({})
  } else if (process.argv.includes('--generate')) {
    await ncdc.generate({ force: false, outputPath: getPath('json-schema') })
  } else {
    // a sensible-ish default if no flag is provided
    await ncdc.serve({ watch: true })
  }
  return
}

void start().catch((err) => {
  console.log('fatal error', err)
  process.exit(1)
})

function getPath(pathRelativeToRepoRoot: string) {
  return resolve(process.cwd(), pathRelativeToRepoRoot)
}

The string Book still refers to the name of the type.

Things this would change

  • Possibility in the future for dynamic responses - really nice if someone wants to provide a function for serveBody
  • not having to use Concurrently to run 4 mocks APIs at the same time (basically, this fixes Allow serving/testing multiple configs #184) - this would be a pretty great performance gain too, specifically because we won't have 4 instances of the typescript compiler watching files
  • realUrl can now be provided via environment variables or any other way someone wants to
  • fixture files would not get reloaded. It would be up to the user to implement some watching logic for their files
    • ncdc serve command would probably need to expose a start method (it already has stop)

Things that stay the same

  • JSDOC would continue to work
  • user can still use the CLI until it's deprecated
  • ncdc still needs access to (at least a subset of) the user's code

tamj0rd2 added a commit that referenced this issue Jan 21, 2021
tamj0rd2 added a commit that referenced this issue Jan 21, 2021
@tamj0rd2 tamj0rd2 removed the help wanted Extra attention is needed label Jan 22, 2021
@tamj0rd2 tamj0rd2 added the cba label May 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant