Skip to content

Commit

Permalink
sdk-request: 令现有测试跑起来
Browse files Browse the repository at this point in the history
  • Loading branch information
chuan6 committed Aug 15, 2017
1 parent 518a8f7 commit 3c29938
Show file tree
Hide file tree
Showing 13 changed files with 248 additions and 131 deletions.
7 changes: 7 additions & 0 deletions packages/teambition-sdk-request/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@
"name": "teambition-sdk-request",
"version": "0.8.13",
"description": "Front-End SDK (Request) for Teambition",
"main": "./dist/cjs/index.js",
"typings": "./dist/cjs/index.d.ts",
"scripts": {
"build_cjs": "rm -rf dist/cjs && tsc src/index.ts -m commonjs --outDir dist/cjs --sourcemap --inlineSources --target ES5 -d --diagnostics --pretty --strict --noUnusedLocals --noUnusedParameters --experimentalDecorators --suppressImplicitAnyIndexErrors --moduleResolution node --importHelpers --noEmitHelpers --lib es5,es2015.iterable,es2015.collection,es2015.promise,es2015.core,dom",
"build_test": "rm -rf spec-js && tsc test/app.ts -m commonjs --sourcemap --inlineSources --outDir spec-js --target ES2015 --diagnostics --pretty --experimentalDecorators --suppressImplicitAnyIndexErrors --types \"node,chai,sinon,sinon-chai\" --moduleResolution node",
"cover": "npm run build_test && rm -rf ./coverage && nyc --reporter=html --reporter=lcov --exclude=node_modules --exclude=spec-js/test --exclude=spec-js/mock --exclude=spec-js/src/sockets/SocketClient.js tman --mocha spec-js/test/app.js",
"lint": "tslint ./src/**/*.ts ./mock/**/*.ts ./test/*.ts ./test/apis/**/*.ts ./test/mock/**/*.ts ./test/utils/**/*.ts",
"test": "npm run lint && npm run cover"
},
"repository": {
"type": "git",
Expand Down
217 changes: 217 additions & 0 deletions packages/teambition-sdk-request/src/event/EventGenerator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import { EventSchema, Utils } from 'teambition-sdk-core'
import { isRecurrence } from './utils'
import { EventId } from 'teambition-types'

const { rrulestr } = require('rrule')

type Timeframe = { startDate: Date, endDate: Date }

export class EventGenerator implements IterableIterator<EventSchema | undefined> {
type: 'event' = 'event'
_id: EventId

private done: boolean
private rrule: any
private startDateCursor: Date
private isRecurrence = isRecurrence(this.event)
private duration: number

[Symbol.iterator] = () => this

constructor(private event: EventSchema) {
this._id = event._id
this.done = false

const startDateObj = new Date(event.startDate)
const endDateObj = new Date(event.endDate)
this.duration = endDateObj.valueOf() - startDateObj.valueOf()

if (this.isRecurrence) {
this.startDateCursor = startDateObj
this.rrule = rrulestr(this.event.recurrence.join('\n'), { forceset: true })
}
}

// 从给予的 startDate 和 endDate 生成一个 EventSchema 对象;
// 当用于普通日程,不需要提供参数。
private makeEvent(timeframe?: Timeframe): EventSchema {
const target = Utils.clone(this.event)

if (!this.isRecurrence || !timeframe) {
return target
}
// this.isRecurrence && timeframe

const timestamp = timeframe.startDate.valueOf()
target._id = `${target._id}_${timestamp}`
target.startDate = timeframe.startDate.toISOString()
target.endDate = timeframe.endDate.toISOString()

return target
}

private getOneTimeframeFromRecurrence(
unadjustedStartDate: Date,
include: boolean = true
): Timeframe | null {
// unadjustedStartDate 可能未经 this.rrule.after 过滤,有可能是
// 一个 exdate(被 rruleset 剔除的日期),发现时需要跳过。
const startDate = this.rrule.after(unadjustedStartDate, include)
if (startDate) {
const endDate = this.computeEndDate(startDate)
return { startDate, endDate }
} else {
return null
}
}

private computeEndDate(startDate: Date): Date {
return new Date(startDate.valueOf() + this.duration)
}

private slice(
from: Date, fromCmpOption: 'byStartDate' | 'byEndDate',
to: Date, toCmpOption: 'byStartDate' | 'byEndDate'
): Timeframe[] {
const skipPred = (eSpan: Timeframe): boolean =>
fromCmpOption === 'byStartDate' && eSpan.startDate < from
|| fromCmpOption === 'byEndDate' && eSpan.endDate < from

const stopPred = (eSpan: Timeframe): boolean =>
toCmpOption === 'byStartDate' && eSpan.startDate > to
|| toCmpOption === 'byEndDate' && eSpan.endDate > to

const result: Timeframe[] = []
let initialEventSpan: Timeframe | null

if (!this.isRecurrence) {
initialEventSpan = {
startDate: new Date(this.event.startDate),
endDate: new Date(this.event.endDate)
}
if (!skipPred(initialEventSpan) && !stopPred(initialEventSpan)) {
// eventSpan 在时间范围内
result.push(initialEventSpan)
}
return result
}
// this.isRecurrence is truthy

initialEventSpan = this.getOneTimeframeFromRecurrence(new Date(this.event.startDate))
if (!initialEventSpan) {
return []
}

let curr: Timeframe | null
for (
curr = initialEventSpan;
curr !== null;
curr = this.getOneTimeframeFromRecurrence(curr.startDate, false)
) {
if (stopPred(curr)) { // 优先检查停止条件
break
}
if (skipPred(curr)) { // 其次检查忽略条件
continue
}

result.push(curr)
}

return result
}

next(): IteratorResult<EventSchema | undefined> {
const doneRet = { value: undefined, done: true }

if (this.done) {
return doneRet
}

if (!this.isRecurrence) {
this.done = true
return { value: this.makeEvent(), done: false }
}

if (!this.startDateCursor) {
this.done = true
return doneRet
}

const eventSpan = this.getOneTimeframeFromRecurrence(this.startDateCursor)
if (!eventSpan) {
this.done = true
return doneRet
}

const result = {
value: this.makeEvent(eventSpan),
done: false
}
this.startDateCursor = this.rrule.after(eventSpan.startDate)
return result
}

takeUntil(startDateUntil: Date, endDateUntil?: Date) {
return this.takeFrom(
new Date(this.event.startDate),
startDateUntil,
endDateUntil
)
}

takeFrom(fromDate: Date, startDateTo: Date, endDateTo?: Date) {
const toDate = !endDateTo ? startDateTo : new Date(
Math.min(
startDateTo.valueOf(),
endDateTo.valueOf() - this.duration
)
)
return this.slice(
fromDate, 'byEndDate',
toDate, 'byStartDate'
).map((eventSpan) => this.makeEvent(eventSpan))
}

after(date: Date): EventSchema | null {
if (!this.isRecurrence) {
if (new Date(this.event.startDate) < date) {
return null
} else {
return this.event
}
}
// this.isRecurrence is truthy
const targetEventSpan = this.getOneTimeframeFromRecurrence(date)
if (!targetEventSpan) {
return null
} else {
return this.makeEvent(targetEventSpan)
}
}

findByEventId(eventId: EventId): EventSchema | null {
if (!this.isRecurrence) {
return eventId === this.event._id ? this.makeEvent() : null
}

const [id, timestampStr] = eventId.split('_', 2)
if (id !== this.event._id) {
return null
}

// 不使用 parseInt 因为不应该兼容前缀正确的错误 timestamp
const timestamp = Number(timestampStr)
const expectedDate = new Date(timestamp)
if (isNaN(timestamp) || isNaN(expectedDate.valueOf())) {
return null
}
// expectedDate is a valid Date object

const targetEventSpan = this.getOneTimeframeFromRecurrence(expectedDate)
if (!targetEventSpan || targetEventSpan.startDate.valueOf() !== expectedDate.valueOf()) {
return null
}
return this.makeEvent(targetEventSpan)
}
}
4 changes: 3 additions & 1 deletion packages/teambition-sdk-request/src/event/get.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import 'rxjs/add/operator/toArray'
import { Observable } from 'rxjs/Observable'
import { QueryToken } from 'reactivedb'
import { CacheStrategy, SDK, SDKFetch, EventSchema, EventGenerator } from 'teambition-sdk-core'
import { CacheStrategy, SDK, SDKFetch, EventSchema } from 'teambition-sdk-core'
import { EventId } from 'teambition-types'

import { EventGenerator } from './EventGenerator'

export function getEventFetch(
this: SDKFetch,
eventId: EventId,
Expand Down
2 changes: 2 additions & 0 deletions packages/teambition-sdk-request/src/event/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
import './get'

export { EventGenerator } from './EventGenerator'
3 changes: 3 additions & 0 deletions packages/teambition-sdk-request/src/event/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { EventSchema } from 'teambition-sdk-core'

export const isRecurrence = (event: EventSchema) => event.recurrence && event.recurrence.length
4 changes: 3 additions & 1 deletion packages/teambition-sdk-request/src/my/recent.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { Observable } from 'rxjs/Observable'
import { QueryToken } from 'reactivedb'
import { CacheStrategy, EventGenerator, SDK, SDKFetch, TaskSchema, EventSchema, SubtaskSchema, Utils } from 'teambition-sdk-core'
import { CacheStrategy, SDK, SDKFetch, TaskSchema, EventSchema, SubtaskSchema, Utils } from 'teambition-sdk-core'
import { UserId } from 'teambition-types'

import { EventGenerator } from '../event'

export interface RecentTaskData extends TaskSchema {
type: 'task'
}
Expand Down
16 changes: 8 additions & 8 deletions packages/teambition-sdk-request/test/apis/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import './event.spec'
import './EventGenerator.spec'
import './like.spec'
import './my.spec'
import './post.spec'
import './task.spec'
import './user.spec'
import './file.spec'
// import './event.spec'
// import './EventGenerator.spec'
// import './like.spec'
// import './my.spec'
// import './post.spec'
// import './task.spec'
// import './user.spec'
// import './file.spec'
import './project.spec'
import './search.spec'
import './organization.spec'
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, before, beforeEach, it, afterEach, after } from 'tman'
import { expect } from 'chai'
import { Scheduler } from 'rxjs'
import { SDKFetch } from '../'
import { SDKFetch } from 'teambition-sdk-core'
import {
getAllOrganizationProjects,
getJoinedOrganizationProjects,
Expand Down
2 changes: 1 addition & 1 deletion packages/teambition-sdk-request/test/apis/project.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Scheduler } from 'rxjs'
import {
GetPersonalProjectsQueryParams
} from '../../src/project/personal'
import { SDKFetch } from '../'
import { SDKFetch } from 'teambition-sdk-core'

const fetchMock = require('fetch-mock')

Expand Down
2 changes: 1 addition & 1 deletion packages/teambition-sdk-request/test/apis/search.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
ScopeType,
buildPath as buildPathForMemberSearching
} from '../../src/search/members'
import { SDKFetch } from '../'
import { SDKFetch } from 'teambition-sdk-core'

