Compare commits

..

8 Commits

Author SHA1 Message Date
standardci
1d576d48ad chore(release): publish new version
- @standardnotes/auth-server@1.67.1
 - @standardnotes/syncing-server@1.24.7
2022-12-12 13:20:47 +00:00
Karol Sójko
4ff8030f87 fix(syncing-server): revisions updating - select fields 2022-12-12 14:18:45 +01:00
Karol Sójko
c15e2e2c8f fix: user signed in email template 2022-12-12 14:18:45 +01:00
standardci
41d31a8d75 chore(release): publish new version
- @standardnotes/auth-server@1.67.0
2022-12-12 13:00:40 +00:00
Karol Sójko
10e2a26352 feat(auth): add email subscription unsubscribed event handler 2022-12-12 13:58:35 +01:00
standardci
6e547f77d0 chore(release): publish new version
- @standardnotes/revisions-server@1.9.26
2022-12-12 12:14:52 +00:00
Karol Sójko
530a426601 fix(revisions): responses to match previous response structure 2022-12-12 13:12:46 +01:00
Karol Sójko
642d6bab77 chore: fix triggers for other repos dep 2022-12-12 12:56:28 +01:00
22 changed files with 226 additions and 13 deletions

View File

@@ -187,7 +187,7 @@ jobs:
tags: standardnotes/${{ inputs.service_name }}:${{ github.sha }}
- name: Run E2E test suite
uses: convictional/trigger-workflow-and-wait@v1.6.3
uses: convictional/trigger-workflow-and-wait@master
with:
owner: standardnotes
repo: e2e

View File

