Merge pull request #20 from Microsoft/dev

merge dev into master
This commit is contained in:
ali-hamud 2016-11-02 15:39:26 +02:00 коммит произвёл GitHub
Родитель 50884fba97 3f245e3629
Коммит 90dc0df685
7 изменённых файлов: 244 добавлений и 43 удалений

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

@ -102,4 +102,31 @@ const advancedFilter = new models.AdvancedFilter(
value: "Microsoft"
}
);
```
## Date Formatting
Dates should be formated using [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) standard. Example: `2016-09-08T00:15:46.861Z`
This is how dates are naturally serialized to JSON:
```
new Date().toJSON(); //=> 2016-09-08T00:15:46.861Z
```
An example filter using this Date format would look like the following:
```
{
"$schema": "http://powerbi.com/product/schema#advanced",
"target": {
"table": "Time",
"column": "Date"
},
"logicalOperator": "And",
"conditions": [
{
"operator": "GreaterThan",
"value": "2014-06-01T07:00:00.000Z"
}
]
}
```

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

@ -155,12 +155,7 @@ gulp.task('tslint:build', 'Run TSLint on src', function () {
gulp.task('tslint:test', 'Run TSLint on src and tests', function () {
return gulp.src(["src/**/*.ts", "test/**/*.ts"])
.pipe(tslint({
formatter: "verbose",
configuration: {
rules: {
"no-console": false
}
}
formatter: "verbose"
}))
.pipe(tslint.report());
});

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

@ -1,6 +1,6 @@
{
"name": "powerbi-models",
"version": "0.7.4",
"version": "0.9.1",
"description": "Contains JavaScript & TypeScript object models for Microsoft Power BI JavaScript SDK. For each model there is a TypeScript interface, a json schema definitions, and a validation function to ensure and object is valid.",
"main": "dist/models.js",
"typings": "dist/models.d.ts",

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

@ -3,7 +3,8 @@ declare var require: Function;
/* tslint:disable:no-var-requires */
export const advancedFilterSchema = require('./schemas/advancedFilter.json');
export const filterSchema = require('./schemas/filter.json');
export const loadSchema = require('./schemas/load.json');
export const loadSchema = require('./schemas/reportLoadConfiguration.json');
export const dashboardLoadSchema = require('./schemas/dashboardLoadConfiguration.json');
export const pageSchema = require('./schemas/page.json');
export const settingsSchema = require('./schemas/settings.json');
export const basicFilterSchema = require('./schemas/basicFilter.json');
@ -22,14 +23,15 @@ export interface IError {
}
function normalizeError(error: IValidationError): IError {
if (!error.message) {
error.message = `${error.path} is invalid. Not meeting ${error.keyword} constraint`;
let message = error.message;
if (!message) {
message = `${error.path} is invalid. Not meeting ${error.keyword} constraint`;
}
delete error.path;
delete error.keyword;
return error;
return {
message
};
}
/**
@ -62,7 +64,7 @@ export const validateSettings = validate(settingsSchema, {
}
});
export interface ILoadConfiguration {
export interface IReportLoadConfiguration {
accessToken: string;
id: string;
settings?: ISettings;
@ -70,7 +72,7 @@ export interface ILoadConfiguration {
filters?: (IBasicFilter | IAdvancedFilter)[];
}
export const validateLoad = validate(loadSchema, {
export const validateReportLoad = validate(loadSchema, {
schemas: {
settings: settingsSchema,
basicFilter: basicFilterSchema,
@ -78,6 +80,13 @@ export const validateLoad = validate(loadSchema, {
}
});
export interface IDashboardLoadConfiguration {
accessToken: string;
id: string;
}
export const validateDashboardLoad = validate(dashboardLoadSchema);
export interface IReport {
id: string;
displayName: string;

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

@ -0,0 +1,24 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"type": "object",
"properties": {
"accessToken": {
"type": "string",
"messages": {
"type": "accessToken must be a string",
"required": "accessToken is required"
}
},
"id": {
"type": "string",
"messages": {
"type": "id must be a string",
"required": "id is required"
}
}
},
"required": [
"accessToken",
"id"
]
}

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

@ -7,16 +7,14 @@
"messages": {
"type": "accessToken must be a string",
"required": "accessToken is required"
},
"invalidMessage": "accessToken property is invalid"
}
},
"id": {
"type": "string",
"messages": {
"type": "id must be a string",
"required": "id is required"
},
"invalidMessage": "id property is invalid"
}
},
"settings": {
"$ref": "#settings"
@ -29,14 +27,17 @@
},
"filters": {
"type": "array",
"oneOf": [
{
"$ref": "#basicFilter"
},
{
"$ref": "#advancedFilter"
}
],
"items": {
"type": "object",
"oneOf": [
{
"$ref": "#basicFilter"
},
{
"$ref": "#advancedFilter"
}
]
},
"invalidMessage": "filters property is invalid"
}
},

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

@ -3,15 +3,13 @@ import * as models from '../src/models';
describe('Unit | Models', function () {
function testForExpectedMessage(errors: models.IError[], message: string) {
expect(errors).toBeDefined();
errors
.forEach(error => {
if (error.message === message) {
expect(true).toBe(true);
}
});
const atLeastOneMessageMatches = errors
.some(error => error.message === message);
expect(atLeastOneMessageMatches).toBe(true);
}
describe('validateLoad', function () {
describe('validateReportLoad', function () {
const accessTokenRequiredMessage = models.loadSchema.properties.accessToken.messages.required;
const accessTokenInvalidTypeMessage = models.loadSchema.properties.accessToken.messages.type;
const idRequiredMessage = models.loadSchema.properties.id.messages.required;
@ -27,7 +25,7 @@ describe('Unit | Models', function () {
};
// Act
const errors = models.validateLoad(testData.load);
const errors = models.validateReportLoad(testData.load);
// Assert
testForExpectedMessage(errors, accessTokenRequiredMessage);
@ -42,7 +40,7 @@ describe('Unit | Models', function () {
};
// Act
const errors = models.validateLoad(testData.load);
const errors = models.validateReportLoad(testData.load);
// Assert
testForExpectedMessage(errors, accessTokenInvalidTypeMessage);
@ -52,11 +50,12 @@ describe('Unit | Models', function () {
// Arrange
const testData = {
load: {
accessToken: "fakeToken"
}
};
// Act
const errors = models.validateLoad(testData.load);
const errors = models.validateReportLoad(testData.load);
// Assert
testForExpectedMessage(errors, idRequiredMessage);
@ -66,14 +65,16 @@ describe('Unit | Models', function () {
// Arrange
const testData = {
load: {
accessToken: "fakeToken",
id: 1
}
};
// Act
const errors = models.validateLoad(testData.load);
const errors = models.validateReportLoad(testData.load);
// Assert
testForExpectedMessage(errors, idRequiredMessage);
testForExpectedMessage(errors, idInvalidTypeMessage);
});
it(`should return undefined if id and accessToken are provided`, function () {
@ -86,7 +87,7 @@ describe('Unit | Models', function () {
};
// Act
const errors = models.validateLoad(testData.load);
const errors = models.validateReportLoad(testData.load);
// Assert
expect(errors).toBeUndefined();
@ -103,12 +104,73 @@ describe('Unit | Models', function () {
};
// Act
const errors = models.validateLoad(testData.load);
const errors = models.validateReportLoad(testData.load);
// Assert
testForExpectedMessage(errors, filtersInvalidMessage);
});
it(`should return errors if filters is array, but item is not a valid basicFilter or advancedFilter`, function () {
// Arrange
const testData = {
load: {
id: 'fakeId',
accessToken: 'fakeAccessToken',
filters: [
{ x: 1 }
]
}
};
// Act
const errors = models.validateReportLoad(testData.load);
// Assert
expect(errors.length > 0).toBe(true);
});
// TODO: Need to fix reportLoadConfiguration.json schema so that this fails.
// Currently this validates without errors, but the second object should be rejected since it is not a valid filter.
xit(`should return errors if filters is array, but not all items are valid basicFilter or advancedFilter`, function () {
// Arrange
const testData = {
load: {
id: 'fakeId',
accessToken: 'fakeAccessToken',
filters: [
new models.BasicFilter({ table: "fakeTable", column: "fakeColumn" }, "In", ["A"]).toJSON(),
{ x: 1 }
]
}
};
// Act
const errors = models.validateReportLoad(testData.load);
// Assert
expect(errors).toBeDefined();
expect(errors.length).toBeGreaterThan(0);
});
it(`should return undefined if filters is valid array of basicFilter or advancedFilter`, function () {
// Arrange
const testData = {
load: {
id: 'fakeId',
accessToken: 'fakeAccessToken',
filters: [
new models.BasicFilter({ table: "fakeTable", column: "fakeColumn" }, "In", ["A"]).toJSON()
]
}
};
// Act
const errors = models.validateReportLoad(testData.load);
// Assert
expect(errors).toBeUndefined();
});
it(`should return errors with one containing message '${pageNameInvalidTypeMessage}' if pageName is not a string`, function () {
// Arrange
const testData = {
@ -120,13 +182,96 @@ describe('Unit | Models', function () {
};
// Act
const errors = models.validateLoad(testData.load);
const errors = models.validateReportLoad(testData.load);
// Assert
testForExpectedMessage(errors, pageNameInvalidTypeMessage);
});
});
describe('validateDashboardLoad', function () {
const accessTokenRequiredMessage = models.dashboardLoadSchema.properties.accessToken.messages.required;
const accessTokenInvalidTypeMessage = models.dashboardLoadSchema.properties.accessToken.messages.type;
const idRequiredMessage = models.dashboardLoadSchema.properties.id.messages.required;
const idInvalidTypeMessage = models.dashboardLoadSchema.properties.id.messages.type;
it(`should return errors with one containing message '${accessTokenRequiredMessage}' if accessToken is not defined`, function () {
// Arrange
const testData = {
load: {
}
};
// Act
const errors = models.validateDashboardLoad(testData.load);
// Assert
testForExpectedMessage(errors, accessTokenRequiredMessage);
});
it(`should return errors with one containing message '${accessTokenInvalidTypeMessage}' if accessToken is not a string`, function () {
// Arrange
const testData = {
load: {
accessToken: 1
}
};
// Act
const errors = models.validateDashboardLoad(testData.load);
// Assert
testForExpectedMessage(errors, accessTokenInvalidTypeMessage);
});
it(`should return errors with one containing message '${idRequiredMessage}' if id is not defined`, function () {
// Arrange
const testData = {
load: {
accessToken: "fakeToken"
}
};
// Act
const errors = models.validateDashboardLoad(testData.load);
// Assert
testForExpectedMessage(errors, idRequiredMessage);
});
it(`should return errors with one containing message '${idInvalidTypeMessage}' if id is not a string`, function () {
// Arrange
const testData = {
load: {
accessToken: 'fakeAccessToken',
id: 1
}
};
// Act
const errors = models.validateDashboardLoad(testData.load);
// Assert
testForExpectedMessage(errors, idInvalidTypeMessage);
});
it(`should return undefined if id and accessToken are provided`, function () {
// Arrange
const testData = {
load: {
id: 'fakeId',
accessToken: 'fakeAccessToken'
}
};
// Act
const errors = models.validateDashboardLoad(testData.load);
// Assert
expect(errors).toBeUndefined();
});
});
describe('validateFilter', function () {
it("should return errors if object does not validate against schema", function () {
// Arrange