Skip to content

Commit

Permalink
Add SchedulerClient (#14)
Browse files Browse the repository at this point in the history
* Add client-scheduler

* Add scheduler client

* Add schedule example

* Fix scheduler create

* Use groupName

* Switch transform order

* Do not get schedule with update

* Add getSchedule tests

* Add remaining schedule tests

* Update CHANGELOG

* Rename to ScheduleClient

* Revert "Rename to ScheduleClient"

This reverts commit aa1a1f6.

* Bind groupName to SchedulerClient
  • Loading branch information
razor-x authored Jun 9, 2024
1 parent 4231379 commit 320478d
Show file tree
Hide file tree
Showing 9 changed files with 3,683 additions and 147 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/)
and this project adheres to [Semantic Versioning](https://semver.org/).

## 1.8.0 / 2024-04-09

### Added

- `SchedulerClient`.

## 1.7.1 / 2022-10-09

### Fixed
Expand Down
4 changes: 3 additions & 1 deletion examples/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ import path from 'path'
import { createExamples } from '@meltwater/examplr'

import { invoke } from './invoke.js'
import * as schedule from './schedule.js'

process.env.AWS_SDK_LOAD_CONFIG = 'true'

const examples = {
invoke
invoke,
...schedule
}

const envVars = ['LOG_LEVEL', 'LOG_FILTER', 'LOG_OUTPUT_MODE']
Expand Down
65 changes: 65 additions & 0 deletions examples/schedule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { SchedulerClient } from '../index.js'

export const createSchedule =
({ log }) =>
async (scheduleName, groupName, arn, roleArn) => {
const client = new SchedulerClient({
groupName,
log
})
return client.createSchedule(scheduleName, {
scheduleExpression: 'rate(1 minute)',
flexibleTimeWindow: { mode: 'OFF' },
input: { foo: 'bar' },
target: {
arn,
roleArn,
eventBridgeParameters: {
detailType: 'example',
source: 'example'
}
}
})
}

export const deleteSchedule =
({ log }) =>
async (scheduleName, groupName) => {
const client = new SchedulerClient({
groupName,
log
})
return client.deleteSchedule(scheduleName)
}

export const updateSchedule =
({ log }) =>
async (scheduleName, groupName, arn, roleArn) => {
const client = new SchedulerClient({
groupName,
log
})
return client.updateSchedule(scheduleName, {
scheduleExpression: 'rate(2 minutes)',
flexibleTimeWindow: { mode: 'OFF' },
input: { foo: 'bar' },
target: {
arn,
roleArn,
eventBridgeParameters: {
detailType: 'example',
source: 'example'
}
}
})
}

export const getSchedule =
({ log }) =>
async (scheduleName, groupName) => {
const client = new SchedulerClient({
groupName,
log
})
return client.getSchedule(scheduleName)
}
1 change: 1 addition & 0 deletions lib/clients/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ export * from './dynamodb-document.js'
export * from './eventbridge.js'
export * from './lambda.js'
export * from './s3.js'
export * from './scheduler.js'
export * from './sqs.js'
60 changes: 60 additions & 0 deletions lib/clients/scheduler.doc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/**
* AWS SchedulerClient client.
* @class SchedulerClient
* @see {@link https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-eventbridge/index.html|@aws-sdk/client-scheduler}
* @param {Object} parameters
* @param {string} [parameters.groupName=default] Schedule group name.
* @param {string} [parameters.name=scheduler] Client name.
* @param {string} [parameters.reqId=<uuid>] Request id.
* @param {Object} [parameters.log=<logger>] Pino compatible logger.
* @param {Constructor} [parameters.AwsSdkSchedulerClient=SchedulerClient]
* Constructor for a SchedulerClient from the AWS SDK.
* @param {Object} [parameters.params={}]
* Additional params to pass to the AwsSdkSchedulerClient constructor.
*/

/**
* Get a schedule.
* @async
* @function getSchedule
* @memberof SchedulerClient
* @instance
* @param {Object[]} [name] Name of the schedule to get.
* @param {Object} [params=[]] Additional params to pass to the GetScheduleCommand.
* @return {Promise<Object>} Response normalized to camel case.
*/

/**
* Create a schedule.
* @async
* @function createSchedule
* @memberof SchedulerClient
* @instance
* @param {Object[]} [name] Name of the schedule to create.
* @param {Object} [params=[]] Additional params to pass to the CreateScheduleCommand.
* @return {Promise<Object>} Response normalized to camel case.
*/

/**
* Delete a schedule.
* @async
* @function deleteSchedule
* @memberof SchedulerClient
* @instance
* @param {Object[]} [name] Name of the schedule to delete.
* @param {Object} [params=[]] Additional params to pass to the DeleteScheduleCommand.
* @return {Promise<Object>} Response normalized to camel case.
*/

/**
* Update a schedule.
* AWS uses a replace all attributes strategy when updating schedules.
* AWS schedules
* @async
* @function deleteSchedule
* @memberof SchedulerClient
* @instance
* @param {Object[]} [name] Name of the schedule to update.
* @param {Object} [params=[]] Additional params to pass to the UpdateScheduleCommand.
* @return {Promise<Object>} Response normalized to camel case.
*/
183 changes: 183 additions & 0 deletions lib/clients/scheduler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
import {
SchedulerClient as AwsSdkSchedulerClient,
CreateScheduleCommand,
DeleteScheduleCommand,
GetScheduleCommand,
UpdateScheduleCommand
} from '@aws-sdk/client-scheduler'
import { v4 as uuidv4 } from 'uuid'
import { createLogger } from '@meltwater/mlabs-logger'
import {
has,
identity,
ifElse,
isObjectLike,
map,
mapPath,
pipe
} from '@meltwater/phi'

import { createCache } from '../cache.js'
import { keysToCamelCase, keysToPascalCase } from '../case.js'

const createClient = createCache()

export class SchedulerClient {
#groupName
#client
#reqId
#log

constructor({
groupName = 'default',
name = 'scheduler',
reqId = uuidv4(),
log = createLogger(),
AwsSchedulerClient = AwsSdkSchedulerClient,
params = {}
}) {
this.#groupName = groupName
this.#client = createClient(name, () => new AwsSchedulerClient(params))
this.#reqId = reqId
this.#log = log.child({
groupName,
params,
client: name,
class: SchedulerClient.name,
reqId
})
}

