@stryker-mutator/dashboard-data-access 0.14.4-pr-888.4 → 0.14.4-pr-889.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/mappers/DashboardQuery.d.ts +2 -2
- package/dist/src/mappers/DashboardQuery.js +8 -6
- package/dist/src/mappers/MutationTestingResultMapper.d.ts +5 -4
- package/dist/src/mappers/MutationTestingResultMapper.js +15 -31
- package/dist/src/mappers/TableStorageMapper.d.ts +3 -3
- package/dist/src/mappers/TableStorageMapper.js +29 -68
- package/dist/src/services/BlobServiceAsPromised.d.ts +11 -0
- package/dist/src/services/BlobServiceAsPromised.js +13 -0
- package/dist/src/services/RealTimeMutantsBlobService.d.ts +2 -2
- package/dist/src/services/RealTimeMutantsBlobService.js +14 -12
- package/dist/src/services/TableServiceAsPromised.d.ts +32 -0
- package/dist/src/services/TableServiceAsPromised.js +13 -0
- package/dist/src/utils.d.ts +2 -5
- package/dist/src/utils.js +2 -8
- package/package.json +4 -6
- package/dist/src/services/BlobServiceClient.d.ts +0 -3
- package/dist/src/services/BlobServiceClient.js +0 -11
- package/dist/src/services/TableClient.d.ts +0 -3
- package/dist/src/services/TableClient.js +0 -13
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ModelClass } from './ModelClass.js';
|
|
2
|
-
import {
|
|
2
|
+
import { TableQuery } from 'azure-storage';
|
|
3
3
|
export declare class DashboardQuery<TModel, TPartitionKeyFields extends keyof TModel, TRowKeyFields extends keyof TModel> {
|
|
4
4
|
protected ModelClass: ModelClass<TModel, TPartitionKeyFields, TRowKeyFields>;
|
|
5
5
|
private readonly whereConditions;
|
|
@@ -7,6 +7,6 @@ export declare class DashboardQuery<TModel, TPartitionKeyFields extends keyof TM
|
|
|
7
7
|
whereRowKeyNotEquals(rowKey: Pick<TModel, TRowKeyFields>): DashboardQuery<TModel, TPartitionKeyFields, TRowKeyFields>;
|
|
8
8
|
wherePartitionKeyEquals(partitionKey: Pick<TModel, TPartitionKeyFields>): DashboardQuery<TModel, TPartitionKeyFields, TRowKeyFields>;
|
|
9
9
|
static create<TModel, TPartitionKeyFields extends keyof TModel, TRowKeyFields extends keyof TModel>(ModelClass: ModelClass<TModel, TPartitionKeyFields, TRowKeyFields>): DashboardQuery<TModel, TPartitionKeyFields, TRowKeyFields>;
|
|
10
|
-
build():
|
|
10
|
+
build(): TableQuery;
|
|
11
11
|
}
|
|
12
12
|
//# sourceMappingURL=DashboardQuery.d.ts.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { TableQuery } from 'azure-storage';
|
|
1
2
|
import { encodeKey } from '../utils.js';
|
|
2
|
-
import { odata } from '@azure/data-tables';
|
|
3
3
|
export class DashboardQuery {
|
|
4
4
|
constructor(ModelClass, whereConditions) {
|
|
5
5
|
this.ModelClass = ModelClass;
|
|
@@ -7,13 +7,15 @@ export class DashboardQuery {
|
|
|
7
7
|
}
|
|
8
8
|
whereRowKeyNotEquals(rowKey) {
|
|
9
9
|
const whereCondition = {
|
|
10
|
-
condition:
|
|
10
|
+
condition: 'not(RowKey eq ?)',
|
|
11
|
+
params: [encodeKey(this.ModelClass.createRowKey(rowKey) || '')],
|
|
11
12
|
};
|
|
12
13
|
return new DashboardQuery(this.ModelClass, [...this.whereConditions, whereCondition]);
|
|
13
14
|
}
|
|
14
15
|
wherePartitionKeyEquals(partitionKey) {
|
|
15
16
|
const whereCondition = {
|
|
16
|
-
condition:
|
|
17
|
+
condition: 'PartitionKey eq ?',
|
|
18
|
+
params: [encodeKey(this.ModelClass.createPartitionKey(partitionKey))],
|
|
17
19
|
};
|
|
18
20
|
return new DashboardQuery(this.ModelClass, [...this.whereConditions, whereCondition]);
|
|
19
21
|
}
|
|
@@ -23,12 +25,12 @@ export class DashboardQuery {
|
|
|
23
25
|
build() {
|
|
24
26
|
return this.whereConditions.reduce((tableQuery, whereCondition, index) => {
|
|
25
27
|
if (index === 0) {
|
|
26
|
-
return
|
|
28
|
+
return tableQuery.where(whereCondition.condition, ...whereCondition.params);
|
|
27
29
|
}
|
|
28
30
|
else {
|
|
29
|
-
return
|
|
31
|
+
return tableQuery.and(whereCondition.condition, whereCondition.params);
|
|
30
32
|
}
|
|
31
|
-
},
|
|
33
|
+
}, new TableQuery());
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
//# sourceMappingURL=DashboardQuery.js.map
|
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import { BlobServiceAsPromised } from '../services/BlobServiceAsPromised.js';
|
|
2
|
+
import { BlobService } from 'azure-storage';
|
|
1
3
|
import * as schema from 'mutation-testing-report-schema';
|
|
2
4
|
import { ReportIdentifier } from '@stryker-mutator/dashboard-common';
|
|
3
|
-
import { BlobServiceClient, ContainerCreateIfNotExistsResponse } from '@azure/storage-blob';
|
|
4
5
|
/**
|
|
5
6
|
* The report json part of a mutation testing report is stored in blob storage
|
|
6
7
|
*/
|
|
7
8
|
export declare class MutationTestingResultMapper {
|
|
8
|
-
|
|
9
|
+
private readonly blobService;
|
|
9
10
|
private static readonly CONTAINER_NAME;
|
|
10
|
-
constructor(blobService?:
|
|
11
|
-
createStorageIfNotExists(): Promise<
|
|
11
|
+
constructor(blobService?: BlobServiceAsPromised);
|
|
12
|
+
createStorageIfNotExists(): Promise<BlobService.ContainerResult>;
|
|
12
13
|
insertOrReplace(id: ReportIdentifier, result: schema.MutationTestResult | null): Promise<void>;
|
|
13
14
|
findOne(identifier: ReportIdentifier): Promise<schema.MutationTestResult | null>;
|
|
14
15
|
delete(id: ReportIdentifier): Promise<void>;
|
|
@@ -1,46 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _MutationTestingResultMapper_containerClient;
|
|
13
|
-
import { hasDetailsCode, isStorageError, toBlobName } from '../utils.js';
|
|
1
|
+
import { BlobServiceAsPromised } from '../services/BlobServiceAsPromised.js';
|
|
2
|
+
import { Constants } from 'azure-storage';
|
|
3
|
+
import { isStorageError, toBlobName } from '../utils.js';
|
|
14
4
|
import { OptimisticConcurrencyError } from '../errors/index.js';
|
|
15
|
-
|
|
16
|
-
const errCodes = Object.freeze({
|
|
5
|
+
const additionalErrorCodes = Object.freeze({
|
|
17
6
|
BLOB_HAS_BEEN_MODIFIED: 'BlobHasBeenModified',
|
|
18
|
-
BLOB_NOT_FOUND: 'BlobNotFound',
|
|
19
7
|
});
|
|
20
8
|
/**
|
|
21
9
|
* The report json part of a mutation testing report is stored in blob storage
|
|
22
10
|
*/
|
|
23
11
|
export class MutationTestingResultMapper {
|
|
24
|
-
constructor(blobService =
|
|
25
|
-
|
|
26
|
-
__classPrivateFieldSet(this, _MutationTestingResultMapper_containerClient, blobService.getContainerClient(MutationTestingResultMapper.CONTAINER_NAME), "f");
|
|
12
|
+
constructor(blobService = new BlobServiceAsPromised()) {
|
|
13
|
+
this.blobService = blobService;
|
|
27
14
|
}
|
|
28
15
|
createStorageIfNotExists() {
|
|
29
|
-
return
|
|
16
|
+
return this.blobService.createContainerIfNotExists(MutationTestingResultMapper.CONTAINER_NAME, {});
|
|
30
17
|
}
|
|
31
18
|
async insertOrReplace(id, result) {
|
|
32
19
|
try {
|
|
33
|
-
await
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
blobContentType: 'application/json',
|
|
38
|
-
blobContentEncoding: 'utf8',
|
|
20
|
+
await this.blobService.createBlockBlobFromText(MutationTestingResultMapper.CONTAINER_NAME, toBlobName(id), JSON.stringify(result), {
|
|
21
|
+
contentSettings: {
|
|
22
|
+
contentType: 'application/json',
|
|
23
|
+
contentEncoding: 'utf8',
|
|
39
24
|
},
|
|
40
25
|
});
|
|
41
26
|
}
|
|
42
27
|
catch (err) {
|
|
43
|
-
if (isStorageError(err) &&
|
|
28
|
+
if (isStorageError(err) && err.code === additionalErrorCodes.BLOB_HAS_BEEN_MODIFIED) {
|
|
44
29
|
throw new OptimisticConcurrencyError(`Blob "${JSON.stringify(id)}" was modified by another process`);
|
|
45
30
|
}
|
|
46
31
|
else {
|
|
@@ -51,11 +36,11 @@ export class MutationTestingResultMapper {
|
|
|
51
36
|
async findOne(identifier) {
|
|
52
37
|
const blobName = toBlobName(identifier);
|
|
53
38
|
try {
|
|
54
|
-
const result = JSON.parse(
|
|
39
|
+
const result = JSON.parse(await this.blobService.blobToText(MutationTestingResultMapper.CONTAINER_NAME, blobName));
|
|
55
40
|
return result;
|
|
56
41
|
}
|
|
57
42
|
catch (error) {
|
|
58
|
-
if (isStorageError(error) &&
|
|
43
|
+
if (isStorageError(error) && error.code === Constants.BlobErrorCodeStrings.BLOB_NOT_FOUND) {
|
|
59
44
|
return null;
|
|
60
45
|
}
|
|
61
46
|
else {
|
|
@@ -66,9 +51,8 @@ export class MutationTestingResultMapper {
|
|
|
66
51
|
}
|
|
67
52
|
async delete(id) {
|
|
68
53
|
const blobName = toBlobName(id);
|
|
69
|
-
await
|
|
54
|
+
await this.blobService.deleteBlobIfExists(MutationTestingResultMapper.CONTAINER_NAME, blobName);
|
|
70
55
|
}
|
|
71
56
|
}
|
|
72
|
-
_MutationTestingResultMapper_containerClient = new WeakMap();
|
|
73
57
|
MutationTestingResultMapper.CONTAINER_NAME = 'mutation-testing-report';
|
|
74
58
|
//# sourceMappingURL=MutationTestingResultMapper.js.map
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import TableServiceAsPromised from '../services/TableServiceAsPromised.js';
|
|
1
2
|
import { Mapper, Result } from './Mapper.js';
|
|
2
3
|
import { ModelClass } from './ModelClass.js';
|
|
3
4
|
import { DashboardQuery } from './DashboardQuery.js';
|
|
4
|
-
import { TableClient } from '@azure/data-tables';
|
|
5
5
|
export default class TableStorageMapper<TModel extends object, TPartitionKeyFields extends keyof TModel, TRowKeyFields extends keyof TModel> implements Mapper<TModel, TPartitionKeyFields, TRowKeyFields> {
|
|
6
|
-
#private;
|
|
7
6
|
private readonly ModelClass;
|
|
8
|
-
|
|
7
|
+
private readonly tableService;
|
|
8
|
+
constructor(ModelClass: ModelClass<TModel, TPartitionKeyFields, TRowKeyFields>, tableService?: TableServiceAsPromised);
|
|
9
9
|
createStorageIfNotExists(): Promise<void>;
|
|
10
10
|
insertOrMerge(model: TModel): Promise<void>;
|
|
11
11
|
findOne(identity: Pick<TModel, TPartitionKeyFields | TRowKeyFields>): Promise<Result<TModel> | null>;
|
|
@@ -1,51 +1,28 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
13
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
14
|
-
var m = o[Symbol.asyncIterator], i;
|
|
15
|
-
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
16
|
-
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
17
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
18
|
-
};
|
|
19
|
-
var _TableStorageMapper_tableClient;
|
|
20
|
-
import { encodeKey, decodeKey, isStorageError, hasDetailsCode } from '../utils.js';
|
|
1
|
+
import { Constants } from 'azure-storage';
|
|
2
|
+
import TableServiceAsPromised from '../services/TableServiceAsPromised.js';
|
|
3
|
+
import { encodeKey, decodeKey, isStorageError } from '../utils.js';
|
|
21
4
|
import { OptimisticConcurrencyError } from '../errors/index.js';
|
|
22
5
|
import { DashboardQuery } from './DashboardQuery.js';
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
UPDATE_CONDITION_NOT_SATISFIED: 'UpdateConditionNotSatisfied',
|
|
26
|
-
ENTITY_ALREADY_EXISTS: 'EntityAlreadyExists',
|
|
27
|
-
RESOURCE_NOT_FOUND: 'ResourceNotFound',
|
|
28
|
-
});
|
|
29
|
-
class TableStorageMapper {
|
|
30
|
-
constructor(ModelClass, tableClient = createTableClient(ModelClass.name)) {
|
|
6
|
+
export default class TableStorageMapper {
|
|
7
|
+
constructor(ModelClass, tableService = new TableServiceAsPromised()) {
|
|
31
8
|
this.ModelClass = ModelClass;
|
|
32
|
-
|
|
33
|
-
__classPrivateFieldSet(this, _TableStorageMapper_tableClient, tableClient, "f");
|
|
9
|
+
this.tableService = tableService;
|
|
34
10
|
}
|
|
35
11
|
async createStorageIfNotExists() {
|
|
36
|
-
await
|
|
12
|
+
await this.tableService.createTableIfNotExists(this.ModelClass.tableName);
|
|
37
13
|
}
|
|
38
14
|
async insertOrMerge(model) {
|
|
39
15
|
const entity = this.toEntity(model);
|
|
40
|
-
await
|
|
16
|
+
await this.tableService.insertOrMergeEntity(this.ModelClass.tableName, entity);
|
|
41
17
|
}
|
|
42
18
|
async findOne(identity) {
|
|
43
19
|
try {
|
|
44
|
-
const result = await
|
|
20
|
+
const result = await this.tableService.retrieveEntity(this.ModelClass.tableName, encodeKey(this.ModelClass.createPartitionKey(identity)), encodeKey(this.ModelClass.createRowKey(identity) || ''));
|
|
45
21
|
return this.toModel(result);
|
|
46
22
|
}
|
|
47
23
|
catch (err) {
|
|
48
|
-
if (isStorageError(err) &&
|
|
24
|
+
if (isStorageError(err) &&
|
|
25
|
+
err.code === Constants.StorageErrorCodeStrings.RESOURCE_NOT_FOUND) {
|
|
49
26
|
return null;
|
|
50
27
|
}
|
|
51
28
|
else {
|
|
@@ -55,26 +32,9 @@ class TableStorageMapper {
|
|
|
55
32
|
}
|
|
56
33
|
}
|
|
57
34
|
async findAll(query = DashboardQuery.create(this.ModelClass)) {
|
|
58
|
-
var _a, e_1, _b, _c;
|
|
59
35
|
const tableQuery = query.build();
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
for (var _d = true, entities_1 = __asyncValues(entities), entities_1_1; entities_1_1 = await entities_1.next(), _a = entities_1_1.done, !_a; _d = true) {
|
|
64
|
-
_c = entities_1_1.value;
|
|
65
|
-
_d = false;
|
|
66
|
-
const entity = _c;
|
|
67
|
-
results.push(this.toModel(entity));
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
71
|
-
finally {
|
|
72
|
-
try {
|
|
73
|
-
if (!_d && !_a && (_b = entities_1.return)) await _b.call(entities_1);
|
|
74
|
-
}
|
|
75
|
-
finally { if (e_1) throw e_1.error; }
|
|
76
|
-
}
|
|
77
|
-
return results;
|
|
36
|
+
const results = await this.tableService.queryEntities(this.ModelClass.tableName, tableQuery, undefined);
|
|
37
|
+
return results.entries.map((entity) => this.toModel(entity));
|
|
78
38
|
}
|
|
79
39
|
/**
|
|
80
40
|
* Replace an entity of a specific version (throws error otherwise)
|
|
@@ -84,14 +44,15 @@ class TableStorageMapper {
|
|
|
84
44
|
*/
|
|
85
45
|
async replace(model, etag) {
|
|
86
46
|
const entity = this.toEntity(model);
|
|
47
|
+
entity['.metadata'].etag = etag;
|
|
87
48
|
try {
|
|
88
|
-
const result = await
|
|
89
|
-
return { model, etag: result.etag };
|
|
49
|
+
const result = await this.tableService.replaceEntity(this.ModelClass.tableName, entity, {});
|
|
50
|
+
return { model, etag: result['.metadata'].etag };
|
|
90
51
|
}
|
|
91
52
|
catch (err) {
|
|
92
53
|
if (isStorageError(err) &&
|
|
93
|
-
|
|
94
|
-
throw new OptimisticConcurrencyError(`Replace entity with etag ${etag} resulted in ${
|
|
54
|
+
err.code === Constants.StorageErrorCodeStrings.UPDATE_CONDITION_NOT_SATISFIED) {
|
|
55
|
+
throw new OptimisticConcurrencyError(`Replace entity with etag ${etag} resulted in ${Constants.StorageErrorCodeStrings.UPDATE_CONDITION_NOT_SATISFIED}`);
|
|
95
56
|
}
|
|
96
57
|
else {
|
|
97
58
|
throw err;
|
|
@@ -101,12 +62,13 @@ class TableStorageMapper {
|
|
|
101
62
|
async insert(model) {
|
|
102
63
|
const entity = this.toEntity(model);
|
|
103
64
|
try {
|
|
104
|
-
const result = await
|
|
105
|
-
return { model, etag: result.etag };
|
|
65
|
+
const result = await this.tableService.insertEntity(this.ModelClass.tableName, entity, {});
|
|
66
|
+
return { model, etag: result['.metadata'].etag };
|
|
106
67
|
}
|
|
107
68
|
catch (err) {
|
|
108
|
-
if (isStorageError(err) &&
|
|
109
|
-
|
|
69
|
+
if (isStorageError(err) &&
|
|
70
|
+
err.code === Constants.TableErrorCodeStrings.ENTITY_ALREADY_EXISTS) {
|
|
71
|
+
throw new OptimisticConcurrencyError(`Trying to insert "${entity.PartitionKey}" "${entity.RowKey}" which already exists (${Constants.TableErrorCodeStrings.ENTITY_ALREADY_EXISTS})`);
|
|
110
72
|
}
|
|
111
73
|
else {
|
|
112
74
|
throw err;
|
|
@@ -115,22 +77,21 @@ class TableStorageMapper {
|
|
|
115
77
|
}
|
|
116
78
|
toModel(entity) {
|
|
117
79
|
const value = new this.ModelClass();
|
|
118
|
-
this.ModelClass.identify(value, decodeKey(entity.
|
|
119
|
-
this.ModelClass.persistedFields.forEach((field) => (value[field] = entity[field]));
|
|
80
|
+
this.ModelClass.identify(value, decodeKey(entity.PartitionKey._), decodeKey(entity.RowKey._));
|
|
81
|
+
this.ModelClass.persistedFields.forEach((field) => (value[field] = entity[field]._));
|
|
120
82
|
return {
|
|
121
|
-
etag: entity.etag,
|
|
83
|
+
etag: entity['.metadata'].etag,
|
|
122
84
|
model: value,
|
|
123
85
|
};
|
|
124
86
|
}
|
|
125
87
|
toEntity(entity) {
|
|
126
88
|
const data = {
|
|
127
|
-
|
|
128
|
-
|
|
89
|
+
PartitionKey: encodeKey(this.ModelClass.createPartitionKey(entity)),
|
|
90
|
+
RowKey: encodeKey(this.ModelClass.createRowKey(entity) || ''),
|
|
129
91
|
};
|
|
130
92
|
this.ModelClass.persistedFields.forEach((field) => (data[field] = entity[field]));
|
|
93
|
+
data['.metadata'] = {};
|
|
131
94
|
return data;
|
|
132
95
|
}
|
|
133
96
|
}
|
|
134
|
-
_TableStorageMapper_tableClient = new WeakMap();
|
|
135
|
-
export default TableStorageMapper;
|
|
136
97
|
//# sourceMappingURL=TableStorageMapper.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BlobService } from 'azure-storage';
|
|
2
|
+
export declare class BlobServiceAsPromised {
|
|
3
|
+
createContainerIfNotExists: (container: string, options: BlobService.CreateContainerOptions) => Promise<BlobService.ContainerResult>;
|
|
4
|
+
createBlockBlobFromText: (container: string, blob: string, text: string | Buffer, options: BlobService.CreateBlobRequestOptions) => Promise<BlobService.BlobResult>;
|
|
5
|
+
blobToText: (container: string, blob: string) => Promise<string>;
|
|
6
|
+
createAppendBlobFromText: (container: string, blob: string, text: string | Buffer) => Promise<BlobService.BlobResult>;
|
|
7
|
+
appendBlockFromText: (container: string, blob: string, text: string | Buffer) => Promise<BlobService.BlobResult>;
|
|
8
|
+
deleteBlobIfExists: (container: string, blob: string) => Promise<unknown>;
|
|
9
|
+
constructor(blobService?: BlobService);
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=BlobServiceAsPromised.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { createBlobService } from 'azure-storage';
|
|
2
|
+
import { promisify } from 'util';
|
|
3
|
+
export class BlobServiceAsPromised {
|
|
4
|
+
constructor(blobService = createBlobService()) {
|
|
5
|
+
this.createContainerIfNotExists = promisify(blobService.createContainerIfNotExists).bind(blobService);
|
|
6
|
+
this.createBlockBlobFromText = promisify(blobService.createBlockBlobFromText).bind(blobService);
|
|
7
|
+
this.blobToText = promisify(blobService.getBlobToText).bind(blobService);
|
|
8
|
+
this.createAppendBlobFromText = promisify(blobService.createAppendBlobFromText).bind(blobService);
|
|
9
|
+
this.appendBlockFromText = promisify(blobService.appendBlockFromText).bind(blobService);
|
|
10
|
+
this.deleteBlobIfExists = promisify(blobService.deleteBlobIfExists).bind(blobService);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=BlobServiceAsPromised.js.map
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { ReportIdentifier } from '@stryker-mutator/dashboard-common';
|
|
2
|
+
import { BlobServiceAsPromised } from './BlobServiceAsPromised.js';
|
|
2
3
|
import { MutantResult } from 'mutation-testing-report-schema';
|
|
3
|
-
import { BlobServiceClient } from '@azure/storage-blob';
|
|
4
4
|
export declare class RealTimeMutantsBlobService {
|
|
5
5
|
#private;
|
|
6
6
|
private static readonly CONTAINER_NAME;
|
|
7
|
-
constructor(blobService?:
|
|
7
|
+
constructor(blobService?: BlobServiceAsPromised);
|
|
8
8
|
createStorageIfNotExists(): Promise<void>;
|
|
9
9
|
createReport(id: ReportIdentifier): Promise<void>;
|
|
10
10
|
appendToReport(id: ReportIdentifier, mutants: Array<Partial<MutantResult>>): Promise<void>;
|
|
@@ -9,31 +9,33 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
9
9
|
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
10
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
11
|
};
|
|
12
|
-
var
|
|
12
|
+
var _RealTimeMutantsBlobService_blobService;
|
|
13
|
+
import { BlobServiceAsPromised } from './BlobServiceAsPromised.js';
|
|
13
14
|
import { toBlobName } from '../utils.js';
|
|
14
|
-
import { createBlobServiceClient } from './BlobServiceClient.js';
|
|
15
15
|
// To make resource delete themselves automatically, this should be managed from within Azure:
|
|
16
16
|
// https://learn.microsoft.com/en-us/azure/storage/blobs/lifecycle-management-overview?tabs=azure-portal
|
|
17
17
|
export class RealTimeMutantsBlobService {
|
|
18
|
-
constructor(blobService =
|
|
19
|
-
|
|
20
|
-
__classPrivateFieldSet(this,
|
|
18
|
+
constructor(blobService = new BlobServiceAsPromised()) {
|
|
19
|
+
_RealTimeMutantsBlobService_blobService.set(this, void 0);
|
|
20
|
+
__classPrivateFieldSet(this, _RealTimeMutantsBlobService_blobService, blobService, "f");
|
|
21
21
|
}
|
|
22
22
|
async createStorageIfNotExists() {
|
|
23
|
-
await __classPrivateFieldGet(this,
|
|
23
|
+
await __classPrivateFieldGet(this, _RealTimeMutantsBlobService_blobService, "f").createContainerIfNotExists(RealTimeMutantsBlobService.CONTAINER_NAME, {});
|
|
24
24
|
}
|
|
25
25
|
async createReport(id) {
|
|
26
|
-
await __classPrivateFieldGet(this,
|
|
26
|
+
await __classPrivateFieldGet(this, _RealTimeMutantsBlobService_blobService, "f").createAppendBlobFromText(RealTimeMutantsBlobService.CONTAINER_NAME, toBlobName(id), '');
|
|
27
27
|
}
|
|
28
28
|
async appendToReport(id, mutants) {
|
|
29
29
|
const blobName = toBlobName(id);
|
|
30
30
|
const data = mutants.map((mutant) => `${JSON.stringify(mutant)}\n`).join('');
|
|
31
|
-
await __classPrivateFieldGet(this,
|
|
31
|
+
await __classPrivateFieldGet(this, _RealTimeMutantsBlobService_blobService, "f").appendBlockFromText(RealTimeMutantsBlobService.CONTAINER_NAME, blobName, data);
|
|
32
32
|
}
|
|
33
33
|
async getReport(id) {
|
|
34
|
-
const data = await __classPrivateFieldGet(this,
|
|
34
|
+
const data = await __classPrivateFieldGet(this, _RealTimeMutantsBlobService_blobService, "f").blobToText(RealTimeMutantsBlobService.CONTAINER_NAME, toBlobName(id));
|
|
35
|
+
if (data === '') {
|
|
36
|
+
return [];
|
|
37
|
+
}
|
|
35
38
|
return (data
|
|
36
|
-
.toString('utf-8')
|
|
37
39
|
.split('\n')
|
|
38
40
|
// Since every line has a newline it will produce an empty string in the list.
|
|
39
41
|
// Remove it, so nothing breaks.
|
|
@@ -42,9 +44,9 @@ export class RealTimeMutantsBlobService {
|
|
|
42
44
|
}
|
|
43
45
|
async delete(id) {
|
|
44
46
|
const blobName = toBlobName(id);
|
|
45
|
-
|
|
47
|
+
__classPrivateFieldGet(this, _RealTimeMutantsBlobService_blobService, "f").deleteBlobIfExists(RealTimeMutantsBlobService.CONTAINER_NAME, blobName);
|
|
46
48
|
}
|
|
47
49
|
}
|
|
48
|
-
|
|
50
|
+
_RealTimeMutantsBlobService_blobService = new WeakMap();
|
|
49
51
|
RealTimeMutantsBlobService.CONTAINER_NAME = 'real-time-mutant-results';
|
|
50
52
|
//# sourceMappingURL=RealTimeMutantsBlobService.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { TableService, TableQuery, common } from 'azure-storage';
|
|
2
|
+
export type Entity<T, TKeyFields extends keyof T> = {
|
|
3
|
+
[K in Exclude<keyof T, TKeyFields>]: {
|
|
4
|
+
$: string;
|
|
5
|
+
_: T[K];
|
|
6
|
+
};
|
|
7
|
+
} & EntityKey & EntityMetadata;
|
|
8
|
+
export interface EntityKey {
|
|
9
|
+
PartitionKey: {
|
|
10
|
+
$: 'Edm.String';
|
|
11
|
+
_: string;
|
|
12
|
+
};
|
|
13
|
+
RowKey: {
|
|
14
|
+
$: 'Edm.String';
|
|
15
|
+
_: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export interface EntityMetadata {
|
|
19
|
+
['.metadata']: {
|
|
20
|
+
etag: string;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export default class TableServiceAsPromised {
|
|
24
|
+
constructor(tableService?: TableService);
|
|
25
|
+
insertEntity: (table: string, entityDescriptor: unknown, options: common.RequestOptions) => Promise<TableService.EntityMetadata>;
|
|
26
|
+
replaceEntity: (table: string, entityDescriptor: unknown, options: common.RequestOptions) => Promise<TableService.EntityMetadata>;
|
|
27
|
+
createTableIfNotExists: (name: string) => Promise<TableService.TableResult>;
|
|
28
|
+
queryEntities: <T, Keys extends keyof T>(table: string, tableQuery: TableQuery, cancellationToken: TableService.TableContinuationToken | undefined) => Promise<TableService.QueryEntitiesResult<Entity<T, Keys> & EntityKey>>;
|
|
29
|
+
insertOrMergeEntity: (table: string, entity: any) => Promise<TableService.EntityMetadata>;
|
|
30
|
+
retrieveEntity: <TResult>(table: string, partitionKey: string, rowKey: string, options?: TableService.TableEntityRequestOptions) => Promise<TResult>;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=TableServiceAsPromised.d.ts.map
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { promisify } from 'util';
|
|
2
|
+
import { createTableService } from 'azure-storage';
|
|
3
|
+
export default class TableServiceAsPromised {
|
|
4
|
+
constructor(tableService = createTableService()) {
|
|
5
|
+
this.createTableIfNotExists = promisify(tableService.createTableIfNotExists).bind(tableService);
|
|
6
|
+
this.queryEntities = promisify(tableService.queryEntities).bind(tableService);
|
|
7
|
+
this.insertOrMergeEntity = promisify(tableService.insertOrMergeEntity).bind(tableService);
|
|
8
|
+
this.retrieveEntity = promisify(tableService.retrieveEntity).bind(tableService);
|
|
9
|
+
this.replaceEntity = promisify(tableService.replaceEntity).bind(tableService);
|
|
10
|
+
this.insertEntity = promisify(tableService.insertEntity).bind(tableService);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=TableServiceAsPromised.js.map
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { ReportIdentifier } from '@stryker-mutator/dashboard-common';
|
|
2
|
-
import {
|
|
2
|
+
import { StorageError } from 'azure-storage';
|
|
3
3
|
export declare function encodeKey(inputWithSlashes: string): string;
|
|
4
4
|
export declare function decodeKey(inputWithSemiColons: string): string;
|
|
5
|
-
export declare function isStorageError(maybeStorageError: unknown): maybeStorageError is
|
|
6
|
-
export declare function hasDetailsCode(maybeDetails: unknown, code: string): maybeDetails is {
|
|
7
|
-
errorCode: string;
|
|
8
|
-
};
|
|
5
|
+
export declare function isStorageError(maybeStorageError: unknown): maybeStorageError is StorageError;
|
|
9
6
|
export declare function toBlobName({ projectName, version, moduleName, realTime }: ReportIdentifier): string;
|
|
10
7
|
//# sourceMappingURL=utils.d.ts.map
|
package/dist/src/utils.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { RestError } from '@azure/storage-blob';
|
|
2
1
|
export function encodeKey(inputWithSlashes) {
|
|
3
2
|
return inputWithSlashes.replace(/\//g, ';');
|
|
4
3
|
}
|
|
@@ -6,13 +5,8 @@ export function decodeKey(inputWithSemiColons) {
|
|
|
6
5
|
return inputWithSemiColons.replace(/;/g, '/');
|
|
7
6
|
}
|
|
8
7
|
export function isStorageError(maybeStorageError) {
|
|
9
|
-
return maybeStorageError instanceof
|
|
10
|
-
|
|
11
|
-
export function hasDetailsCode(maybeDetails, code) {
|
|
12
|
-
return (typeof maybeDetails === 'object' &&
|
|
13
|
-
maybeDetails !== null &&
|
|
14
|
-
'errorCode' in maybeDetails &&
|
|
15
|
-
maybeDetails.errorCode === code);
|
|
8
|
+
return (maybeStorageError instanceof Error &&
|
|
9
|
+
maybeStorageError.name === 'StorageError');
|
|
16
10
|
}
|
|
17
11
|
export function toBlobName({ projectName, version, moduleName, realTime }) {
|
|
18
12
|
const slug = [projectName, version, moduleName, realTime ? 'real-time' : realTime]
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stryker-mutator/dashboard-data-access",
|
|
3
|
-
"version": "0.14.4-pr-
|
|
3
|
+
"version": "0.14.4-pr-889.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "This package contains the data access layer of the stryker dashboard application.",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"test": "c8 --check-coverage --reporter=html --report-dir=reports/coverage --lines 80 --functions 75 --branches 65 mocha --node-option enable-source-maps \"dist/test/helpers/**/*.js\" \"dist/test/unit/**/*.js\"",
|
|
8
|
-
"test:integration": "mocha --node-option enable-source-maps \"dist/test/helpers/**/*.js\" \"dist/test/integration/**/*.js\"",
|
|
9
8
|
"stryker": "stryker run"
|
|
10
9
|
},
|
|
11
10
|
"publishConfig": {
|
|
@@ -22,11 +21,10 @@
|
|
|
22
21
|
"typings": "src/index.ts",
|
|
23
22
|
"license": "ISC",
|
|
24
23
|
"dependencies": {
|
|
25
|
-
"@
|
|
26
|
-
"
|
|
27
|
-
"@stryker-mutator/dashboard-common": "0.14.4-pr-888.4",
|
|
24
|
+
"@stryker-mutator/dashboard-common": "0.14.4-pr-889.0",
|
|
25
|
+
"azure-storage": "2.10.7",
|
|
28
26
|
"mutation-testing-metrics": "3.2.0",
|
|
29
27
|
"mutation-testing-report-schema": "3.1.1"
|
|
30
28
|
},
|
|
31
|
-
"gitHead": "
|
|
29
|
+
"gitHead": "3fdcbcfc2c3da666c22ba37b605fbc29064ef89d"
|
|
32
30
|
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { BlobServiceClient } from '@azure/storage-blob';
|
|
2
|
-
export const createBlobServiceClient = () => {
|
|
3
|
-
const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
|
|
4
|
-
if (connectionString) {
|
|
5
|
-
return BlobServiceClient.fromConnectionString(connectionString);
|
|
6
|
-
}
|
|
7
|
-
else {
|
|
8
|
-
throw new Error('AZURE_STORAGE_CONNECTION_STRING is not set');
|
|
9
|
-
}
|
|
10
|
-
};
|
|
11
|
-
//# sourceMappingURL=BlobServiceClient.js.map
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { TableClient } from '@azure/data-tables';
|
|
2
|
-
export const createTableClient = (tableName) => {
|
|
3
|
-
const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
|
|
4
|
-
if (connectionString) {
|
|
5
|
-
return TableClient.fromConnectionString(connectionString, tableName, {
|
|
6
|
-
allowInsecureConnection: process.env.NODE_ENV !== 'production',
|
|
7
|
-
});
|
|
8
|
-
}
|
|
9
|
-
else {
|
|
10
|
-
throw new Error('AZURE_STORAGE_CONNECTION_STRING is not set');
|
|
11
|
-
}
|
|
12
|
-
};
|
|
13
|
-
//# sourceMappingURL=TableClient.js.map
|