* converted all files to ts, updated errors, except for __audits in test/basic.ts

* passes tslint working on compiling

* working on build and travis

* Update travis file

* Update package.json

* add linting command

* add rimraf to cleanup script

* fix spelling of clutter

* clean up configurations and add comments

* clean up package scripts

* update path param

* fix new linting and ts errors
This commit is contained in:
Allison Durkan 2018-09-26 11:12:15 -04:00 коммит произвёл Ethan Arrowood
Родитель 834cec3658
Коммит e45b1a8691
12 изменённых файлов: 743 добавлений и 519 удалений

2
.gitignore поставляемый
Просмотреть файл

@ -57,3 +57,5 @@ typings/
# dotenv environment variables file # dotenv environment variables file
.env .env
# typescript compiled files
dist/

Просмотреть файл

@ -1,4 +1,11 @@
language: node_js language: node_js
before_script:
- npm run compile
script:
- npm run test
node_js: node_js:
- "node" - "node"
- "lts/*" - "lts/*"

Просмотреть файл

@ -1,5 +0,0 @@
module.exports = {
parsers: require('./parsers'),
validators: require('./validators'),
auditer: require('./audit')
}

726
package-lock.json сгенерированный

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -2,9 +2,12 @@
"name": "platform-chaos", "name": "platform-chaos",
"version": "1.0.0", "version": "1.0.0",
"description": "A node sdk for building services capable of injecting chaos into PaaS offerings", "description": "A node sdk for building services capable of injecting chaos into PaaS offerings",
"main": "index.js", "main": "dist/index.js",
"scripts": { "scripts": {
"test": "mocha" "lint": "npx tslint --project .",
"test": "npm run lint && mocha dist/test/",
"compile": "npm run cleanup && npx tsc",
"cleanup": "rimraf dist/"
}, },
"keywords": [ "keywords": [
"azure", "azure",
@ -14,6 +17,11 @@
"author": "microsoft", "author": "microsoft",
"license": "MIT", "license": "MIT",
"devDependencies": { "devDependencies": {
"@types/lodash.isequal": "^4.5.3",
"@types/mocha": "^5.2.5",
"@types/shimmer": "^1.0.1",
"@types/sinon": "^5.0.2",
"@types/uuid": "^3.4.4",
"eslint": "^5.4.0", "eslint": "^5.4.0",
"eslint-config-standard": "^12.0.0", "eslint-config-standard": "^12.0.0",
"eslint-plugin-import": "^2.14.0", "eslint-plugin-import": "^2.14.0",
@ -22,7 +30,11 @@
"eslint-plugin-standard": "^4.0.0", "eslint-plugin-standard": "^4.0.0",
"lodash.isequal": "^4.5.0", "lodash.isequal": "^4.5.0",
"mocha": "^5.2.0", "mocha": "^5.2.0",
"sinon": "^6.2.0" "rimraf": "^2.6.2",
"sinon": "^6.2.0",
"tslint": "^5.11.0",
"tslint-config-standard": "^8.0.1",
"typescript": "^3.0.3"
}, },
"dependencies": { "dependencies": {
"shimmer": "^1.2.0", "shimmer": "^1.2.0",

Просмотреть файл

@ -1,43 +1,58 @@
const shimmer = require('shimmer') import * as assert from 'assert'
const assert = require('assert') import * as os from 'os'
const os = require('os') import * as shimmer from 'shimmer'
const uuidv4 = require('uuid/v4') import * as uuidv4 from 'uuid'
/* interface implementation of Audit defined in
* https://github.com/Azure/platform-chaos/wiki/Auditing
* */
export interface IAudit {
auditId: string,
date: string,
eventName: string,
extensionLog: string[],
resources: string,
system: string
}
interface IAuditOptions {
eventName: string,
resources: string
}
const auditSystem = `${os.hostname()}-${os.platform()}` const auditSystem = `${os.hostname()}-${os.platform()}`
let auditQueue = [] let auditQueue: IAudit[] = []
const audit = (extensionLogArgs, auditOptions) => { const audit = (extensionLogArgs, auditOptions: IAuditOptions) => {
const { eventName, resources } = auditOptions const { eventName, resources } = auditOptions
auditQueue.push({ auditQueue.push({
auditId: uuidv4(), auditId: uuidv4(),
eventName: eventName,
system: auditSystem,
date: new Date().toISOString(), date: new Date().toISOString(),
resources: resources, eventName,
extensionLog: typeof extensionLogArgs === 'string' ? [ extensionLogArgs ] : Array.from(extensionLogArgs) extensionLog: typeof extensionLogArgs === 'string' ? [ extensionLogArgs ] : Array.from(extensionLogArgs),
resources,
system: auditSystem
}) })
} }
module.exports = { export default {
audit: audit, audit,
initialize: (context, opts) => { initialize: (context: any, opts: IAuditOptions) => {
assert(opts, 'Options object must be defined') assert(opts, 'Options object must be defined')
assert(typeof opts.eventName === 'string', 'Event name must be a string')
assert(typeof opts.resources === 'string', 'Resources must be a string')
const auditOptions = opts const auditOptions = opts
auditQueue = [] auditQueue = []
shimmer.wrap(context, 'log', function (original) { shimmer.wrap(context, 'log', (original: (message: any) => void) => {
return function () { return function () {
audit(arguments, auditOptions) audit(arguments, auditOptions)
return original.apply(this, arguments) return original.apply(this, arguments)
} }
}) })
shimmer.wrap(context, 'done', function (original) { shimmer.wrap(context, 'done', (original: (err: any, propertyBag: any) => void) => {
return function () { return function () {
const audits = auditQueue const audits = auditQueue
@ -47,10 +62,10 @@ module.exports = {
} }
} else if (typeof context.res.body === 'string') { } else if (typeof context.res.body === 'string') {
const body = JSON.parse(context.res.body) const body = JSON.parse(context.res.body)
body['__audits'] = audits body.__audits = audits
context.res.body = JSON.stringify(body) context.res.body = JSON.stringify(body)
} else if (typeof context.res.body === 'object') { } else if (typeof context.res.body === 'object') {
context.res.body['__audits'] = audits context.res.body.__audits = audits
} }
/* /*
* If context.res.body is not of type string or object * If context.res.body is not of type string or object

9
src/index.ts Normal file
Просмотреть файл

@ -0,0 +1,9 @@
import Auditers from './audit'
import Parsers from './parsers'
import Validators from './validators'
export default {
auditer: Auditers,
parsers: Parsers,
validators: Validators
}

Просмотреть файл

@ -1,15 +1,16 @@
const validate = require('./validators') import validate from './validators'
module.exports = { export default {
accessTokenToCredentials: (req) => { accessTokenToCredentials: (req) => {
// ensure we have a valid accessToken before proceeding // ensure we have a valid accessToken before proceeding
validate.accessToken(req) validate.accessToken(req)
// this implements the contract defined by msRestAzure // this implements the contract defined by msRestAzure
// see https://github.com/Azure/azure-sdk-for-node/blob/0a52678ea7b3a24a478975f7169fc30e9fc9759e/runtime/ms-rest-azure/lib/credentials/deviceTokenCredentials.js // tslint:disable-next-line:max-line-length
// see https://github.com/Azure/azure-sdk-for-node/blob/master/runtime/ms-rest-azure/lib/credentials/deviceTokenCredentials.js
return { return {
signRequest: (webResource, callback) => { signRequest: (webResource, callback) => {
webResource.headers['Authorization'] = req.body.accessToken webResource.headers.Authorization = req.body.accessToken
callback(null) callback(null)
} }
} }
@ -19,11 +20,11 @@ module.exports = {
validate.resources(req) validate.resources(req)
// parse the resources into objects // parse the resources into objects
return req.body.resources.map(r => r.split('/')).map(r => { return req.body.resources.map((r) => r.split('/')).map((r) => {
return { return {
subscriptionId: r[0],
resourceGroupName: r[1], resourceGroupName: r[1],
resourceName: r[2] resourceName: r[2],
subscriptionId: r[0]
} }
}) })
} }

Просмотреть файл

@ -1,195 +1,206 @@
const assert = require('assert') import * as assert from 'assert'
const index = require('../') import * as sinon from 'sinon'
const sinon = require('sinon')
const isEqual = require('lodash.isequal') import isEqual = require('lodash.isequal')
/* eslint-env node, mocha */ import { IAudit } from '../audit'
import index from '../index'
describe('platform-chaos', () => {
it('is named properly', () => { interface IHeader {
assert.equal(require('../package.json').name, 'platform-chaos') Authorization?: string
assert.equal(require('../package-lock.json').name, 'platform-chaos') }
})
it('parses resources', () => { interface IBody {
assert.throws(() => { __audits?: IAudit[]
index.validators.resources({ }
body: {
resources: [1] describe('platform-chaos', () => {
} it('is named properly', () => {
}) assert.strictEqual(require('../../package.json').name, 'platform-chaos')
}) assert.strictEqual(require('../../package-lock.json').name, 'platform-chaos')
})
assert.throws(() => { it('parses resources', () => {
index.validators.resources({ assert.throws(() => {
body: { index.validators.resources({
resources: [''] body: {
} resources: [1]
}) }
}) })
})
assert.throws(() => {
index.validators.resources({ assert.throws(() => {
body: { index.validators.resources({
resources: ['one/two/three/four'] body: {
} resources: ['']
}) }
}) })
})
const instance = index.parsers.resourcesToObjects({
body: { assert.throws(() => {
resources: ['sub/rg/resource'] index.validators.resources({
} body: {
})[0] resources: ['one/two/three/four']
}
assert.equal(instance.subscriptionId, 'sub') })
assert.equal(instance.resourceGroupName, 'rg') })
assert.equal(instance.resourceName, 'resource')
}) const instance = index.parsers.resourcesToObjects({
body: {
it('parses accessTokens', () => { resources: ['sub/rg/resource']
// invalid at }
assert.throws(() => { })[0]
index.validators.accessToken({
body: { assert.strictEqual(instance.subscriptionId, 'sub')
accessToken: 1 assert.strictEqual(instance.resourceGroupName, 'rg')
} assert.strictEqual(instance.resourceName, 'resource')
}) })
})
it('parses accessTokens', () => {
// empty resourceIds // invalid at
assert.throws(() => { assert.throws(() => {
index.validators.accessToken({ index.validators.accessToken({
body: { body: {
accessToken: 'valid type' accessToken: 1
} }
}) })
}) })
// invalid resourceIds type // empty resourceIds
assert.throws(() => { assert.throws(() => {
index.validators.accessToken({ index.validators.accessToken({
body: { body: {
accessToken: 'valid type' accessToken: 'valid type'
} }
}) })
}) })
// invalid resourceIds format // invalid resourceIds type
assert.throws(() => { assert.throws(() => {
index.validators.accessToken({ index.validators.accessToken({
body: { body: {
accessToken: 'valid type' accessToken: 'valid type'
} }
}) })
}) })
// valid // invalid resourceIds format
const expectedAccessToken = 'Bearer 12345234r2' assert.throws(() => {
const instance = index.parsers.accessTokenToCredentials({ index.validators.accessToken({
body: { body: {
accessToken: expectedAccessToken accessToken: 'valid type'
} }
}) })
})
const res = {
headers: {} // valid
} const expectedAccessToken = 'Bearer 12345234r2'
const instance = index.parsers.accessTokenToCredentials({
instance.signRequest(res, () => {}) body: {
accessToken: expectedAccessToken
assert.equal(res.headers['Authorization'], expectedAccessToken) }
}) })
it('audits correctly', () => { const res = {
function contextLog () { headers: {} as IHeader
// in reality this would be `console.log(...arguments)` }
// but in order to not cluter test we noop this
return () => null instance.signRequest(res, () => null)
}
function contextDone () { assert.strictEqual(res.headers.Authorization, expectedAccessToken)
return () => null })
}
it('audits correctly', () => {
const contextLogSpy = sinon.spy(contextLog) function contextLog () {
const contextDoneSpy = sinon.spy(contextDone) // in reality this would be `console.log(...arguments)`
// but in order to not clutter test we noop this
const context = { return () => null
log: contextLogSpy, }
done: contextDoneSpy, function contextDone () {
res: { body: {} } return () => null
} }
index.auditer.initialize(context, { const contextLogSpy = sinon.spy(contextLog)
eventName: 'testEvent', const contextDoneSpy = sinon.spy(contextDone)
resources: 'testResource'
}) const context = {
done: contextDoneSpy,
const logItem1 = { log: contextLogSpy,
'prop1': 'important information', res: { body: {} }
'anotherProp': 'more important info' }
}
const logItem2 = 'Hello, World!' index.auditer.initialize(context, {
const logItem3 = ['abc', { a: 12 }] eventName: 'testEvent',
resources: 'testResource'
context.log(logItem1) })
context.log(logItem2)
context.log(...logItem3) const logItem1 = {
anotherProp: 'more important info',
context.res.body = { prop1: 'important information'
message: 'I am writing to the body' }
} const logItem2 = 'Hello, World!'
const logItem3 = ['abc', { a: 12 }]
context.done()
context.log(logItem1)
assert(contextLogSpy.called, 'context.log should be called') context.log(logItem2)
assert(contextDoneSpy.called, 'context.done should be called') context.log(...logItem3)
const body = context.res.body context.res.body = {
message: 'I am writing to the body'
assert(typeof body === 'object', 'context.res.body should exist as an object') }
assert(body.hasOwnProperty('__audits'), 'body contains __audits property')
assert(isEqual(body.__audits[0].extensionLog[0], logItem1), 'log item 1 is added to audit correctly') context.done()
assert(isEqual(body.__audits[1].extensionLog[0], logItem2), 'log item 2 is added to audit correctly')
assert(isEqual(body.__audits[2].extensionLog, logItem3), 'log item 3 is added to audit correctly') assert(contextLogSpy.called, 'context.log should be called')
}) assert(contextDoneSpy.called, 'context.done should be called')
it('allows user to audit directly', () => { const body: IBody = context.res.body
function contextLog () {
// in reality this would be `console.log(...arguments)` assert(body.hasOwnProperty('__audits'), 'body contains __audits property')
// but in order to not cluter test we noop this assert(isEqual(body.__audits && body.__audits[0].extensionLog[0], logItem1),
return () => null 'log item 1 is added to audit correctly')
} assert(isEqual(body.__audits && body.__audits[1].extensionLog[0], logItem2),
function contextDone () { 'log item 2 is added to audit correctly')
return () => null assert(isEqual(body.__audits && body.__audits[2].extensionLog, logItem3),
} 'log item 3 is added to audit correctly')
})
const contextLogSpy = sinon.spy(contextLog)
const contextDoneSpy = sinon.spy(contextDone) it('allows user to audit directly', () => {
function contextLog () {
const context = { // in reality this would be `console.log(...arguments)`
log: contextLogSpy, // but in order to not cluter test we noop this
done: contextDoneSpy, return () => null
res: { body: {} } }
} function contextDone () {
return () => null
const opts = { }
eventName: 'testEvent',
resources: 'testResource' const contextLogSpy = sinon.spy(contextLog)
} const contextDoneSpy = sinon.spy(contextDone)
index.auditer.initialize(context, opts) const context = {
done: contextDoneSpy,
index.auditer.audit('Hello, World!', opts) log: contextLogSpy,
res: { body: {} }
context.done() }
assert(contextLogSpy.notCalled, 'context.log should not be called') const opts = {
assert(contextDoneSpy.called, 'context.done should be called') eventName: 'testEvent',
resources: 'testResource'
const body = context.res.body }
assert(typeof body === 'object', 'context.res.body should exist as an object') index.auditer.initialize(context, opts)
assert(body.hasOwnProperty('__audits'), 'body contains __audits property')
assert(body.__audits[0].extensionLog[0] === 'Hello, World!', 'log item 1 is added to audit correctly') index.auditer.audit('Hello, World!', opts)
})
}) context.done()
assert(contextLogSpy.notCalled, 'context.log should not be called')
assert(contextDoneSpy.called, 'context.done should be called')
const body: IBody = context.res.body
assert(body.hasOwnProperty('__audits'), 'body contains __audits property')
assert(body.__audits && body.__audits[0].extensionLog[0] === 'Hello, World!',
'log item 1 is added to audit correctly')
})
})

Просмотреть файл

@ -1,6 +1,6 @@
const assert = require('assert') import * as assert from 'assert'
module.exports = { export default {
accessToken: (req) => { accessToken: (req) => {
assert.ok(req.body.accessToken) assert.ok(req.body.accessToken)
assert.ok(typeof req.body.accessToken === 'string') assert.ok(typeof req.body.accessToken === 'string')

16
tsconfig.json Normal file
Просмотреть файл

@ -0,0 +1,16 @@
{
"compilerOptions": {
"lib": ["es6"],
"module": "commonjs",
"outDir": "dist",
"sourceMap": true,
"target": "es6",
"strictNullChecks": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules"
]
}

6
tslint.json Normal file
Просмотреть файл

@ -0,0 +1,6 @@
{
"extends": [
"tslint:recommended",
"tslint-config-standard"
]
}