const fetchMock = require('fetch-mock')

Expand Down
8 changes: 2 additions & 6 deletions packages/teambition-sdk-request/test/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@ import 'isomorphic-fetch'
process.env.NODE_ENV = 'production'

export { run, setExit, reset, mocha } from 'tman'
export * from './utils/utils'
export * from './utils/httpErrorSpec'

export * from './mock/MockSpec'

import './apis'
import './sockets'
import './net'

import '../src/index'
29 changes: 0 additions & 29 deletions packages/teambition-sdk-request/test/index.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1 @@
'use strict'
import { Database, DataStoreType } from 'reactivedb'
import { testable } from '../src/testable'
import { SDK } from '../src/index'

import './SDKFetch.spec'

testable.UseXMLHTTPRequest = false

export function createSdk() {
const sdk = new SDK()

const database = new Database(DataStoreType.MEMORY, false, 'teambition-sdk', 1)
sdk.initReactiveDB(database)

return sdk
}

export function createSdkWithoutRDB() {
return new SDK()
}

export function loadRDB(sdk: SDK) {
const database = new Database(DataStoreType.MEMORY, false, `teambition-sdk-test`, 1)
return sdk.initReactiveDB(database)
}

export * from '../src/index'
export * from '../src/utils/index'
export * from '../mock/index'
Loading

0 comments on commit 3c29938

Please sign in to comment.