@@ -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.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
* **auth:** add email subscription unsubscribed event handler ([10e2a26](https://github.com/standardnotes/server/commit/10e2a263522dfa33c06940f29cb77f783f66b20c))
## [1.66.9](https://github.com/standardnotes/server/compare/@standardnotes/auth-server@1.66.8...@standardnotes/auth-server@1.66.9) (2022-12-12)
**Note:** Version bump only for package @standardnotes/auth-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/auth-server",
"version": "1.66.9",
"version": "1.67.1",
"engines": {
"node": ">=18.0.0 <19.0.0"
},

View File

@@ -193,6 +193,7 @@ import { SubscriptionInvitesController } from '../Controller/SubscriptionInvites
import { CreateCrossServiceToken } from '../Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken'
import { ProcessUserRequest } from '../Domain/UseCase/ProcessUserRequest/ProcessUserRequest'
import { UserRequestsController } from '../Controller/UserRequestsController'
import { EmailSubscriptionUnsubscribedEventHandler } from '../Domain/Handler/EmailSubscriptionUnsubscribedEventHandler'
// eslint-disable-next-line @typescript-eslint/no-var-requires
const newrelicFormatter = require('@newrelic/winston-enricher')
@@ -560,6 +561,15 @@ export class ContainerConfigLoader {
)
}
container
.bind<EmailSubscriptionUnsubscribedEventHandler>(TYPES.EmailSubscriptionUnsubscribedEventHandler)
.toConstantValue(
new EmailSubscriptionUnsubscribedEventHandler(
container.get(TYPES.UserRepository),
container.get(TYPES.SettingService),
),
)
const eventHandlers: Map<string, DomainEventHandlerInterface> = new Map([
['USER_REGISTERED', container.get(TYPES.UserRegisteredEventHandler)],
['ACCOUNT_DELETION_REQUESTED', container.get(TYPES.AccountDeletionRequestedEventHandler)],
@@ -582,6 +592,7 @@ export class ContainerConfigLoader {
],
['SHARED_SUBSCRIPTION_INVITATION_CREATED', container.get(TYPES.SharedSubscriptionInvitationCreatedEventHandler)],
['PREDICATE_VERIFICATION_REQUESTED', container.get(TYPES.PredicateVerificationRequestedEventHandler)],
['EMAIL_SUBSCRIPTION_UNSUBSCRIBED', container.get(TYPES.EmailSubscriptionUnsubscribedEventHandler)],
])
if (env.get('SQS_QUEUE_URL', true)) {

View File

@@ -138,6 +138,7 @@ const TYPES = {
UserDisabledSessionUserAgentLoggingEventHandler: Symbol.for('UserDisabledSessionUserAgentLoggingEventHandler'),
SharedSubscriptionInvitationCreatedEventHandler: Symbol.for('SharedSubscriptionInvitationCreatedEventHandler'),
PredicateVerificationRequestedEventHandler: Symbol.for('PredicateVerificationRequestedEventHandler'),
EmailSubscriptionUnsubscribedEventHandler: Symbol.for('EmailSubscriptionUnsubscribedEventHandler'),
// Services
DeviceDetector: Symbol.for('DeviceDetector'),
SessionService: Symbol.for('SessionService'),

View File

@@ -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>
`

View File

@@ -0,0 +1,109 @@
import { EmailLevel } from '@standardnotes/domain-core'
import { EmailSubscriptionUnsubscribedEvent } from '@standardnotes/domain-events'
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
import { User } from '../User/User'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
import { EmailSubscriptionUnsubscribedEventHandler } from './EmailSubscriptionUnsubscribedEventHandler'
describe('EmailSubscriptionUnsubscribedEventHandler', () => {
let userRepository: UserRepositoryInterface
let settingsService: SettingServiceInterface
let event: EmailSubscriptionUnsubscribedEvent
const createHandler = () => new EmailSubscriptionUnsubscribedEventHandler(userRepository, settingsService)
beforeEach(() => {
userRepository = {} as jest.Mocked<UserRepositoryInterface>
userRepository.findOneByEmail = jest.fn().mockReturnValue({} as jest.Mocked<User>)
settingsService = {} as jest.Mocked<SettingServiceInterface>
settingsService.createOrReplace = jest.fn()
event = {
payload: {
userEmail: 'test@test.te',
level: EmailLevel.LEVELS.Marketing,
},
} as jest.Mocked<EmailSubscriptionUnsubscribedEvent>
})
it('should not do anything if user is not found', async () => {
userRepository.findOneByEmail = jest.fn().mockReturnValue(null)
await createHandler().handle(event)
expect(settingsService.createOrReplace).not.toHaveBeenCalled()
})
it('should update user marketing email settings', async () => {
await createHandler().handle(event)
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
user: {},
props: {
name: 'MUTE_MARKETING_EMAILS',
unencryptedValue: 'muted',
sensitive: false,
},
})
})
it('should update user sign in email settings', async () => {
event.payload.level = EmailLevel.LEVELS.SignIn
await createHandler().handle(event)
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
user: {},
props: {
name: 'MUTE_SIGN_IN_EMAILS',
unencryptedValue: 'muted',
sensitive: false,
},
})
})
it('should update user email backup email settings', async () => {
event.payload.level = EmailLevel.LEVELS.FailedEmailBackup
await createHandler().handle(event)
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
user: {},
props: {
name: 'MUTE_FAILED_BACKUPS_EMAILS',
unencryptedValue: 'muted',
sensitive: false,
},
})
})
it('should update user email backup email settings', async () => {
event.payload.level = EmailLevel.LEVELS.FailedCloudBackup
await createHandler().handle(event)
expect(settingsService.createOrReplace).toHaveBeenCalledWith({
user: {},
props: {
name: 'MUTE_FAILED_CLOUD_BACKUPS_EMAILS',
unencryptedValue: 'muted',
sensitive: false,
},
})
})
it('should throw error for unrecognized level', async () => {
event.payload.level = 'foobar'
let caughtError = null
try {
await createHandler().handle(event)
} catch (error) {
caughtError = error
}
expect(caughtError).not.toBeNull()
})
})

View File

@@ -0,0 +1,41 @@
import { EmailLevel } from '@standardnotes/domain-core'
import { DomainEventHandlerInterface, EmailSubscriptionUnsubscribedEvent } from '@standardnotes/domain-events'
import { SettingName } from '@standardnotes/settings'
import { SettingServiceInterface } from '../Setting/SettingServiceInterface'
import { UserRepositoryInterface } from '../User/UserRepositoryInterface'
export class EmailSubscriptionUnsubscribedEventHandler implements DomainEventHandlerInterface {
constructor(private userRepository: UserRepositoryInterface, private settingsService: SettingServiceInterface) {}
async handle(event: EmailSubscriptionUnsubscribedEvent): Promise<void> {
const user = await this.userRepository.findOneByEmail(event.payload.userEmail)
if (user === null) {
return
}
await this.settingsService.createOrReplace({
user,
props: {
name: this.getSettingNameFromLevel(event.payload.level),
unencryptedValue: 'muted',
sensitive: false,
},
})
}
private getSettingNameFromLevel(level: string): string {
switch (level) {
case EmailLevel.LEVELS.FailedCloudBackup:
return SettingName.MuteFailedCloudBackupsEmails
case EmailLevel.LEVELS.FailedEmailBackup:
return SettingName.MuteFailedBackupsEmails
case EmailLevel.LEVELS.Marketing:
return SettingName.MuteMarketingEmails
case EmailLevel.LEVELS.SignIn:
return SettingName.MuteSignInEmails
default:
throw new Error(`Unknown level: ${level}`)
}
}
}

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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
* **revisions:** responses to match previous response structure ([530a426](https://github.com/standardnotes/server/commit/530a42660157b63d034cd2228fc83a3fcce921e0))
## [1.9.25](https://github.com/standardnotes/server/compare/@standardnotes/revisions-server@1.9.24...@standardnotes/revisions-server@1.9.25) (2022-12-12)
**Note:** Version bump only for package @standardnotes/revisions-server

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/revisions-server",
"version": "1.9.25",
"version": "1.9.26",
"engines": {
"node": ">=18.0.0 <19.0.0"
},

View File

@@ -2,11 +2,13 @@ import { Logger } from 'winston'
import { HttpResponse, HttpStatusCode } from '@standardnotes/api'
import { GetRevisionsMetada } from '../Domain/UseCase/GetRevisionsMetada/GetRevisionsMetada'
import { GetRevisionsMetadataRequestParams } from '../Infra/Http/GetRevisionsMetadataRequestParams'
import { GetRevisionRequestParams } from '../Infra/Http/GetRevisionRequestParams'
import { GetRevisionsMetadataRequestParams } from '../Infra/Http/Request/GetRevisionsMetadataRequestParams'
import { GetRevisionRequestParams } from '../Infra/Http/Request/GetRevisionRequestParams'
import { DeleteRevisionRequestParams } from '../Infra/Http/Request/DeleteRevisionRequestParams'
import { GetRevision } from '../Domain/UseCase/GetRevision/GetRevision'
import { DeleteRevision } from '../Domain/UseCase/DeleteRevision/DeleteRevision'
import { DeleteRevisionRequestParams } from '../Infra/Http/DeleteRevisionRequestParams'
import { GetRevisionsMetadataResponse } from '../Infra/Http/Response/GetRevisionsMetadataResponse'
import { GetRevisionResponse } from '../Infra/Http/Response/GetRevisionResponse'
export class RevisionsController {
constructor(
@@ -16,7 +18,7 @@ export class RevisionsController {
private logger: Logger,
) {}
async getRevisions(params: GetRevisionsMetadataRequestParams): Promise<HttpResponse> {
async getRevisions(params: GetRevisionsMetadataRequestParams): Promise<GetRevisionsMetadataResponse> {
const revisionMetadataOrError = await this.getRevisionsMetadata.execute({
itemUuid: params.itemUuid,
userUuid: params.userUuid,
@@ -41,7 +43,7 @@ export class RevisionsController {
}
}
async getRevision(params: GetRevisionRequestParams): Promise<HttpResponse> {
async getRevision(params: GetRevisionRequestParams): Promise<GetRevisionResponse> {
const revisionOrError = await this.doGetRevision.execute({
revisionUuid: params.revisionUuid,
userUuid: params.userUuid,

View File

@@ -0,0 +1,8 @@
import { HttpErrorResponseBody, HttpResponse } from '@standardnotes/api'
import { Either } from '@standardnotes/common'
import { GetRevisionResponseBody } from './GetRevisionResponseBody'
export interface GetRevisionResponse extends HttpResponse {
data: Either<GetRevisionResponseBody, HttpErrorResponseBody>
}

View File

@@ -0,0 +1,5 @@
import { Revision } from '../../../Domain/Revision/Revision'
export interface GetRevisionResponseBody {
revision: Revision
}

View File

@@ -0,0 +1,8 @@
import { HttpErrorResponseBody, HttpResponse } from '@standardnotes/api'
import { Either } from '@standardnotes/common'
import { GetRevisionsMetadataResponseBody } from './GetRevisionsMetadataResponseBody'
export interface GetRevisionsMetadataResponse extends HttpResponse {
data: Either<GetRevisionsMetadataResponseBody, HttpErrorResponseBody>
}

View File

@@ -0,0 +1,5 @@
import { RevisionMetadata } from '../../../Domain/Revision/RevisionMetadata'
export interface GetRevisionsMetadataResponseBody {
revisions: Array<RevisionMetadata>
}

View File

@@ -18,7 +18,7 @@ export class InversifyExpressRevisionsController extends BaseHttpController {
userUuid: response.locals.user.uuid,
})
return this.json(result.data, result.status)
return this.json(result.data.error ? result.data : result.data.revisions, result.status)
}
@httpGet('/:uuid')
@@ -28,7 +28,7 @@ export class InversifyExpressRevisionsController extends BaseHttpController {
userUuid: response.locals.user.uuid,
})
return this.json(result.data, result.status)
return this.json(result.data.error ? result.data : result.data.revision, result.status)
}
@httpDelete('/:uuid')

View File

@@ -3,6 +3,12 @@
All notable changes to this project will be documented in this file.
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
## [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

View File

@@ -22,7 +22,7 @@ const fixRevisionsOwnership = async (
sortBy: 'updated_at_timestamp',
sortOrder: 'ASC',
createdBefore: new Date('2022-11-23'),
selectFields: ['user_uuid', 'item_uuid'],
selectFields: ['user_uuid', 'uuid'],
})
return new Promise((resolve, reject) => {

View File

@@ -1,6 +1,6 @@
{
"name": "@standardnotes/syncing-server",
"version": "1.24.6",
"version": "1.24.7",
"engines": {
"node": ">=18.0.0 <19.0.0"
},