mirror of
https://github.com/standardnotes/server
synced 2026-01-26 23:01:10 -05:00
Compare commits
39 Commits
@standardn
...
@standardn
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae56126585 | ||
|
|
6dcf0ac124 | ||
|
|
63e2ce43c2 | ||
|
|
f27aa21eb5 | ||
|
|
42926c663b | ||
|
|
d38116183c | ||
|
|
9ca373e208 | ||
|
|
4084f2f5ec | ||
|
|
684ffbadbc | ||
|
|
1c4d4c57de | ||
|
|
d83111a199 | ||
|
|
f10fa839fb | ||
|
|
1f20395ff3 | ||
|
|
bfe6f4255a | ||
|
|
b9032f3012 | ||
|
|
ce53c459e6 | ||
|
|
6df42fb0d5 | ||
|
|
1e2b496f4f | ||
|
|
528c1b0d57 | ||
|
|
22fba8ba80 | ||
|
|
6f26261ebe | ||
|
|
4b1fe3ba91 | ||
|
|
9f95262bd4 | ||
|
|
2ec28e541e | ||
|
|
4764d4b19a | ||
|
|
9b27547dae | ||
|
|
a96f2c9153 | ||
|
|
225e0aaf88 | ||
|
|
f0c85910bc | ||
|
|
124c443528 | ||
|
|
37c7f8d39f | ||
|
|
c419f1ce22 | ||
|
|
4949cdfe2f | ||
|
|
cd101b96ea | ||
|
|
40d0e4631f | ||
|
|
a55a995660 | ||
|
|
1d576d48ad | ||
|
|
4ff8030f87 | ||
|
|
c15e2e2c8f |
4
.pnp.cjs
generated
4
.pnp.cjs
generated
@@ -2737,10 +2737,6 @@ const RAW_RUNTIME_STATE =
|
||||
"packageLocation": "./packages/domain-core/",\
|
||||
"packageDependencies": [\
|
||||
["@standardnotes/domain-core", "workspace:packages/domain-core"],\
|
||||
["@standardnotes/common", "workspace:packages/common"],\
|
||||
["@standardnotes/features", "npm:1.53.1"],\
|
||||
["@standardnotes/predicates", "workspace:packages/predicates"],\
|
||||
["@standardnotes/security", "workspace:packages/security"],\
|
||||
["@types/jest", "npm:29.1.1"],\
|
||||
["@types/uuid", "npm:8.3.4"],\
|
||||
["@typescript-eslint/eslint-plugin", "virtual:fd909b174d079e30b336c4ce72c38a88c1e447767b1a8dd7655e07719a1e31b97807f0931368724fc78897ff15e6a6d00b83316c0f76d11f85111f342e08bb79#npm:5.30.5"],\
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [2.12.27](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.26...@standardnotes/analytics@2.12.27) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.12.26](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.25...@standardnotes/analytics@2.12.26) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
## [2.12.25](https://github.com/standardnotes/server/compare/@standardnotes/analytics@2.12.24...@standardnotes/analytics@2.12.25) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/analytics
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/analytics",
|
||||
"version": "2.12.25",
|
||||
"version": "2.12.27",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,20 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.67.3](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.67.2...@standardnotes/auth-server@1.67.3) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.67.2](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.67.1...@standardnotes/auth-server@1.67.2) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/auth-server
|
||||
|
||||
## [1.67.1](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.67.0...@standardnotes/auth-server@1.67.1) (2022-12-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* user signed in email template ([c15e2e2](https://github.com/standardnotes/server/commit/c15e2e2c8f3a6c177e227d25440501fa38dd3d0e))
|
||||
|
||||
# [1.67.0](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.66.9...@standardnotes/auth-server@1.67.0) (2022-12-12)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/auth-server",
|
||||
"version": "1.67.0",
|
||||
"version": "1.67.3",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -20,6 +20,5 @@ export const html = (email: string, device: string, browser: string, timeAndDate
|
||||
<br />
|
||||
SN
|
||||
</p>
|
||||
<a href="https://app.standardnotes.com/?settings=account">Mute these emails</a>
|
||||
</div>
|
||||
`
|
||||
|
||||
@@ -3,6 +3,18 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
# [1.11.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.10.0...@standardnotes/domain-core@1.11.0) (2022-12-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add legacy session model ([4084f2f](https://github.com/standardnotes/server/commit/4084f2f5ecf8379ff69d619d3d12495c010c3980))
|
||||
|
||||
# [1.10.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.9.0...@standardnotes/domain-core@1.10.0) (2022-12-15)
|
||||
|
||||
### Features
|
||||
|
||||
* **domain-core:** add session model ([1c4d4c5](https://github.com/standardnotes/server/commit/1c4d4c57dea1187dc130f1ae8b7dc8ede332320f))
|
||||
|
||||
# [1.9.0](https://github.com/standardnotes/server/compare/@standardnotes/domain-core@1.8.0...@standardnotes/domain-core@1.9.0) (2022-12-07)
|
||||
|
||||
### Features
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/domain-core",
|
||||
"version": "1.9.0",
|
||||
"version": "1.11.0",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
@@ -23,10 +23,6 @@
|
||||
"test": "jest spec --coverage --passWithNoTests"
|
||||
},
|
||||
"dependencies": {
|
||||
"@standardnotes/common": "workspace:*",
|
||||
"@standardnotes/features": "^1.52.1",
|
||||
"@standardnotes/predicates": "workspace:*",
|
||||
"@standardnotes/security": "workspace:*",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"shallow-equal-object": "^1.1.1",
|
||||
"uuid": "^9.0.0"
|
||||
|
||||
16
packages/domain-core/src/Domain/Auth/LegacySession.spec.ts
Normal file
16
packages/domain-core/src/Domain/Auth/LegacySession.spec.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import { LegacySession } from './LegacySession'
|
||||
|
||||
describe('LegacySession', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = LegacySession.create('foobar')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().accessToken).toEqual('foobar')
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
const valueOrError = LegacySession.create('')
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
22
packages/domain-core/src/Domain/Auth/LegacySession.ts
Normal file
22
packages/domain-core/src/Domain/Auth/LegacySession.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { LegacySessionProps } from './LegacySessionProps'
|
||||
import { Validator } from '../Core/Validator'
|
||||
|
||||
export class LegacySession extends ValueObject<LegacySessionProps> {
|
||||
get accessToken(): string {
|
||||
return this.props.token
|
||||
}
|
||||
|
||||
private constructor(props: LegacySessionProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(token: string): Result<LegacySession> {
|
||||
if (Validator.isNotEmpty(token).isFailed()) {
|
||||
return Result.fail<LegacySession>('Could not create legacy session. Token value is empty')
|
||||
}
|
||||
|
||||
return Result.ok<LegacySession>(new LegacySession({ token }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export interface LegacySessionProps {
|
||||
token: string
|
||||
}
|
||||
26
packages/domain-core/src/Domain/Auth/Session.spec.ts
Normal file
26
packages/domain-core/src/Domain/Auth/Session.spec.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { Session } from './Session'
|
||||
import { SessionToken } from './SessionToken'
|
||||
|
||||
describe('Session', () => {
|
||||
it('should create a session value object', () => {
|
||||
const accessToken = SessionToken.create('foobar1', 1234567890).getValue()
|
||||
const refreshToken = SessionToken.create('foobar2', 1234567890).getValue()
|
||||
|
||||
const valueOrError = Session.create(accessToken, refreshToken)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().accessToken.value).toEqual('foobar1')
|
||||
expect(valueOrError.getValue().refreshToken.value).toEqual('foobar2')
|
||||
expect(valueOrError.getValue().isReadOnly()).toEqual(false)
|
||||
})
|
||||
|
||||
it('should create a session reado-only value object', () => {
|
||||
const accessToken = SessionToken.create('foobar', 1234567890).getValue()
|
||||
const refreshToken = SessionToken.create('foobar', 1234567890).getValue()
|
||||
|
||||
const valueOrError = Session.create(accessToken, refreshToken, true)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().isReadOnly()).toEqual(true)
|
||||
})
|
||||
})
|
||||
26
packages/domain-core/src/Domain/Auth/Session.ts
Normal file
26
packages/domain-core/src/Domain/Auth/Session.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { SessionProps } from './SessionProps'
|
||||
import { SessionToken } from './SessionToken'
|
||||
|
||||
export class Session extends ValueObject<SessionProps> {
|
||||
get accessToken(): SessionToken {
|
||||
return this.props.accessToken
|
||||
}
|
||||
|
||||
get refreshToken(): SessionToken {
|
||||
return this.props.refreshToken
|
||||
}
|
||||
|
||||
isReadOnly(): boolean {
|
||||
return this.props.readonlyAccess || false
|
||||
}
|
||||
|
||||
private constructor(props: SessionProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(accessToken: SessionToken, refreshToken: SessionToken, readonlyAccess?: boolean): Result<Session> {
|
||||
return Result.ok<Session>(new Session({ accessToken, refreshToken, readonlyAccess }))
|
||||
}
|
||||
}
|
||||
7
packages/domain-core/src/Domain/Auth/SessionProps.ts
Normal file
7
packages/domain-core/src/Domain/Auth/SessionProps.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { SessionToken } from './SessionToken'
|
||||
|
||||
export interface SessionProps {
|
||||
accessToken: SessionToken
|
||||
refreshToken: SessionToken
|
||||
readonlyAccess?: boolean
|
||||
}
|
||||
21
packages/domain-core/src/Domain/Auth/SessionToken.spec.ts
Normal file
21
packages/domain-core/src/Domain/Auth/SessionToken.spec.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { SessionToken } from './SessionToken'
|
||||
|
||||
describe('SessionToken', () => {
|
||||
it('should create a value object', () => {
|
||||
const valueOrError = SessionToken.create('foobar', 1234567890)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeFalsy()
|
||||
expect(valueOrError.getValue().value).toEqual('foobar')
|
||||
expect(valueOrError.getValue().expiresAt).toEqual(1234567890)
|
||||
})
|
||||
|
||||
it('should not create an invalid value object', () => {
|
||||
let valueOrError = SessionToken.create('', 1234567890)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
|
||||
valueOrError = SessionToken.create('foobar', undefined as unknown as number)
|
||||
|
||||
expect(valueOrError.isFailed()).toBeTruthy()
|
||||
})
|
||||
})
|
||||
29
packages/domain-core/src/Domain/Auth/SessionToken.ts
Normal file
29
packages/domain-core/src/Domain/Auth/SessionToken.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { ValueObject } from '../Core/ValueObject'
|
||||
import { Result } from '../Core/Result'
|
||||
import { SessionTokenProps } from './SessionTokenProps'
|
||||
import { Validator } from '../Core/Validator'
|
||||
|
||||
export class SessionToken extends ValueObject<SessionTokenProps> {
|
||||
get value(): string {
|
||||
return this.props.value
|
||||
}
|
||||
|
||||
get expiresAt(): number {
|
||||
return this.props.expiresAt
|
||||
}
|
||||
|
||||
private constructor(props: SessionTokenProps) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
static create(value: string, expiresAt: number): Result<SessionToken> {
|
||||
if (Validator.isNotEmpty(value).isFailed()) {
|
||||
return Result.fail<SessionToken>('Could not create session token. Token value is empty')
|
||||
}
|
||||
if (Validator.isNotEmpty(expiresAt).isFailed()) {
|
||||
return Result.fail<SessionToken>('Could not create session token. Token expiration is empty')
|
||||
}
|
||||
|
||||
return Result.ok<SessionToken>(new SessionToken({ value, expiresAt }))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface SessionTokenProps {
|
||||
value: string
|
||||
expiresAt: number
|
||||
}
|
||||
@@ -1,3 +1,10 @@
|
||||
export * from './Auth/LegacySession'
|
||||
export * from './Auth/LegacySessionProps'
|
||||
export * from './Auth/Session'
|
||||
export * from './Auth/SessionProps'
|
||||
export * from './Auth/SessionToken'
|
||||
export * from './Auth/SessionTokenProps'
|
||||
|
||||
export * from './Common/Dates'
|
||||
export * from './Common/DatesProps'
|
||||
export * from './Common/Email'
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.9.28](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.9.27...@standardnotes/revisions-server@1.9.28) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.9.27](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.9.26...@standardnotes/revisions-server@1.9.27) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/revisions-server
|
||||
|
||||
## [1.9.26](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.9.25...@standardnotes/revisions-server@1.9.26) (2022-12-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/revisions-server",
|
||||
"version": "1.9.26",
|
||||
"version": "1.9.28",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.15.8](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.15.7...@standardnotes/scheduler-server@1.15.8) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.15.7](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.15.6...@standardnotes/scheduler-server@1.15.7) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
## [1.15.6](https://github.com/standardnotes/server/compare/@standardnotes/scheduler-server@1.15.5...@standardnotes/scheduler-server@1.15.6) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/scheduler-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/scheduler-server",
|
||||
"version": "1.15.6",
|
||||
"version": "1.15.8",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -3,6 +3,116 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.26.10](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.9...@standardnotes/syncing-server@1.26.10) (2022-12-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** item query created_at condition ([6dcf0ac](https://github.com/standardnotes/syncing-server-js/commit/6dcf0ac124252c441d08a67f2954441ab6266bdf))
|
||||
|
||||
## [1.26.9](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.8...@standardnotes/syncing-server@1.26.9) (2022-12-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** fetching items in raw mode ([f27aa21](https://github.com/standardnotes/syncing-server-js/commit/f27aa21eb52a73b748e3a555bf201c834fd34aad))
|
||||
|
||||
## [1.26.8](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.7...@standardnotes/syncing-server@1.26.8) (2022-12-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** select fields in query ([d381161](https://github.com/standardnotes/syncing-server-js/commit/d38116183c9f7b6fb6d13d9076571fef72be555c))
|
||||
|
||||
## [1.26.7](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.6...@standardnotes/syncing-server@1.26.7) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.26.6](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.5...@standardnotes/syncing-server@1.26.6) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
## [1.26.5](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.4...@standardnotes/syncing-server@1.26.5) (2022-12-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** revisions processing limit ([f10fa83](https://github.com/standardnotes/syncing-server-js/commit/f10fa839fbe1baec32fd234b41d8cd42fc50931a))
|
||||
|
||||
## [1.26.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.3...@standardnotes/syncing-server@1.26.4) (2022-12-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** user uuid field name ([bfe6f42](https://github.com/standardnotes/syncing-server-js/commit/bfe6f4255a2d3f6e7dfa5eab1509dd770d9bff18))
|
||||
|
||||
## [1.26.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.2...@standardnotes/syncing-server@1.26.3) (2022-12-15)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** select fields in query for revisions ([ce53c45](https://github.com/standardnotes/syncing-server-js/commit/ce53c459e6ad0d469fcd0ebd7bf4caeb0e1d9c9c))
|
||||
|
||||
## [1.26.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.1...@standardnotes/syncing-server@1.26.2) (2022-12-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** revisions procedure logs ([1e2b496](https://github.com/standardnotes/syncing-server-js/commit/1e2b496f4f87fd49ae8fba8ed9b76d3b6a2c31fa))
|
||||
|
||||
## [1.26.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.26.0...@standardnotes/syncing-server@1.26.1) (2022-12-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** revisions procedure logs ([22fba8b](https://github.com/standardnotes/syncing-server-js/commit/22fba8ba806115b0f4bb4b083ae8595a3f0010b0))
|
||||
|
||||
# [1.26.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.25.6...@standardnotes/syncing-server@1.26.0) (2022-12-14)
|
||||
|
||||
### Features
|
||||
|
||||
* **syncing-server:** change revisions procedure to pagination instead of streaming ([4b1fe3b](https://github.com/standardnotes/syncing-server-js/commit/4b1fe3ba91594858e15cbdfbc21062c428dd03b4))
|
||||
|
||||
## [1.25.6](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.25.5...@standardnotes/syncing-server@1.25.6) (2022-12-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** additional stream events handling on revisions procedure ([2ec28e5](https://github.com/standardnotes/syncing-server-js/commit/2ec28e541efa2bd9172431d45c5c1560692a912c))
|
||||
|
||||
## [1.25.5](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.25.4...@standardnotes/syncing-server@1.25.5) (2022-12-14)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** revisions procedure with env var defined ranges ([9b27547](https://github.com/standardnotes/syncing-server-js/commit/9b27547dae1e5d5e6d071a069803e2bf3f8acdda))
|
||||
|
||||
## [1.25.4](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.25.3...@standardnotes/syncing-server@1.25.4) (2022-12-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** logs on revisions procedure ([225e0aa](https://github.com/standardnotes/syncing-server-js/commit/225e0aaf88a396bf308c2e5eed0bb6e130cb2d64))
|
||||
|
||||
## [1.25.3](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.25.2...@standardnotes/syncing-server@1.25.3) (2022-12-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** revisions ownership procedure destructured ([124c443](https://github.com/standardnotes/syncing-server-js/commit/124c4435285c2c2e8d0ce8b47907ebd47af27576))
|
||||
|
||||
## [1.25.2](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.25.1...@standardnotes/syncing-server@1.25.2) (2022-12-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** change revisions migration to notes ([c419f1c](https://github.com/standardnotes/syncing-server-js/commit/c419f1ce220c27acabfc813a30b3edd6c4aadaa1))
|
||||
|
||||
## [1.25.1](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.25.0...@standardnotes/syncing-server@1.25.1) (2022-12-13)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** revisions procedure properties ([cd101b9](https://github.com/standardnotes/syncing-server-js/commit/cd101b96eae8969a4dd2387deb1d4e8679ead216))
|
||||
|
||||
# [1.25.0](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.24.7...@standardnotes/syncing-server@1.25.0) (2022-12-12)
|
||||
|
||||
### Features
|
||||
|
||||
* **syncing-server:** fix streaming items for revisions update ([a55a995](https://github.com/standardnotes/syncing-server-js/commit/a55a9956602bee7dbb0f93f058aceff7a2136ffd))
|
||||
|
||||
## [1.24.7](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.24.6...@standardnotes/syncing-server@1.24.7) (2022-12-12)
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **syncing-server:** revisions updating - select fields ([4ff8030](https://github.com/standardnotes/syncing-server-js/commit/4ff8030f8709ee18853c2e782cfc5d99c826f074))
|
||||
|
||||
## [1.24.6](https://github.com/standardnotes/syncing-server-js/compare/@standardnotes/syncing-server@1.24.5...@standardnotes/syncing-server@1.24.6) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/syncing-server
|
||||
|
||||
@@ -10,51 +10,82 @@ import { Env } from '../src/Bootstrap/Env'
|
||||
import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
|
||||
import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
|
||||
import { ItemRepositoryInterface } from '../src/Domain/Item/ItemRepositoryInterface'
|
||||
import { Stream } from 'stream'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
const fixRevisionsOwnership = async (
|
||||
year: number,
|
||||
month: number,
|
||||
revisionsProcessingLimit: number,
|
||||
itemRepository: ItemRepositoryInterface,
|
||||
domainEventFactory: DomainEventFactoryInterface,
|
||||
domainEventPublisher: DomainEventPublisherInterface,
|
||||
logger: Logger,
|
||||
): Promise<void> => {
|
||||
const stream = await itemRepository.streamAll({
|
||||
sortBy: 'updated_at_timestamp',
|
||||
const createdAfter = new Date(`${year}-${month}-1`)
|
||||
const createdBefore = new Date(`${month !== 12 ? year : year + 1}-${month !== 12 ? month + 1 : 1}-1`)
|
||||
|
||||
logger.info(`[${createdAfter.toISOString()} - ${createdBefore.toISOString()}] Processing items`)
|
||||
|
||||
const itemsCount = await itemRepository.countAll({
|
||||
createdBetween: [createdAfter, createdBefore],
|
||||
selectString: 'item.uuid as uuid, item.user_uuid as userUuid',
|
||||
contentType: [ContentType.Note, ContentType.File],
|
||||
sortOrder: 'ASC',
|
||||
createdBefore: new Date('2022-11-23'),
|
||||
selectFields: ['user_uuid', 'item_uuid'],
|
||||
sortBy: 'uuid',
|
||||
})
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
stream
|
||||
.pipe(
|
||||
new Stream.Transform({
|
||||
objectMode: true,
|
||||
transform: async (rawItemData, _encoding, callback) => {
|
||||
try {
|
||||
if (!rawItemData.item_user_uuid || !rawItemData.item_uuid) {
|
||||
logger.error('Could not process item %O', rawItemData)
|
||||
logger.info(
|
||||
`[${createdAfter.toISOString()} - ${createdBefore.toISOString()}] There are ${itemsCount} items to process.`,
|
||||
)
|
||||
|
||||
return callback()
|
||||
}
|
||||
const amountOfPages = Math.ceil(itemsCount / revisionsProcessingLimit)
|
||||
const tenPercentOfPages = Math.ceil(amountOfPages / 10)
|
||||
let itemsProcessedCounter = 0
|
||||
let itemsSkippedCounter = 0
|
||||
for (let page = 1; page <= amountOfPages; page++) {
|
||||
if (page % tenPercentOfPages === 0) {
|
||||
logger.info(
|
||||
`[${createdAfter.toISOString()} - ${createdBefore.toISOString()}] Processing page ${page}/${amountOfPages} of items.`,
|
||||
)
|
||||
logger.info(
|
||||
`[${createdAfter.toISOString()} - ${createdBefore.toISOString()}] Processed successfully/skipped items: ${itemsProcessedCounter}/${itemsSkippedCounter}.`,
|
||||
)
|
||||
}
|
||||
|
||||
await domainEventPublisher.publish(
|
||||
domainEventFactory.createRevisionsOwnershipUpdateRequestedEvent({
|
||||
userUuid: rawItemData.item_user_uuid,
|
||||
itemUuid: rawItemData.item_uuid,
|
||||
}),
|
||||
)
|
||||
} catch (error) {
|
||||
logger.error(`Could not process item ${rawItemData.item_uuid}: ${(error as Error).message}`)
|
||||
}
|
||||
const items = await itemRepository.findAllRaw<{ uuid: string; userUuid: string }>({
|
||||
createdBetween: [createdAfter, createdBefore],
|
||||
selectString: 'item.uuid as uuid, item.user_uuid as userUuid',
|
||||
contentType: [ContentType.Note, ContentType.File],
|
||||
offset: (page - 1) * revisionsProcessingLimit,
|
||||
limit: revisionsProcessingLimit,
|
||||
sortOrder: 'ASC',
|
||||
sortBy: 'uuid',
|
||||
})
|
||||
|
||||
callback()
|
||||
},
|
||||
if (items.length === 0) {
|
||||
logger.warn(
|
||||
`[${createdAfter.toISOString()} - ${createdBefore.toISOString()}] No items fetched for offset ${
|
||||
(page - 1) * revisionsProcessingLimit
|
||||
} and limit ${revisionsProcessingLimit}.`,
|
||||
)
|
||||
}
|
||||
|
||||
for (const item of items) {
|
||||
if (!item.userUuid || !item.uuid) {
|
||||
itemsSkippedCounter++
|
||||
continue
|
||||
}
|
||||
|
||||
await domainEventPublisher.publish(
|
||||
domainEventFactory.createRevisionsOwnershipUpdateRequestedEvent({
|
||||
userUuid: item.userUuid,
|
||||
itemUuid: item.uuid,
|
||||
}),
|
||||
)
|
||||
.on('finish', resolve)
|
||||
.on('error', reject)
|
||||
})
|
||||
|
||||
itemsProcessedCounter++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const container = new ContainerConfigLoader()
|
||||
@@ -70,7 +101,28 @@ void container.load().then((container) => {
|
||||
const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.DomainEventFactory)
|
||||
const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.DomainEventPublisher)
|
||||
|
||||
Promise.resolve(fixRevisionsOwnership(itemRepository, domainEventFactory, domainEventPublisher, logger))
|
||||
const years = env.get('REVISION_YEARS').split(',')
|
||||
const months = env.get('REVISION_MONTHS').split(',')
|
||||
const revisionsProcessingLimit = env.get('REVISIONS_PROCESSING_LIMIT')
|
||||
|
||||
const promises = []
|
||||
for (const year of years) {
|
||||
for (const month of months) {
|
||||
promises.push(
|
||||
fixRevisionsOwnership(
|
||||
+year,
|
||||
+month,
|
||||
+revisionsProcessingLimit,
|
||||
itemRepository,
|
||||
domainEventFactory,
|
||||
domainEventPublisher,
|
||||
logger,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Promise.all(promises)
|
||||
.then(() => {
|
||||
logger.info('revisions ownership fix complete.')
|
||||
|
||||
|
||||
@@ -5,33 +5,33 @@ COMMAND=$1 && shift 1
|
||||
|
||||
case "$COMMAND" in
|
||||
'start-local')
|
||||
echo "Starting Web in Local Mode..."
|
||||
echo "[Docker] Starting Web in Local Mode..."
|
||||
yarn workspace @standardnotes/syncing-server start:local
|
||||
;;
|
||||
|
||||
'start-web' )
|
||||
echo "Starting Web..."
|
||||
echo "[Docker] Starting Web..."
|
||||
yarn workspace @standardnotes/syncing-server start
|
||||
;;
|
||||
|
||||
'start-worker' )
|
||||
echo "Starting Worker..."
|
||||
echo "[Docker] Starting Worker..."
|
||||
yarn workspace @standardnotes/syncing-server worker
|
||||
;;
|
||||
|
||||
'content-size-recalculate' )
|
||||
echo "Starting Content Size Recalculation..."
|
||||
echo "[Docker] Starting Content Size Recalculation..."
|
||||
USER_UUID=$1 && shift 1
|
||||
yarn workspace @standardnotes/syncing-server content-size $USER_UUID
|
||||
;;
|
||||
|
||||
'revisions-ownership-fix' )
|
||||
echo "Starting Revisions Ownership Fixing..."
|
||||
echo "[Docker] Starting Revisions Ownership Fixing..."
|
||||
yarn workspace @standardnotes/syncing-server revisions-ownership
|
||||
;;
|
||||
|
||||
* )
|
||||
echo "Unknown command"
|
||||
echo "[Docker] Unknown command"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/syncing-server",
|
||||
"version": "1.24.6",
|
||||
"version": "1.26.10",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export type ItemQuery = {
|
||||
userUuid?: string
|
||||
sortBy: string
|
||||
sortOrder: 'ASC' | 'DESC'
|
||||
sortBy?: string
|
||||
sortOrder?: 'ASC' | 'DESC'
|
||||
uuids?: Array<string>
|
||||
lastSyncTime?: number
|
||||
syncTimeComparison?: '>' | '>='
|
||||
contentType?: string
|
||||
contentType?: string | string[]
|
||||
deleted?: boolean
|
||||
offset?: number
|
||||
limit?: number
|
||||
createdBefore?: Date
|
||||
selectFields?: string[]
|
||||
createdBetween?: Date[]
|
||||
selectString?: string
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { ExtendedIntegrityPayload } from './ExtendedIntegrityPayload'
|
||||
export interface ItemRepositoryInterface {
|
||||
deleteByUserUuid(userUuid: string): Promise<void>
|
||||
findAll(query: ItemQuery): Promise<Item[]>
|
||||
findAllRaw<T>(query: ItemQuery): Promise<T[]>
|
||||
streamAll(query: ItemQuery): Promise<ReadStream>
|
||||
countAll(query: ItemQuery): Promise<number>
|
||||
findContentSizeForComputingTransferLimit(
|
||||
|
||||
@@ -1,401 +0,0 @@
|
||||
import 'reflect-metadata'
|
||||
|
||||
import { Repository, SelectQueryBuilder } from 'typeorm'
|
||||
import { ContentType } from '@standardnotes/common'
|
||||
|
||||
import { Item } from '../../Domain/Item/Item'
|
||||
|
||||
import { MySQLItemRepository } from './MySQLItemRepository'
|
||||
import { TimerInterface } from '@standardnotes/time'
|
||||
import { ReadStream } from 'fs'
|
||||
|
||||
describe('MySQLItemRepository', () => {
|
||||
let queryBuilder: SelectQueryBuilder<Item>
|
||||
let ormRepository: Repository<Item>
|
||||
let item: Item
|
||||
let timer: TimerInterface
|
||||
|
||||
const createRepository = () => new MySQLItemRepository(ormRepository)
|
||||
|
||||
beforeEach(() => {
|
||||
queryBuilder = {} as jest.Mocked<SelectQueryBuilder<Item>>
|
||||
|
||||
item = {} as jest.Mocked<Item>
|
||||
timer = {} as jest.Mocked<TimerInterface>
|
||||
timer.getTimestampInMicroseconds = jest.fn(() => 1616161616161616)
|
||||
|
||||
ormRepository = {} as jest.Mocked<Repository<Item>>
|
||||
ormRepository.save = jest.fn()
|
||||
ormRepository.remove = jest.fn()
|
||||
ormRepository.createQueryBuilder = jest.fn().mockImplementation(() => queryBuilder)
|
||||
})
|
||||
|
||||
it('should save', async () => {
|
||||
await createRepository().save(item)
|
||||
|
||||
expect(ormRepository.save).toHaveBeenCalledWith(item)
|
||||
})
|
||||
|
||||
it('should remove', async () => {
|
||||
await createRepository().remove(item)
|
||||
|
||||
expect(ormRepository.remove).toHaveBeenCalledWith(item)
|
||||
})
|
||||
|
||||
it('should delete all items for a given user', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.delete = jest.fn().mockReturnThis()
|
||||
queryBuilder.from = jest.fn().mockReturnThis()
|
||||
queryBuilder.execute = jest.fn()
|
||||
|
||||
await createRepository().deleteByUserUuid('123')
|
||||
|
||||
expect(queryBuilder.delete).toHaveBeenCalled()
|
||||
|
||||
expect(queryBuilder.from).toHaveBeenCalledWith('items')
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('user_uuid = :userUuid', { userUuid: '123' })
|
||||
|
||||
expect(queryBuilder.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should find one item by uuid and user uuid', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.getOne = jest.fn().mockReturnValue(item)
|
||||
|
||||
const result = await createRepository().findByUuidAndUserUuid('1-2-3', '2-3-4')
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('item.uuid = :uuid AND item.user_uuid = :userUuid', {
|
||||
uuid: '1-2-3',
|
||||
userUuid: '2-3-4',
|
||||
})
|
||||
expect(result).toEqual(item)
|
||||
})
|
||||
|
||||
it('should find one item by uuid', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.getOne = jest.fn().mockReturnValue(item)
|
||||
|
||||
const result = await createRepository().findByUuid('1-2-3')
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('item.uuid = :uuid', {
|
||||
uuid: '1-2-3',
|
||||
})
|
||||
expect(result).toEqual(item)
|
||||
})
|
||||
|
||||
it('should find items by all query criteria filled in', async () => {
|
||||
queryBuilder.getMany = jest.fn().mockReturnValue([item])
|
||||
queryBuilder.where = jest.fn()
|
||||
queryBuilder.andWhere = jest.fn()
|
||||
queryBuilder.orderBy = jest.fn()
|
||||
queryBuilder.skip = jest.fn()
|
||||
queryBuilder.take = jest.fn()
|
||||
|
||||
const result = await createRepository().findAll({
|
||||
userUuid: '1-2-3',
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'DESC',
|
||||
deleted: false,
|
||||
contentType: ContentType.Note,
|
||||
lastSyncTime: 123,
|
||||
syncTimeComparison: '>=',
|
||||
uuids: ['2-3-4'],
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
})
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledTimes(1)
|
||||
expect(queryBuilder.andWhere).toHaveBeenCalledTimes(4)
|
||||
expect(queryBuilder.where).toHaveBeenNthCalledWith(1, 'item.user_uuid = :userUuid', { userUuid: '1-2-3' })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(1, 'item.uuid IN (:...uuids)', { uuids: ['2-3-4'] })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(2, 'item.deleted = :deleted', { deleted: false })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(3, 'item.content_type = :contentType', {
|
||||
contentType: 'Note',
|
||||
})
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(4, 'item.updated_at_timestamp >= :lastSyncTime', {
|
||||
lastSyncTime: 123,
|
||||
})
|
||||
expect(queryBuilder.skip).toHaveBeenCalledWith(1)
|
||||
expect(queryBuilder.take).toHaveBeenCalledWith(10)
|
||||
|
||||
expect(queryBuilder.orderBy).toHaveBeenCalledWith('item.updated_at_timestamp', 'DESC')
|
||||
|
||||
expect(result).toEqual([item])
|
||||
})
|
||||
|
||||
it('should stream items by all query criteria filled in', async () => {
|
||||
const stream = {} as jest.Mocked<ReadStream>
|
||||
queryBuilder.stream = jest.fn().mockReturnValue(stream)
|
||||
queryBuilder.where = jest.fn()
|
||||
queryBuilder.andWhere = jest.fn()
|
||||
queryBuilder.orderBy = jest.fn()
|
||||
queryBuilder.skip = jest.fn()
|
||||
queryBuilder.take = jest.fn()
|
||||
|
||||
const result = await createRepository().streamAll({
|
||||
userUuid: '1-2-3',
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'DESC',
|
||||
deleted: false,
|
||||
contentType: ContentType.Note,
|
||||
lastSyncTime: 123,
|
||||
syncTimeComparison: '>=',
|
||||
uuids: ['2-3-4'],
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
})
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledTimes(1)
|
||||
expect(queryBuilder.andWhere).toHaveBeenCalledTimes(4)
|
||||
expect(queryBuilder.where).toHaveBeenNthCalledWith(1, 'item.user_uuid = :userUuid', { userUuid: '1-2-3' })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(1, 'item.uuid IN (:...uuids)', { uuids: ['2-3-4'] })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(2, 'item.deleted = :deleted', { deleted: false })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(3, 'item.content_type = :contentType', {
|
||||
contentType: 'Note',
|
||||
})
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(4, 'item.updated_at_timestamp >= :lastSyncTime', {
|
||||
lastSyncTime: 123,
|
||||
})
|
||||
expect(queryBuilder.skip).toHaveBeenCalledWith(1)
|
||||
expect(queryBuilder.take).toHaveBeenCalledWith(10)
|
||||
|
||||
expect(queryBuilder.orderBy).toHaveBeenCalledWith('item.updated_at_timestamp', 'DESC')
|
||||
|
||||
expect(result).toEqual(stream)
|
||||
})
|
||||
|
||||
it('should find items content sizes by all query criteria filled in', async () => {
|
||||
queryBuilder.getRawMany = jest.fn().mockReturnValue([{ uuid: item.uuid, contentSize: item.contentSize }])
|
||||
queryBuilder.where = jest.fn()
|
||||
queryBuilder.andWhere = jest.fn()
|
||||
queryBuilder.orderBy = jest.fn()
|
||||
queryBuilder.select = jest.fn()
|
||||
queryBuilder.addSelect = jest.fn()
|
||||
queryBuilder.skip = jest.fn()
|
||||
queryBuilder.take = jest.fn()
|
||||
|
||||
const result = await createRepository().findContentSizeForComputingTransferLimit({
|
||||
userUuid: '1-2-3',
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'DESC',
|
||||
deleted: false,
|
||||
contentType: ContentType.Note,
|
||||
lastSyncTime: 123,
|
||||
syncTimeComparison: '>=',
|
||||
uuids: ['2-3-4'],
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
})
|
||||
|
||||
expect(queryBuilder.select).toHaveBeenCalledWith('item.uuid', 'uuid')
|
||||
expect(queryBuilder.addSelect).toHaveBeenCalledWith('item.content_size', 'contentSize')
|
||||
expect(queryBuilder.where).toHaveBeenCalledTimes(1)
|
||||
expect(queryBuilder.andWhere).toHaveBeenCalledTimes(4)
|
||||
expect(queryBuilder.where).toHaveBeenNthCalledWith(1, 'item.user_uuid = :userUuid', { userUuid: '1-2-3' })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(1, 'item.uuid IN (:...uuids)', { uuids: ['2-3-4'] })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(2, 'item.deleted = :deleted', { deleted: false })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(3, 'item.content_type = :contentType', {
|
||||
contentType: 'Note',
|
||||
})
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(4, 'item.updated_at_timestamp >= :lastSyncTime', {
|
||||
lastSyncTime: 123,
|
||||
})
|
||||
expect(queryBuilder.skip).toHaveBeenCalledWith(1)
|
||||
expect(queryBuilder.take).toHaveBeenCalledWith(10)
|
||||
|
||||
expect(queryBuilder.orderBy).toHaveBeenCalledWith('item.updated_at_timestamp', 'DESC')
|
||||
|
||||
expect(result).toEqual([item])
|
||||
})
|
||||
|
||||
it('should find items by all query criteria filled in', async () => {
|
||||
queryBuilder.getMany = jest.fn().mockReturnValue([item])
|
||||
queryBuilder.where = jest.fn()
|
||||
queryBuilder.andWhere = jest.fn()
|
||||
queryBuilder.orderBy = jest.fn()
|
||||
queryBuilder.skip = jest.fn()
|
||||
queryBuilder.take = jest.fn()
|
||||
|
||||
const result = await createRepository().findAll({
|
||||
userUuid: '1-2-3',
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'DESC',
|
||||
deleted: false,
|
||||
contentType: ContentType.Note,
|
||||
lastSyncTime: 123,
|
||||
syncTimeComparison: '>=',
|
||||
uuids: ['2-3-4'],
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
})
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledTimes(1)
|
||||
expect(queryBuilder.andWhere).toHaveBeenCalledTimes(4)
|
||||
expect(queryBuilder.where).toHaveBeenNthCalledWith(1, 'item.user_uuid = :userUuid', { userUuid: '1-2-3' })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(1, 'item.uuid IN (:...uuids)', { uuids: ['2-3-4'] })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(2, 'item.deleted = :deleted', { deleted: false })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(3, 'item.content_type = :contentType', {
|
||||
contentType: 'Note',
|
||||
})
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(4, 'item.updated_at_timestamp >= :lastSyncTime', {
|
||||
lastSyncTime: 123,
|
||||
})
|
||||
expect(queryBuilder.skip).toHaveBeenCalledWith(1)
|
||||
expect(queryBuilder.take).toHaveBeenCalledWith(10)
|
||||
|
||||
expect(queryBuilder.orderBy).toHaveBeenCalledWith('item.updated_at_timestamp', 'DESC')
|
||||
|
||||
expect(result).toEqual([item])
|
||||
})
|
||||
|
||||
it('should count items by all query criteria filled in', async () => {
|
||||
queryBuilder.getCount = jest.fn().mockReturnValue(1)
|
||||
queryBuilder.where = jest.fn()
|
||||
queryBuilder.andWhere = jest.fn()
|
||||
queryBuilder.orderBy = jest.fn()
|
||||
queryBuilder.skip = jest.fn()
|
||||
queryBuilder.take = jest.fn()
|
||||
|
||||
const result = await createRepository().countAll({
|
||||
userUuid: '1-2-3',
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'DESC',
|
||||
deleted: false,
|
||||
contentType: ContentType.Note,
|
||||
lastSyncTime: 123,
|
||||
syncTimeComparison: '>=',
|
||||
uuids: ['2-3-4'],
|
||||
offset: 1,
|
||||
limit: 10,
|
||||
})
|
||||
|
||||
expect(queryBuilder.where).toHaveBeenCalledTimes(1)
|
||||
expect(queryBuilder.andWhere).toHaveBeenCalledTimes(4)
|
||||
expect(queryBuilder.where).toHaveBeenNthCalledWith(1, 'item.user_uuid = :userUuid', { userUuid: '1-2-3' })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(1, 'item.uuid IN (:...uuids)', { uuids: ['2-3-4'] })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(2, 'item.deleted = :deleted', { deleted: false })
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(3, 'item.content_type = :contentType', {
|
||||
contentType: 'Note',
|
||||
})
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(4, 'item.updated_at_timestamp >= :lastSyncTime', {
|
||||
lastSyncTime: 123,
|
||||
})
|
||||
expect(queryBuilder.skip).toHaveBeenCalledWith(1)
|
||||
expect(queryBuilder.take).toHaveBeenCalledWith(10)
|
||||
|
||||
expect(queryBuilder.orderBy).toHaveBeenCalledWith('item.updated_at_timestamp', 'DESC')
|
||||
|
||||
expect(result).toEqual(1)
|
||||
})
|
||||
|
||||
it('should find items by only mandatory query criteria', async () => {
|
||||
queryBuilder.getMany = jest.fn().mockReturnValue([item])
|
||||
queryBuilder.where = jest.fn()
|
||||
queryBuilder.orderBy = jest.fn()
|
||||
|
||||
const result = await createRepository().findAll({
|
||||
sortBy: 'updated_at_timestamp',
|
||||
sortOrder: 'DESC',
|
||||
})
|
||||
|
||||
expect(queryBuilder.orderBy).toHaveBeenCalledWith('item.updated_at_timestamp', 'DESC')
|
||||
|
||||
expect(result).toEqual([item])
|
||||
})
|
||||
|
||||
it('should find dates for computing integrity hash', async () => {
|
||||
queryBuilder.getRawMany = jest
|
||||
.fn()
|
||||
.mockReturnValue([{ updated_at_timestamp: 1616164633241312 }, { updated_at_timestamp: 1616164633242313 }])
|
||||
queryBuilder.select = jest.fn()
|
||||
queryBuilder.where = jest.fn()
|
||||
queryBuilder.andWhere = jest.fn()
|
||||
|
||||
const result = await createRepository().findDatesForComputingIntegrityHash('1-2-3')
|
||||
|
||||
expect(queryBuilder.select).toHaveBeenCalledWith('item.updated_at_timestamp')
|
||||
expect(queryBuilder.where).toHaveBeenCalledTimes(1)
|
||||
expect(queryBuilder.where).toHaveBeenNthCalledWith(1, 'item.user_uuid = :userUuid', { userUuid: '1-2-3' })
|
||||
expect(queryBuilder.andWhere).toHaveBeenCalledTimes(1)
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(1, 'item.deleted = :deleted', { deleted: false })
|
||||
|
||||
expect(result.length).toEqual(2)
|
||||
expect(result[0]).toEqual({ updated_at_timestamp: 1616164633242313 })
|
||||
expect(result[1]).toEqual({ updated_at_timestamp: 1616164633241312 })
|
||||
})
|
||||
|
||||
it('should find items for computing integrity payloads', async () => {
|
||||
queryBuilder.getRawMany = jest.fn().mockReturnValue([
|
||||
{ uuid: '1-2-3', updated_at_timestamp: 1616164633241312, content_type: ContentType.Note },
|
||||
{ uuid: '2-3-4', updated_at_timestamp: 1616164633242313, content_type: ContentType.ItemsKey },
|
||||
])
|
||||
queryBuilder.select = jest.fn()
|
||||
queryBuilder.addSelect = jest.fn()
|
||||
queryBuilder.where = jest.fn()
|
||||
queryBuilder.andWhere = jest.fn()
|
||||
|
||||
const result = await createRepository().findItemsForComputingIntegrityPayloads('1-2-3')
|
||||
|
||||
expect(queryBuilder.select).toHaveBeenCalledWith('item.uuid', 'uuid')
|
||||
expect(queryBuilder.addSelect).toHaveBeenNthCalledWith(1, 'item.updated_at_timestamp', 'updated_at_timestamp')
|
||||
expect(queryBuilder.addSelect).toHaveBeenNthCalledWith(2, 'item.content_type', 'content_type')
|
||||
expect(queryBuilder.where).toHaveBeenCalledTimes(1)
|
||||
expect(queryBuilder.where).toHaveBeenNthCalledWith(1, 'item.user_uuid = :userUuid', { userUuid: '1-2-3' })
|
||||
expect(queryBuilder.andWhere).toHaveBeenCalledTimes(1)
|
||||
expect(queryBuilder.andWhere).toHaveBeenNthCalledWith(1, 'item.deleted = :deleted', { deleted: false })
|
||||
|
||||
expect(result.length).toEqual(2)
|
||||
expect(result[0]).toEqual({
|
||||
uuid: '2-3-4',
|
||||
updated_at_timestamp: 1616164633242313,
|
||||
content_type: ContentType.ItemsKey,
|
||||
})
|
||||
expect(result[1]).toEqual({ uuid: '1-2-3', updated_at_timestamp: 1616164633241312, content_type: ContentType.Note })
|
||||
})
|
||||
|
||||
it('should find item by uuid and mark it for deletion', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.update = jest.fn().mockReturnThis()
|
||||
queryBuilder.update().set = jest.fn().mockReturnThis()
|
||||
queryBuilder.execute = jest.fn()
|
||||
|
||||
const item = { uuid: 'e-1-2-3' } as jest.Mocked<Item>
|
||||
const updatedAtTimestamp = timer.getTimestampInMicroseconds()
|
||||
await createRepository().markItemsAsDeleted([item.uuid], updatedAtTimestamp)
|
||||
|
||||
expect(queryBuilder.update).toHaveBeenCalled()
|
||||
expect(queryBuilder.update().set).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
deleted: true,
|
||||
content: null,
|
||||
encItemKey: null,
|
||||
authHash: null,
|
||||
updatedAtTimestamp: expect.anything(),
|
||||
}),
|
||||
)
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('uuid IN (:...uuids)', {
|
||||
uuids: ['e-1-2-3'],
|
||||
})
|
||||
expect(queryBuilder.execute).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should update item content size', async () => {
|
||||
queryBuilder.where = jest.fn().mockReturnThis()
|
||||
queryBuilder.update = jest.fn().mockReturnThis()
|
||||
queryBuilder.update().set = jest.fn().mockReturnThis()
|
||||
queryBuilder.execute = jest.fn()
|
||||
|
||||
await createRepository().updateContentSize('1-2-3', 345)
|
||||
|
||||
expect(queryBuilder.update).toHaveBeenCalled()
|
||||
expect(queryBuilder.update().set).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
contentSize: 345,
|
||||
}),
|
||||
)
|
||||
expect(queryBuilder.where).toHaveBeenCalledWith('uuid = :itemUuid', {
|
||||
itemUuid: '1-2-3',
|
||||
})
|
||||
expect(queryBuilder.execute).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
@@ -103,6 +103,10 @@ export class MySQLItemRepository implements ItemRepositoryInterface {
|
||||
return this.createFindAllQueryBuilder(query).getMany()
|
||||
}
|
||||
|
||||
async findAllRaw<T>(query: ItemQuery): Promise<T[]> {
|
||||
return this.createFindAllQueryBuilder(query).getRawMany<T>()
|
||||
}
|
||||
|
||||
async streamAll(query: ItemQuery): Promise<ReadStream> {
|
||||
return this.createFindAllQueryBuilder(query).stream()
|
||||
}
|
||||
@@ -131,10 +135,12 @@ export class MySQLItemRepository implements ItemRepositoryInterface {
|
||||
private createFindAllQueryBuilder(query: ItemQuery): SelectQueryBuilder<Item> {
|
||||
const queryBuilder = this.ormRepository.createQueryBuilder('item')
|
||||
|
||||
queryBuilder.orderBy(`item.${query.sortBy}`, query.sortOrder)
|
||||
if (query.sortBy !== undefined && query.sortOrder !== undefined) {
|
||||
queryBuilder.orderBy(`item.${query.sortBy}`, query.sortOrder)
|
||||
}
|
||||
|
||||
if (query.selectFields !== undefined) {
|
||||
queryBuilder.select(query.selectFields.map((field) => `item.${field}`))
|
||||
if (query.selectString !== undefined) {
|
||||
queryBuilder.select(query.selectString)
|
||||
}
|
||||
if (query.userUuid !== undefined) {
|
||||
queryBuilder.where('item.user_uuid = :userUuid', { userUuid: query.userUuid })
|
||||
@@ -146,15 +152,22 @@ export class MySQLItemRepository implements ItemRepositoryInterface {
|
||||
queryBuilder.andWhere('item.deleted = :deleted', { deleted: query.deleted })
|
||||
}
|
||||
if (query.contentType) {
|
||||
queryBuilder.andWhere('item.content_type = :contentType', { contentType: query.contentType })
|
||||
if (Array.isArray(query.contentType)) {
|
||||
queryBuilder.andWhere('item.content_type IN (:...contentTypes)', { contentTypes: query.contentType })
|
||||
} else {
|
||||
queryBuilder.andWhere('item.content_type = :contentType', { contentType: query.contentType })
|
||||
}
|
||||
}
|
||||
if (query.lastSyncTime && query.syncTimeComparison) {
|
||||
queryBuilder.andWhere(`item.updated_at_timestamp ${query.syncTimeComparison} :lastSyncTime`, {
|
||||
lastSyncTime: query.lastSyncTime,
|
||||
})
|
||||
}
|
||||
if (query.createdBefore !== undefined) {
|
||||
queryBuilder.andWhere('item.created_at < :createdAt', { createdAt: query.createdBefore.toISOString() })
|
||||
if (query.createdBetween !== undefined) {
|
||||
queryBuilder.andWhere('item.created_at >= :createdAfter AND item.created_at <= :createdBefore', {
|
||||
createdAfter: query.createdBetween[0].toISOString(),
|
||||
createdBefore: query.createdBetween[1].toISOString(),
|
||||
})
|
||||
}
|
||||
if (query.offset !== undefined) {
|
||||
queryBuilder.skip(query.offset)
|
||||
|
||||
@@ -3,6 +3,14 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## [1.18.6](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.18.5...@standardnotes/workspace-server@1.18.6) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/workspace-server
|
||||
|
||||
## [1.18.5](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.18.4...@standardnotes/workspace-server@1.18.5) (2022-12-15)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/workspace-server
|
||||
|
||||
## [1.18.4](https://github.com/standardnotes/server/compare/@standardnotes/workspace-server@1.18.3...@standardnotes/workspace-server@1.18.4) (2022-12-12)
|
||||
|
||||
**Note:** Version bump only for package @standardnotes/workspace-server
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@standardnotes/workspace-server",
|
||||
"version": "1.18.4",
|
||||
"version": "1.18.6",
|
||||
"engines": {
|
||||
"node": ">=18.0.0 <19.0.0"
|
||||
},
|
||||
|
||||
@@ -1981,10 +1981,6 @@ __metadata:
|
||||
version: 0.0.0-use.local
|
||||
resolution: "@standardnotes/domain-core@workspace:packages/domain-core"
|
||||
dependencies:
|
||||
"@standardnotes/common": "workspace:*"
|
||||
"@standardnotes/features": "npm:^1.52.1"
|
||||
"@standardnotes/predicates": "workspace:*"
|
||||
"@standardnotes/security": "workspace:*"
|
||||
"@types/jest": "npm:^29.1.1"
|
||||
"@types/uuid": "npm:^8.3.0"
|
||||
"@typescript-eslint/eslint-plugin": "npm:^5.30.0"
|
||||
|
||||
Reference in New Issue
Block a user