* 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
.env
# typescript compiled files
dist/

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

@ -1,4 +1,11 @@
language: node_js
before_script:
- npm run compile
script:
- npm run test
node_js:
- "node"
- "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",
"version": "1.0.0",
"description": "A node sdk for building services capable of injecting chaos into PaaS offerings",
"main": "index.js",
"main": "dist/index.js",
"scripts": {
"test": "mocha"
"lint": "npx tslint --project .",
"test": "npm run lint && mocha dist/test/",
"compile": "npm run cleanup && npx tsc",
"cleanup": "rimraf dist/"
},
"keywords": [
"azure",
@ -14,6 +17,11 @@
"author": "microsoft",
"license": "MIT",
"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-config-standard": "^12.0.0",
"eslint-plugin-import": "^2.14.0",
@ -22,7 +30,11 @@
"eslint-plugin-standard": "^4.0.0",
"lodash.isequal": "^4.5.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": {
"shimmer": "^1.2.0",

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

@ -1,43 +1,58 @@
const shimmer = require('shimmer')
const assert = require('assert')
const os = require('os')
const uuidv4 = require('uuid/v4')
import * as assert from 'assert'
import * as os from 'os'
import * as shimmer from 'shimmer'
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()}`
let auditQueue = []
let auditQueue: IAudit[] = []
const audit = (extensionLogArgs, auditOptions) => {
const audit = (extensionLogArgs, auditOptions: IAuditOptions) => {
const { eventName, resources } = auditOptions
auditQueue.push({
auditId: uuidv4(),
eventName: eventName,
system: auditSystem,
date: new Date().toISOString(),
resources: resources,
extensionLog: typeof extensionLogArgs === 'string' ? [ extensionLogArgs ] : Array.from(extensionLogArgs)
eventName,
extensionLog: typeof extensionLogArgs === 'string' ? [ extensionLogArgs ] : Array.from(extensionLogArgs),
resources,
system: auditSystem
})
}
module.exports = {
audit: audit,
initialize: (context, opts) => {
export default {
audit,
initialize: (context: any, opts: IAuditOptions) => {
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
auditQueue = []
shimmer.wrap(context, 'log', function (original) {
shimmer.wrap(context, 'log', (original: (message: any) => void) => {
return function () {
audit(arguments, auditOptions)
return original.apply(this, arguments)
}
})
shimmer.wrap(context, 'done', function (original) {
shimmer.wrap(context, 'done', (original: (err: any, propertyBag: any) => void) => {
return function () {
const audits = auditQueue
@ -47,10 +62,10 @@ module.exports = {
}
} else if (typeof context.res.body === 'string') {
const body = JSON.parse(context.res.body)
body['__audits'] = audits
body.__audits = audits
context.res.body = JSON.stringify(body)
} 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

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) => {
// ensure we have a valid accessToken before proceeding
validate.accessToken(req)
// 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 {
signRequest: (webResource, callback) => {
webResource.headers['Authorization'] = req.body.accessToken
webResource.headers.Authorization = req.body.accessToken
callback(null)
}
}
@ -19,11 +20,11 @@ module.exports = {
validate.resources(req)
// 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 {
subscriptionId: r[0],
resourceGroupName: r[1],
resourceName: r[2]
resourceName: r[2],
subscriptionId: r[0]
}
})
}

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

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