Skip to content

Commit

Permalink
Story/000255/time fields (#260)
Browse files Browse the repository at this point in the history
* added time fields

* added new time fields to backlogitem

* handle status update side-effects

* status change time fields update working

* added test cases for all statuses

* covered status change with time field already set

* fix open-cli issue

* cover last branch for tests

* added 4 date fields

Co-authored-by: Kevin <[email protected]>
  • Loading branch information
51ngul4r1ty and singularity15 authored Dec 28, 2020
1 parent e287a78 commit 4b6bde3
Show file tree
Hide file tree
Showing 15 changed files with 292 additions and 156 deletions.
20 changes: 19 additions & 1 deletion config/jest/setup.js
Original file line number Diff line number Diff line change
@@ -1 +1,19 @@
// Add your setup here
import "jest";

expect.extend({
toEqualX(actual, msgAndValueObj) {
const name = msgAndValueObj.name;
const value = msgAndValueObj.value;
if (actual === value) {
return {
message: () => `${name} equals "${value}"`,
pass: true
};
} else {
return {
message: () => `${name} should equal "${value}" but received "${actual}"`,
pass: false
};
}
}
});
126 changes: 4 additions & 122 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "atoll",
"version": "0.33.0",
"version": "0.34.0",
"author": {
"name": "Kevin Berry",
"email": "[email protected]"
Expand All @@ -17,7 +17,7 @@
"build-deploy": "node ./scripts/build-deploy.js",
"build:dev": "npm run build-only:dev && npm run build-deploy",
"build-only:dev": "npx --no-install cross-env NODE_ENV=development node scripts/build.js",
"depgraph": "npm run build:depgraph && open-cli dependency-graph.svg",
"depgraph": "npm run build:depgraph && npx --no-install opener dependency-graph.svg",
"build:depgraph": "depcruise -c .dependency-cruiser.js --exclude '^node_modules' --output-type dot src | dot -T svg > dependency-graph.svg",
"clean": "npx rimraf --no-install build",
"clean:transpiled": "npx rimraf --no-install build/transpiled",
Expand All @@ -36,7 +36,7 @@
"test:tsc": "tsc --noEmit --project tsconfig.json",
"test:jest": "npx --no-install cross-env CI=true node scripts/test.js --env=jsdom --coverage",
"test:update": "npx --no-install cross-env CI=true npm test --updateSnapshot",
"test:report": "open-cli ./coverage/lcov-report/index.html",
"test:report": "npx --no-install opener ./coverage/lcov-report/index.html",
"start:analyzer": "webpack-bundle-analyzer build/client/static/bundle-stats.json",
"transpile": "babel -d build/transpiled ./src --extensions .es6,.js,.es,.jsx,.mjs,.ts,.tsx --ignore **/*.d.ts",
"lint:css": "stylelint src/**/*.css",
Expand All @@ -57,7 +57,7 @@
"setup": "ts-node ./scripts/setup.ts"
},
"dependencies": {
"@atoll/shared": "0.33.0",
"@atoll/shared": "0.34.0",
"@flopflip/memory-adapter": "1.6.0",
"@flopflip/react-broadcast": "10.1.11",
"axios": "0.19.2",
Expand Down Expand Up @@ -144,7 +144,6 @@
"launchdarkly-js-client-sdk": "2.17.0",
"mini-css-extract-plugin": "0.9.0",
"nodemon": "2.0.2",
"open-cli": "6.0.1",
"postcss-assets": "5.0.0",
"postcss-custom-properties": "9.1.1",
"postcss-flexbugs-fixes": "4.2.1",
Expand Down
14 changes: 14 additions & 0 deletions src/custom.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export {};

interface MsgAndValueObj {
name: string;
value: any;
}

declare global {
namespace jest {
interface Matchers<R> {
toEqualX(msgAndValueObj: MsgAndValueObj): R;
}
}
}
5 changes: 5 additions & 0 deletions src/database/model/upgrade.sql
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,8 @@ alter table sprint
alter column archived set not null;

alter table backlogitem add column "acceptanceCriteria" text;

alter table backlogitem add column "startedAt" timestamp with time zone;
alter table backlogitem add column "finishedAt" timestamp with time zone;
alter table backlogitem add column "acceptedAt" timestamp with time zone;
alter table backlogitem add column "releasedAt" timestamp with time zone;
29 changes: 15 additions & 14 deletions src/server/api/handlers/backlogItems.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@ import { CreateOptions, Transaction } from "sequelize";

// libraries
import {
getValidStatuses,
isValidStatus,
ApiBacklogItem,
ApiBacklogItemRank,
logger,
ApiSprintStats,
getValidStatuses,
isValidStatus,
logger,
mapApiItemToBacklogItem
} from "@atoll/shared";

Expand Down Expand Up @@ -43,6 +43,7 @@ import {
} from "../../dataaccess/mappers/dataAccessToApiMappers";
import { handleSprintStatUpdate } from "./updaters/sprintStatUpdater";
import { getIdForSprintContainingBacklogItem } from "./fetchers/sprintFetcher";
import { getUpdatedDataItemWhenStatusChanges } from "../utils/statusChangeUtils";

export const backlogItemsGetHandler = async (req: Request, res: Response) => {
const params = getParamsFromRequest(req);
Expand Down Expand Up @@ -242,9 +243,10 @@ export const backlogItemsPostHandler = async (req: Request, res: Response) => {
transaction = await sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE });
await sequelize.query('SET CONSTRAINTS "backlogitemrank_backlogitemId_fkey" DEFERRED;', { transaction });
await sequelize.query('SET CONSTRAINTS "backlogitemrank_nextbacklogitemId_fkey" DEFERRED;', { transaction });
const addedBacklogItem = await BacklogItemModel.create(bodyWithId, { transaction } as CreateOptions);
const newItem = getUpdatedDataItemWhenStatusChanges(null, bodyWithId);
const addedBacklogItem = await BacklogItemModel.create(newItem, { transaction } as CreateOptions);
if (!prevBacklogItemId) {
await backlogItemRankFirstItemInserter(bodyWithId, transaction);
await backlogItemRankFirstItemInserter(newItem, transaction);
} else {
// 1. if there is a single item in database then we'll have this entry:
// backlogitemId=null, nextbacklogitemId=item1
Expand All @@ -270,14 +272,14 @@ export const backlogItemsPostHandler = async (req: Request, res: Response) => {
const prevBacklogItem = prevBacklogItems[0];
// (2) oldNextItemId = prevBacklogItem.nextbacklogitemId
const oldNextItemId = ((prevBacklogItem as unknown) as ApiBacklogItemRank).nextbacklogitemId;
// (3) update existing entry with nextbacklogitemId = bodyWithId.id
await prevBacklogItem.update({ nextbacklogitemId: bodyWithId.id }, { transaction });
// (4) add new row with backlogitemId = bodyWithId.id, nextbacklogitemId = oldNextItemId
// (3) update existing entry with nextbacklogitemId = newItem.id
await prevBacklogItem.update({ nextbacklogitemId: newItem.id }, { transaction });
// (4) add new row with backlogitemId = newItem.id, nextbacklogitemId = oldNextItemId
await BacklogItemRankModel.create(
{
...addIdToBody({
projectId: bodyWithId.projectId,
backlogitemId: bodyWithId.id,
projectId: newItem.projectId,
backlogitemId: newItem.id,
nextbacklogitemId: oldNextItemId
})
},
Expand Down Expand Up @@ -332,7 +334,6 @@ export const backlogItemPutHandler = async (req: Request, res: Response) => {
);
return;
}
let sprintStats: ApiSprintStats;
let transaction: Transaction;
try {
transaction = await sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE });
Expand All @@ -344,7 +345,7 @@ export const backlogItemPutHandler = async (req: Request, res: Response) => {
respondWithNotFound(res, `Unable to find backlogitem to update with ID ${req.body.id}`);
} else {
const originalApiBacklogItem = mapDbToApiBacklogItem(backlogItem);
const newDataItem = req.body;
const newDataItem = getUpdatedDataItemWhenStatusChanges(originalApiBacklogItem, req.body);
await backlogItem.update(newDataItem, { transaction });

await handleResponseWithUpdatedStats(newDataItem, originalApiBacklogItem, backlogItem, res, transaction);
Expand Down Expand Up @@ -379,7 +380,6 @@ export const backlogItemPatchHandler = async (req: Request, res: Response) => {
if (respondedWithMismatchedItemIds(res, queryParamItemId, bodyItemId)) {
return;
}
let sprintStats: ApiSprintStats;
let transaction: Transaction;
try {
transaction = await sequelize.transaction({ isolationLevel: Transaction.ISOLATION_LEVELS.SERIALIZABLE });
Expand All @@ -395,7 +395,8 @@ export const backlogItemPatchHandler = async (req: Request, res: Response) => {
if (invalidPatchMessage) {
respondWithFailedValidation(res, `Unable to patch: ${invalidPatchMessage}`);
} else {
const newDataItem = getPatchedItem(originalApiBacklogItem, req.body);
let newDataItem = getPatchedItem(originalApiBacklogItem, req.body);
newDataItem = getUpdatedDataItemWhenStatusChanges(originalApiBacklogItem, newDataItem);
await backlogItem.update(newDataItem, { transaction });

await handleResponseWithUpdatedStats(newDataItem, originalApiBacklogItem, backlogItem, res, transaction);
Expand Down
4 changes: 2 additions & 2 deletions src/server/api/handlers/updaters/sprintStatUpdater.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ import {
} from "@atoll/shared";

// data access
import { ApiToDataAccessMapOptions, BacklogItemModel, mapApiToDbSprint, SprintModel } from "../../../dataaccess";
import { ApiToDataAccessMapOptions, mapApiToDbSprint, SprintModel } from "../../../dataaccess";

// utils
import { mapDbToApiBacklogItem, mapDbToApiSprint } from "../../../dataaccess/mappers/dataAccessToApiMappers";
import { mapDbToApiSprint } from "../../../dataaccess/mappers/dataAccessToApiMappers";

enum Operation {
None = 0,
Expand Down
Loading

0 comments on commit 4b6bde3

Please sign in to comment.