async getSchedule(scheduleName, params = {}) {
const log = this.#log.child({
scheduleName,
meta: params,
method: SchedulerClient.prototype.getSchedule.name
})
try {
log.info('start')
const req = this.#formatReq({ ...params, name: scheduleName })
const command = new GetScheduleCommand(req)

const res = await this.#client.send(command)

const data = formatRes(res)

log.debug({ data }, 'data')
log.info('end')
return data
} catch (err) {
log.error({ err }, 'fail')
throw err
}
}

async createSchedule(scheduleName, params = {}) {
const log = this.#log.child({
scheduleName,
meta: params,
method: SchedulerClient.prototype.createSchedule.name
})
try {
log.info('start')
const req = this.#formatReq({ ...params, name: scheduleName })
const command = new CreateScheduleCommand(req)

const res = await this.#client.send(command)

const data = formatRes(res)

log.debug({ data }, 'data')
log.info('end')
return data
} catch (err) {
log.error({ err }, 'fail')
throw err
}
}

async deleteSchedule(scheduleName, params = {}) {
const log = this.#log.child({
scheduleName,
meta: params,
method: SchedulerClient.prototype.deleteSchedule.name
})
try {
log.info('start')
const req = this.#formatReq({ ...params, name: scheduleName })
const command = new DeleteScheduleCommand(req)

const res = await this.#client.send(command)

const data = formatRes(res)

log.debug({ data }, 'data')
log.info('end')
return data
} catch (err) {
log.error({ err }, 'fail')
throw err
}
}

async updateSchedule(scheduleName, params = {}) {
const log = this.#log.child({
scheduleName,
meta: params,
method: SchedulerClient.prototype.updateSchedule.name
})
try {
log.info('start')

const req = this.#formatReq({ ...params, name: scheduleName })
const command = new UpdateScheduleCommand(req)

const res = await this.#client.send(command)

const data = formatRes(res)

log.debug({ data }, 'data')
log.info('end')
return data
} catch (err) {
log.error({ err }, 'fail')
throw err
}
}

#formatReq = (input) => {
return formatReq({ ...input, GroupName: this.#groupName })
}
}

const formatReq = pipe(
keysToPascalCase,
map(ifElse(isObjectLike, keysToPascalCase, identity)),
ifElse(
has('Target'),
mapPath(
['Target'],
pipe(
keysToPascalCase,
map(ifElse(isObjectLike, keysToPascalCase, identity))
)
),
identity
)
)

const formatRes = pipe(
ifElse(
has('Target'),
mapPath(
['Target'],
pipe(
keysToCamelCase,
map(ifElse(isObjectLike, keysToCamelCase, identity))
)
),
identity
),
map(ifElse(isObjectLike, keysToCamelCase, identity)),
keysToCamelCase
)
Loading

0 comments on commit 320478d

Please sign in to comment.