Skip to content

Commit

Permalink
feat: Provide "ready helpers" as ReadyContext methods
Browse files Browse the repository at this point in the history
  • Loading branch information
zenflow committed Mar 17, 2021
1 parent e1ddd9b commit cebd1a9
Show file tree
Hide file tree
Showing 12 changed files with 67 additions and 159 deletions.
6 changes: 2 additions & 4 deletions src/Service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { PassThrough } from 'stream'
import cloneable from 'cloneable-readable'
import { ServiceProcess } from './ServiceProcess'
import { NormalizedServiceConfig } from './validateAndNormalizeConfig'
import { ReadyContext } from './interfaces/ReadyContext'
import { OnCrashContext } from './interfaces/OnCrashContext'
import { ServiceCrash } from './interfaces/ServiceCrash'
import { InternalError } from './InternalError'
import { Logger } from './Logger'
import { createReadyContext } from './createReadyContext'

const delay = promisify(setTimeout)

Expand Down Expand Up @@ -57,9 +57,7 @@ export class Service {
}

private defineReady() {
const ctx: ReadyContext = {
output: this.outputClone,
}
const ctx = createReadyContext(this.outputClone)
this.ready = promiseTry(() => this.config.ready(ctx))
.finally(() => this.outputClone.destroy())
.catch(error => {
Expand Down
32 changes: 32 additions & 0 deletions src/createReadyContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { promisify } from 'util'
import stream from 'stream'
import { ReadyContext } from './interfaces/ReadyContext'
import { onceTcpPortUsed } from './util/onceTcpPortUsed'

const delay = promisify(setTimeout)

export function createReadyContext(output: stream.Readable): ReadyContext {
return {
onceTcpPortUsed,
onceOutputLineIs: line => onceOutputLine(output, l => l === line),
onceOutputLineIncludes: text =>
onceOutputLine(output, l => l.includes(text)),
onceOutputLine: test => onceOutputLine(output, test),
onceDelay: milliseconds => delay(milliseconds),
}
}

function onceOutputLine(
output: stream.Readable,
test: (line: string) => boolean,
): Promise<void> {
return new Promise<void>(resolve => {
const handler = (line: string) => {
if (test(line)) {
output.off('data', handler)
resolve()
}
}
output.on('data', handler)
})
}
2 changes: 0 additions & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
export * from './ready-helpers'

export { startCompositeService } from './startCompositeService'

export { CompositeServiceConfig } from './interfaces/CompositeServiceConfig'
Expand Down
32 changes: 25 additions & 7 deletions src/interfaces/ReadyContext.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
import stream from 'stream'

/**
* Context object given as argument to each {@link ServiceConfig.ready} function
*/
export interface ReadyContext {
/**
* Interleaved lines from stdout & stderr of the service.
*
* Each chunk is a utf8 string ending in '\n'.
* Wait until the given TCP `port` (on the given `host`) is accepting connections.
* The `port` is required.
* The `host` defaults to "localhost".
*
* Can be used `as AsyncIterable<string>`.
* Works by trying establish a TCP connection to the given port every 250 milliseconds.
*/
onceTcpPortUsed: (port: number | string, host?: string) => Promise<void>

/**
* Wait until a line in the console output passes custom `test`
*/
onceOutputLine: (test: (line: string) => boolean) => Promise<void>

/**
* Wait until a certain exact `line` appears in the console output
*/
onceOutputLineIs: (line: string) => Promise<void>

/**
* Wait until a line including `text` appears in the console output
*/
onceOutputLineIncludes: (text: string) => Promise<void>

/**
* Wait a predetermined length of time
*/
output: stream.Readable
onceDelay: (milliseconds: number) => Promise<void>
}
3 changes: 0 additions & 3 deletions src/interfaces/ServiceConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,6 @@ export interface ServiceConfig {
*
* If any error is encountered in its execution,
* the composite service will shut down any running services and exit.
*
* This library includes a collection of [Ready Helpers](./composite-service.oncetcpportused.md)
* to help you define this property.
*/
ready?: (ctx: ReadyContext) => Promise<any>

Expand Down
3 changes: 0 additions & 3 deletions src/ready-helpers/index.ts

This file was deleted.

81 changes: 0 additions & 81 deletions src/ready-helpers/output-line.ts

This file was deleted.

25 changes: 0 additions & 25 deletions src/ready-helpers/timeout.ts

This file was deleted.

26 changes: 0 additions & 26 deletions src/ready-helpers/port-used.ts β†’ src/util/onceTcpPortUsed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,6 @@ import { Socket } from 'net'

const delay = promisify(setTimeout)

/**
* Waits until the given `port` (on the given `host`) is accepting connections
*
* @remarks
*
* Works by trying to connect to the given port
* (as opposed to trying to listen on the given port)
* every 250 milliseconds.
*
* @param port -
* @param host - Defaults to `'localhost'`
*
* @example
*
* ```js
* const { onceTcpPortUsed } = require('composite-service')
*
* const myServiceConfig = {
* command: 'node server.js',
* env: { PORT: 3000 },
* ready: () => onceTcpPortUsed(3000),
* }
* ```
*
* @public
*/
export async function onceTcpPortUsed(
port: number | string,
host = 'localhost',
Expand Down
4 changes: 2 additions & 2 deletions test/integration/crashing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@ const delay = (time: number) =>

function getScript(customCode = '') {
return `
const { startCompositeService, onceOutputLineIs } = require('.');
const { startCompositeService } = require('.');
const config = {
logLevel: 'debug',
gracefulShutdown: true,
serviceDefaults: {
command: 'node test/integration/fixtures/http-service.js',
ready: ctx => onceOutputLineIs(ctx.output, 'Started πŸš€\\n'),
ready: ctx => ctx.onceOutputLineIs('Started πŸš€\\n'),
},
services: {
first: {
Expand Down
4 changes: 2 additions & 2 deletions test/integration/process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe('process', () => {
'force kills service after forceKillTimeout',
async () => {
proc = new CompositeProcess(`
const { startCompositeService, onceOutputLineIs } = require('.');
const { startCompositeService } = require('.');
startCompositeService({
logLevel: 'debug',
services: {
Expand All @@ -92,7 +92,7 @@ describe('process', () => {
+ 'process.on("SIGINT", () => console.log("got SIGINT"));'
+ 'console.log("started");'
],
ready: ctx => onceOutputLineIs(ctx.output, 'started\\n'),
ready: ctx => ctx.onceOutputLineIs('started\\n'),
forceKillTimeout: 500,
},
},
Expand Down
8 changes: 4 additions & 4 deletions test/integration/working.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { redactConfigDump } from './helpers/redact'

function getScript() {
return `
const { startCompositeService, onceOutputLineIs, onceOutputLine, onceTcpPortUsed } = require('.');
const { startCompositeService } = require('.');
const config = {
logLevel: 'debug',
gracefulShutdown: true,
Expand All @@ -12,18 +12,18 @@ function getScript() {
cwd: 'test/integration/fixtures',
command: ['node', 'http-service.js'],
env: { PORT: 8001, RESPONSE_TEXT: 'first', START_DELAY: 500, STOP_DELAY: 500 },
ready: ctx => onceTcpPortUsed(8001),
ready: ctx => ctx.onceTcpPortUsed(8001),
},
second: {
command: 'node test/integration/fixtures/http-service.js',
env: { PORT: 8002, RESPONSE_TEXT: 'second' },
ready: ctx => onceOutputLineIs(ctx.output, 'Started πŸš€\\n'),
ready: ctx => ctx.onceOutputLineIs('Started πŸš€\\n'),
},
third: {
dependencies: ['first', 'second'],
command: 'node test/integration/fixtures/http-service.js',
env: { PORT: 8003, RESPONSE_TEXT: 'third' },
ready: ctx => onceOutputLine(ctx.output, line => line === 'Started πŸš€\\n'),
ready: ctx => ctx.onceOutputLine(line => line === 'Started πŸš€\\n'),
},
},
};
Expand Down

0 comments on commit cebd1a9

Please sign in to comment.