mirror of
https://github.com/omnivore-app/omnivore.git
synced 2026-03-11 08:54:26 +00:00
Merge pull request #2891 from omnivore-app/disable-database-query-log-in-github-action
disable database query logs in github action
This commit is contained in:
commit
fe3093834c
23 changed files with 199 additions and 256 deletions
28
.github/workflows/run-tests.yaml
vendored
28
.github/workflows/run-tests.yaml
vendored
|
|
@ -27,21 +27,6 @@ jobs:
|
|||
--health-retries 5
|
||||
ports:
|
||||
- 5432
|
||||
elastic:
|
||||
image: docker.elastic.co/elasticsearch/elasticsearch:7.17.1
|
||||
env:
|
||||
discovery.type: single-node
|
||||
http.cors.allow-origin: '*'
|
||||
http.cors.enabled: true
|
||||
http.cors.allow-headers: 'X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization'
|
||||
http.cors.allow-credentials: true
|
||||
options: >-
|
||||
--health-cmd "curl http://0.0.0.0:9200/_cluster/health"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
--health-retries 10
|
||||
ports:
|
||||
- 9200
|
||||
redis:
|
||||
image: redis
|
||||
options: >-
|
||||
|
|
@ -82,22 +67,12 @@ jobs:
|
|||
PG_USER: postgres
|
||||
PG_PASSWORD: postgres
|
||||
PG_DB: omnivore_test
|
||||
ELASTIC_URL: http://localhost:${{ job.services.elastic.ports[9200] }}/
|
||||
PGPASSWORD: postgres # This is required for the psql command to work without a password prompt
|
||||
- name: TypeScript Build and Lint
|
||||
run: |
|
||||
source ~/.nvm/nvm.sh
|
||||
yarn build
|
||||
yarn lint
|
||||
env:
|
||||
PG_HOST: localhost
|
||||
PG_PORT: ${{ job.services.postgres.ports[5432] }}
|
||||
PG_USER: app_user
|
||||
PG_PASSWORD: app_pass
|
||||
PG_DB: omnivore_test
|
||||
PG_POOL_MAX: 10
|
||||
ELASTIC_URL: http://localhost:${{ job.services.elastic.ports[9200] }}/
|
||||
REDIS_URL: redis://localhost:${{ job.services.redis.ports[6379] }}
|
||||
- name: Tests
|
||||
run: |
|
||||
source ~/.nvm/nvm.sh
|
||||
|
|
@ -108,8 +83,7 @@ jobs:
|
|||
PG_USER: app_user
|
||||
PG_PASSWORD: app_pass
|
||||
PG_DB: omnivore_test
|
||||
PG_POOL_MAX: 10
|
||||
ELASTIC_URL: http://localhost:${{ job.services.elastic.ports[9200] }}/
|
||||
PG_LOGGER: debug
|
||||
REDIS_URL: redis://localhost:${{ job.services.redis.ports[6379] }}
|
||||
build-docker-images:
|
||||
name: Build docker images
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ import * as httpContext from 'express-http-context2'
|
|||
import * as jwt from 'jsonwebtoken'
|
||||
import { EntityManager } from 'typeorm'
|
||||
import { promisify } from 'util'
|
||||
import { appDataSource } from './data_source'
|
||||
import { sanitizeDirectiveTransformer } from './directives'
|
||||
import { env } from './env'
|
||||
import { createPubSubClient } from './pubsub'
|
||||
import { entityManager } from './repository'
|
||||
import { functionResolvers } from './resolvers/function_resolvers'
|
||||
import { ClaimsToSet, ResolverContext } from './resolvers/types'
|
||||
import ScalarResolvers from './scalars'
|
||||
|
|
@ -79,7 +79,7 @@ const contextFunc: ContextFunction<ExpressContext, ResolverContext> = async ({
|
|||
cb: (em: EntityManager) => TResult,
|
||||
userRole?: string
|
||||
): Promise<TResult> =>
|
||||
entityManager.transaction(async (tx) => {
|
||||
appDataSource.transaction(async (tx) => {
|
||||
await setClaims(tx, undefined, userRole)
|
||||
return cb(tx)
|
||||
}),
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */
|
||||
import { PubSub } from '@google-cloud/pubsub'
|
||||
import {
|
||||
BaseEntity,
|
||||
EntitySubscriberInterface,
|
||||
EventSubscriber,
|
||||
InsertEvent,
|
||||
} from 'typeorm'
|
||||
import { env } from '../env'
|
||||
import { logger } from '../utils/logger'
|
||||
|
||||
const TOPIC_NAME = 'EntityCreated'
|
||||
|
||||
@EventSubscriber()
|
||||
export class PublishEntitySubscriber implements EntitySubscriberInterface {
|
||||
async afterInsert(event: InsertEvent<BaseEntity>): Promise<void> {
|
||||
const client = new PubSub()
|
||||
|
||||
const msg = JSON.stringify({
|
||||
type: 'EntityCreated',
|
||||
entity: event.entity,
|
||||
entityClass: event.entity?.constructor?.name,
|
||||
})
|
||||
|
||||
if (env.dev.isLocal) {
|
||||
logger.info('PublishEntitySubscriber', msg)
|
||||
return
|
||||
}
|
||||
|
||||
await client
|
||||
.topic(TOPIC_NAME)
|
||||
.publishMessage({ data: Buffer.from(msg) })
|
||||
.catch((err) => {
|
||||
logger.error('PublishEntitySubscriber error publishing event', err)
|
||||
})
|
||||
}
|
||||
}
|
||||
34
packages/api/src/events/user/profile_created.ts
Normal file
34
packages/api/src/events/user/profile_created.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
EntitySubscriberInterface,
|
||||
EventSubscriber,
|
||||
InsertEvent,
|
||||
} from 'typeorm'
|
||||
import { Profile } from '../../entity/profile'
|
||||
import { createDefaultFiltersForUser } from '../../services/create_user'
|
||||
import { addPopularReadsForNewUser } from '../../services/popular_reads'
|
||||
|
||||
@EventSubscriber()
|
||||
export class AddPopularReadsToNewUser
|
||||
implements EntitySubscriberInterface<Profile>
|
||||
{
|
||||
listenTo() {
|
||||
return Profile
|
||||
}
|
||||
|
||||
async afterInsert(event: InsertEvent<Profile>): Promise<void> {
|
||||
await addPopularReadsForNewUser(event.entity.user.id, event.manager)
|
||||
}
|
||||
}
|
||||
|
||||
@EventSubscriber()
|
||||
export class AddDefaultFiltersToNewUser
|
||||
implements EntitySubscriberInterface<Profile>
|
||||
{
|
||||
listenTo() {
|
||||
return Profile
|
||||
}
|
||||
|
||||
async afterInsert(event: InsertEvent<Profile>): Promise<void> {
|
||||
await createDefaultFiltersForUser(event.manager)(event.entity.user.id)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
import {
|
||||
EntitySubscriberInterface,
|
||||
EventSubscriber,
|
||||
InsertEvent,
|
||||
} from 'typeorm'
|
||||
import { Profile } from '../../entity/profile'
|
||||
import { createPubSubClient } from '../../pubsub'
|
||||
import { addPopularReadsForNewUser } from '../../services/popular_reads'
|
||||
import { IntercomClient } from '../../utils/intercom'
|
||||
|
||||
@EventSubscriber()
|
||||
export class CreateIntercomAccount
|
||||
implements EntitySubscriberInterface<Profile>
|
||||
{
|
||||
listenTo() {
|
||||
return Profile
|
||||
}
|
||||
|
||||
async afterInsert(event: InsertEvent<Profile>): Promise<void> {
|
||||
const profile = event.entity
|
||||
|
||||
const customAttributes: { source_user_id: string } = {
|
||||
source_user_id: profile.user.sourceUserId,
|
||||
}
|
||||
await IntercomClient?.contacts.createUser({
|
||||
email: profile.user.email,
|
||||
externalId: profile.user.id,
|
||||
name: profile.user.name,
|
||||
avatar: profile.pictureUrl || undefined,
|
||||
customAttributes: customAttributes,
|
||||
signedUpAt: Math.floor(Date.now() / 1000),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@EventSubscriber()
|
||||
export class PublishNewUserEvent implements EntitySubscriberInterface<Profile> {
|
||||
listenTo() {
|
||||
return Profile
|
||||
}
|
||||
|
||||
async afterInsert(event: InsertEvent<Profile>): Promise<void> {
|
||||
const client = createPubSubClient()
|
||||
await client.userCreated(
|
||||
event.entity.user.id,
|
||||
event.entity.user.email,
|
||||
event.entity.user.name,
|
||||
event.entity.username
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@EventSubscriber()
|
||||
export class AddPopularReadsToNewUser
|
||||
implements EntitySubscriberInterface<Profile>
|
||||
{
|
||||
listenTo() {
|
||||
return Profile
|
||||
}
|
||||
|
||||
async afterInsert(event: InsertEvent<Profile>): Promise<void> {
|
||||
await addPopularReadsForNewUser(event.entity.user.id, event.manager)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { DeepPartial } from 'typeorm'
|
||||
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'
|
||||
import { entityManager } from '.'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { Highlight } from '../entity/highlight'
|
||||
import { unescapeHtml } from '../utils/helpers'
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ const unescapeHighlight = (highlight: DeepPartial<Highlight>) => {
|
|||
return highlight
|
||||
}
|
||||
|
||||
export const highlightRepository = entityManager
|
||||
export const highlightRepository = appDataSource
|
||||
.getRepository(Highlight)
|
||||
.extend({
|
||||
findById(id: string) {
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ export const setClaims = async (
|
|||
|
||||
export const authTrx = async <T>(
|
||||
fn: (manager: EntityManager) => Promise<T>,
|
||||
em = entityManager,
|
||||
em = appDataSource.manager,
|
||||
uid?: string,
|
||||
userRole?: string
|
||||
): Promise<T> => {
|
||||
|
|
@ -40,7 +40,5 @@ export const authTrx = async <T>(
|
|||
}
|
||||
|
||||
export const getRepository = <T>(entity: EntityTarget<T>) => {
|
||||
return entityManager.getRepository(entity)
|
||||
return appDataSource.getRepository(entity)
|
||||
}
|
||||
|
||||
export const entityManager = appDataSource.manager
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { In } from 'typeorm'
|
||||
import { QueryDeepPartialEntity } from 'typeorm/query-builder/QueryPartialEntity'
|
||||
import { entityManager } from '.'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { Label } from '../entity/label'
|
||||
import { generateRandomColor } from '../utils/helpers'
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ const convertToLabel = (label: CreateLabelInput, userId: string) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const labelRepository = entityManager.getRepository(Label).extend({
|
||||
export const labelRepository = appDataSource.getRepository(Label).extend({
|
||||
findById(id: string) {
|
||||
return this.findOneBy({ id })
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { entityManager } from '.'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { LibraryItem } from '../entity/library_item'
|
||||
|
||||
export const libraryItemRepository = entityManager
|
||||
export const libraryItemRepository = appDataSource
|
||||
.getRepository(LibraryItem)
|
||||
.extend({
|
||||
findById(id: string) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { In } from 'typeorm'
|
||||
import { entityManager } from '.'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { User } from './../entity/user'
|
||||
|
||||
const TOP_USERS = [
|
||||
|
|
@ -14,7 +14,7 @@ const TOP_USERS = [
|
|||
]
|
||||
export const MAX_RECORDS_LIMIT = 1000
|
||||
|
||||
export const userRepository = entityManager.getRepository(User).extend({
|
||||
export const userRepository = appDataSource.getRepository(User).extend({
|
||||
findById(id: string) {
|
||||
return this.findOneBy({ id })
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,29 +1,22 @@
|
|||
import { EntityManager } from 'typeorm'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { Filter } from '../entity/filter'
|
||||
import { GroupMembership } from '../entity/groups/group_membership'
|
||||
import { Invite } from '../entity/groups/invite'
|
||||
import { Profile } from '../entity/profile'
|
||||
import { StatusType, User } from '../entity/user'
|
||||
import { env } from '../env'
|
||||
import { SignupErrorCode } from '../generated/graphql'
|
||||
import { authTrx, entityManager, getRepository } from '../repository'
|
||||
import { createPubSubClient } from '../pubsub'
|
||||
import { authTrx, getRepository } from '../repository'
|
||||
import { userRepository } from '../repository/user'
|
||||
import { AuthProvider } from '../routers/auth/auth_types'
|
||||
import { analytics } from '../utils/analytics'
|
||||
import { IntercomClient } from '../utils/intercom'
|
||||
import { logger } from '../utils/logger'
|
||||
import { validateUsername } from '../utils/usernamePolicy'
|
||||
import { sendConfirmationEmail } from './send_emails'
|
||||
import { Filter } from '../entity/filter'
|
||||
import { analytics } from '../utils/analytics'
|
||||
import { env } from '../env'
|
||||
|
||||
const TOP_USERS = [
|
||||
'jacksonh',
|
||||
'nat',
|
||||
'luis',
|
||||
'satindar',
|
||||
'malandrina',
|
||||
'patrick',
|
||||
'alexgutjahr',
|
||||
'hongbowu',
|
||||
]
|
||||
export const MAX_RECORDS_LIMIT = 1000
|
||||
|
||||
export const createUser = async (input: {
|
||||
|
|
@ -71,7 +64,7 @@ export const createUser = async (input: {
|
|||
return Promise.reject({ errorCode: SignupErrorCode.InvalidUsername })
|
||||
}
|
||||
|
||||
const [user, profile] = await entityManager.transaction<[User, Profile]>(
|
||||
const [user, profile] = await appDataSource.transaction<[User, Profile]>(
|
||||
async (t) => {
|
||||
let hasInvite = false
|
||||
let invite: Invite | null = null
|
||||
|
|
@ -110,17 +103,29 @@ export const createUser = async (input: {
|
|||
})
|
||||
}
|
||||
|
||||
await createDefaultFiltersForUser(t)(user.id)
|
||||
|
||||
return [user, profile]
|
||||
}
|
||||
)
|
||||
|
||||
if (input.pendingConfirmation) {
|
||||
if (!(await sendConfirmationEmail(user))) {
|
||||
return Promise.reject({ errorCode: SignupErrorCode.InvalidEmail })
|
||||
}
|
||||
const customAttributes: { source_user_id: string } = {
|
||||
source_user_id: user.sourceUserId,
|
||||
}
|
||||
await IntercomClient?.contacts.createUser({
|
||||
email: user.email,
|
||||
externalId: user.id,
|
||||
name: user.name,
|
||||
avatar: profile.pictureUrl || undefined,
|
||||
customAttributes: customAttributes,
|
||||
signedUpAt: Math.floor(Date.now() / 1000),
|
||||
})
|
||||
|
||||
const pubsubClient = createPubSubClient()
|
||||
await pubsubClient.userCreated(
|
||||
user.id,
|
||||
user.email,
|
||||
user.name,
|
||||
profile.username
|
||||
)
|
||||
|
||||
analytics.track({
|
||||
userId: user.id,
|
||||
|
|
@ -132,10 +137,16 @@ export const createUser = async (input: {
|
|||
},
|
||||
})
|
||||
|
||||
if (input.pendingConfirmation) {
|
||||
if (!(await sendConfirmationEmail(user))) {
|
||||
return Promise.reject({ errorCode: SignupErrorCode.InvalidEmail })
|
||||
}
|
||||
}
|
||||
|
||||
return [user, profile]
|
||||
}
|
||||
|
||||
const createDefaultFiltersForUser =
|
||||
export const createDefaultFiltersForUser =
|
||||
(t: EntityManager) =>
|
||||
async (userId: string): Promise<Filter[]> => {
|
||||
const defaultFilters = [
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import * as jwt from 'jsonwebtoken'
|
||||
import { DeepPartial, FindOptionsWhere, IsNull, Not } from 'typeorm'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { Feature } from '../entity/feature'
|
||||
import { env } from '../env'
|
||||
import { entityManager, getRepository } from '../repository'
|
||||
import { getRepository } from '../repository'
|
||||
import { logger } from '../utils/logger'
|
||||
|
||||
export enum FeatureName {
|
||||
|
|
@ -41,7 +42,7 @@ const optInUltraRealisticVoice = async (uid: string): Promise<Feature> => {
|
|||
|
||||
const MAX_USERS = 1500
|
||||
// opt in to feature for the first 1500 users
|
||||
const optedInFeatures = (await entityManager.query(
|
||||
const optedInFeatures = (await appDataSource.query(
|
||||
`insert into omnivore.features (user_id, name, granted_at)
|
||||
select $1, $2, $3 from omnivore.features
|
||||
where name = $2 and granted_at is not null
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { nanoid } from 'nanoid'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { Group } from '../entity/groups/group'
|
||||
import { GroupMembership } from '../entity/groups/group_membership'
|
||||
import { Invite } from '../entity/groups/invite'
|
||||
|
|
@ -6,7 +7,7 @@ import { RuleActionType } from '../entity/rule'
|
|||
import { User } from '../entity/user'
|
||||
import { homePageURL } from '../env'
|
||||
import { RecommendationGroup, User as GraphqlUser } from '../generated/graphql'
|
||||
import { entityManager, getRepository } from '../repository'
|
||||
import { getRepository } from '../repository'
|
||||
import { userDataToUser } from '../utils/helpers'
|
||||
import { findOrCreateLabels } from './labels'
|
||||
import { createRule } from './rules'
|
||||
|
|
@ -21,7 +22,7 @@ export const createGroup = async (input: {
|
|||
onlyAdminCanPost?: boolean | null
|
||||
onlyAdminCanSeeMembers?: boolean | null
|
||||
}): Promise<[Group, Invite]> => {
|
||||
const [group, invite] = await entityManager.transaction<[Group, Invite]>(
|
||||
const [group, invite] = await appDataSource.transaction<[Group, Invite]>(
|
||||
async (t) => {
|
||||
// Max number of groups a user can create
|
||||
const maxGroups = 3
|
||||
|
|
@ -113,7 +114,7 @@ export const joinGroup = async (
|
|||
user: User,
|
||||
inviteCode: string
|
||||
): Promise<RecommendationGroup> => {
|
||||
const invite = await entityManager.transaction<Invite>(async (t) => {
|
||||
const invite = await appDataSource.transaction<Invite>(async (t) => {
|
||||
// Check if the invite exists
|
||||
const invite = await t
|
||||
.getRepository(Invite)
|
||||
|
|
@ -173,7 +174,7 @@ export const leaveGroup = async (
|
|||
user: User,
|
||||
groupId: string
|
||||
): Promise<boolean> => {
|
||||
return entityManager.transaction(async (t) => {
|
||||
return appDataSource.transaction(async (t) => {
|
||||
const group = await t
|
||||
.getRepository(Group)
|
||||
.createQueryBuilder('group')
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ import * as httpContext from 'express-http-context2'
|
|||
import { readFileSync } from 'fs'
|
||||
import path from 'path'
|
||||
import { DeepPartial, EntityManager } from 'typeorm'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { LibraryItem } from '../entity/library_item'
|
||||
import { PageType } from '../generated/graphql'
|
||||
import { authTrx, entityManager } from '../repository'
|
||||
import { authTrx } from '../repository'
|
||||
import { libraryItemRepository } from '../repository/library_item'
|
||||
import { generateSlug, stringToHash, wordsCount } from '../utils/helpers'
|
||||
import { logger } from '../utils/logger'
|
||||
|
|
@ -107,7 +108,7 @@ const addPopularReads = async (
|
|||
|
||||
export const addPopularReadsForNewUser = async (
|
||||
userId: string,
|
||||
em = entityManager
|
||||
em = appDataSource.manager
|
||||
): Promise<void> => {
|
||||
const defaultReads = ['omnivore_organize', 'power_read_it_later']
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import { AbuseReport } from '../entity/reports/abuse_report'
|
||||
import { ContentDisplayReport } from '../entity/reports/content_display_report'
|
||||
import { env } from '../env'
|
||||
import { ReportItemInput, ReportType } from '../generated/graphql'
|
||||
import { authTrx, getRepository } from '../repository'
|
||||
import { logger } from '../utils/logger'
|
||||
import { sendEmail } from '../utils/sendEmail'
|
||||
import { findLibraryItemById } from './library_item'
|
||||
|
||||
export const saveContentDisplayReport = async (
|
||||
|
|
@ -18,7 +20,7 @@ export const saveContentDisplayReport = async (
|
|||
// We capture the article content and original html now, in case it
|
||||
// reparsed or updated later, this gives us a view of exactly
|
||||
// what the user saw.
|
||||
const result = await getRepository(ContentDisplayReport).save({
|
||||
const report = await getRepository(ContentDisplayReport).save({
|
||||
user: { id: uid },
|
||||
content: item.readableContent,
|
||||
originalHtml: item.originalContent || undefined,
|
||||
|
|
@ -27,7 +29,23 @@ export const saveContentDisplayReport = async (
|
|||
libraryItemId: item.id,
|
||||
})
|
||||
|
||||
return !!result
|
||||
const message = `A new content display report was created by:
|
||||
${report.user.id} for URL: ${report.originalUrl}
|
||||
${report.reportComment}`
|
||||
|
||||
logger.info(message)
|
||||
|
||||
if (!env.dev.isLocal) {
|
||||
// If we are in the local environment, just log a message, otherwise email the report
|
||||
await sendEmail({
|
||||
to: env.sender.feedback,
|
||||
subject: 'New content display report',
|
||||
text: message,
|
||||
from: env.sender.message,
|
||||
})
|
||||
}
|
||||
|
||||
return !!report
|
||||
}
|
||||
|
||||
export const saveAbuseReport = async (
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
import axios from 'axios'
|
||||
import { appDataSource } from '../data_source'
|
||||
import { NewsletterEmail } from '../entity/newsletter_email'
|
||||
import { Subscription } from '../entity/subscription'
|
||||
import { SubscriptionStatus, SubscriptionType } from '../generated/graphql'
|
||||
import { authTrx, entityManager, getRepository } from '../repository'
|
||||
import { authTrx, getRepository } from '../repository'
|
||||
import { logger } from '../utils/logger'
|
||||
import { sendEmail } from '../utils/sendEmail'
|
||||
|
||||
|
|
@ -105,7 +106,7 @@ export const saveSubscription = async ({
|
|||
}
|
||||
|
||||
const existingSubscription = await getSubscriptionByName(name, userId)
|
||||
const result = await entityManager.transaction(async (tx) => {
|
||||
const result = await appDataSource.transaction(async (tx) => {
|
||||
if (existingSubscription) {
|
||||
// update subscription if already exists
|
||||
await tx
|
||||
|
|
|
|||
|
|
@ -74,11 +74,6 @@ interface BackendEnv {
|
|||
gcsUploadSAKeyFilePath: string
|
||||
gcsUploadPrivateBucket: string
|
||||
}
|
||||
elastic: {
|
||||
url: string
|
||||
username: string
|
||||
password: string
|
||||
}
|
||||
sender: {
|
||||
message: string
|
||||
feedback: string
|
||||
|
|
@ -144,8 +139,6 @@ const nullableEnvVars = [
|
|||
'GAUTH_SECRET',
|
||||
'SEGMENT_WRITE_KEY',
|
||||
'TWITTER_BEARER_TOKEN',
|
||||
'ELASTIC_USERNAME',
|
||||
'ELASTIC_PASSWORD',
|
||||
'GCS_UPLOAD_PRIVATE_BUCKET',
|
||||
'SENDER_MESSAGE',
|
||||
'SENDER_FEEDBACK',
|
||||
|
|
@ -267,11 +260,6 @@ export function getEnv(): BackendEnv {
|
|||
gcsUploadSAKeyFilePath: parse('GCS_UPLOAD_SA_KEY_FILE_PATH'),
|
||||
gcsUploadPrivateBucket: parse('GCS_UPLOAD_PRIVATE_BUCKET'),
|
||||
}
|
||||
const elastic = {
|
||||
url: parse('ELASTIC_URL'),
|
||||
username: parse('ELASTIC_USERNAME'),
|
||||
password: parse('ELASTIC_PASSWORD'),
|
||||
}
|
||||
const sender = {
|
||||
message: parse('SENDER_MESSAGE'),
|
||||
feedback: parse('SENDER_FEEDBACK'),
|
||||
|
|
@ -317,7 +305,6 @@ export function getEnv(): BackendEnv {
|
|||
dev,
|
||||
fileUpload,
|
||||
queue,
|
||||
elastic,
|
||||
sender,
|
||||
sendgrid,
|
||||
readwise,
|
||||
|
|
|
|||
|
|
@ -260,15 +260,17 @@ export const enqueueParseRequest = async ({
|
|||
|
||||
// If there is no Google Cloud Project Id exposed, it means that we are in local environment
|
||||
if (env.dev.isLocal || !GOOGLE_CLOUD_PROJECT) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios.post(env.queue.contentFetchUrl, payload).catch((error) => {
|
||||
logError(error)
|
||||
logger.error(
|
||||
`Error occurred while requesting local puppeteer-parse function\nPlease, ensure your function is set up properly and running using "yarn start" from the "/pkg/gcf/puppeteer-parse" folder`
|
||||
)
|
||||
})
|
||||
}, 0)
|
||||
if (env.queue.contentFetchUrl) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios.post(env.queue.contentFetchUrl, payload).catch((error) => {
|
||||
logError(error)
|
||||
logger.error(
|
||||
`Error occurred while requesting local puppeteer-parse function\nPlease, ensure your function is set up properly and running using "yarn start" from the "/pkg/gcf/puppeteer-parse" folder`
|
||||
)
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
|
|
@ -414,12 +416,14 @@ export const enqueueTextToSpeech = async ({
|
|||
const taskHandlerUrl = `${env.queue.textToSpeechTaskHandlerUrl}?token=${token}`
|
||||
// If there is no Google Cloud Project Id exposed, it means that we are in local environment
|
||||
if (env.dev.isLocal || !GOOGLE_CLOUD_PROJECT) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios.post(taskHandlerUrl, payload).catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
if (env.queue.textToSpeechTaskHandlerUrl) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios.post(taskHandlerUrl, payload).catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
const createdTasks = await createHttpTaskWithToken({
|
||||
|
|
@ -461,16 +465,18 @@ export const enqueueRecommendation = async (
|
|||
}
|
||||
// If there is no Google Cloud Project Id exposed, it means that we are in local environment
|
||||
if (env.dev.isLocal || !GOOGLE_CLOUD_PROJECT) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios
|
||||
.post(env.queue.recommendationTaskHandlerUrl, payload, {
|
||||
headers,
|
||||
})
|
||||
.catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
if (env.queue.recommendationTaskHandlerUrl) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios
|
||||
.post(env.queue.recommendationTaskHandlerUrl, payload, {
|
||||
headers,
|
||||
})
|
||||
.catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
|
|
@ -505,16 +511,18 @@ export const enqueueImportFromIntegration = async (
|
|||
}
|
||||
// If there is no Google Cloud Project Id exposed, it means that we are in local environment
|
||||
if (env.dev.isLocal || !GOOGLE_CLOUD_PROJECT) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios
|
||||
.post(`${env.queue.integrationTaskHandlerUrl}/import`, payload, {
|
||||
headers,
|
||||
})
|
||||
.catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
if (env.queue.integrationTaskHandlerUrl) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios
|
||||
.post(`${env.queue.integrationTaskHandlerUrl}/import`, payload, {
|
||||
headers,
|
||||
})
|
||||
.catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
return nanoid()
|
||||
}
|
||||
|
||||
|
|
@ -552,16 +560,18 @@ export const enqueueThumbnailTask = async (
|
|||
|
||||
// If there is no Google Cloud Project Id exposed, it means that we are in local environment
|
||||
if (env.dev.isLocal || !GOOGLE_CLOUD_PROJECT) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios
|
||||
.post(env.queue.thumbnailTaskHandlerUrl, payload, {
|
||||
headers,
|
||||
})
|
||||
.catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
if (env.queue.thumbnailTaskHandlerUrl) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios
|
||||
.post(env.queue.thumbnailTaskHandlerUrl, payload, {
|
||||
headers,
|
||||
})
|
||||
.catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
|
|
@ -599,16 +609,18 @@ export const enqueueRssFeedFetch = async (
|
|||
|
||||
// If there is no Google Cloud Project Id exposed, it means that we are in local environment
|
||||
if (env.dev.isLocal || !GOOGLE_CLOUD_PROJECT) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios
|
||||
.post(env.queue.rssFeedTaskHandlerUrl, payload, {
|
||||
headers,
|
||||
})
|
||||
.catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
if (env.queue.rssFeedTaskHandlerUrl) {
|
||||
// Calling the handler function directly.
|
||||
setTimeout(() => {
|
||||
axios
|
||||
.post(env.queue.rssFeedTaskHandlerUrl, payload, {
|
||||
headers,
|
||||
})
|
||||
.catch((error) => {
|
||||
logError(error)
|
||||
})
|
||||
}, 0)
|
||||
}
|
||||
return nanoid()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -401,7 +401,7 @@ const getJSONLdLinkMetadata = async (
|
|||
|
||||
return result
|
||||
} catch (error) {
|
||||
logger.error(`Unable to get JSONLD link of the article`, error)
|
||||
logger.error('Unable to get JSONLD link of the article')
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export const sendEmail = async (msg: MailDataRequired): Promise<boolean> => {
|
|||
const client = new MailService()
|
||||
if (!process.env.SENDGRID_MSGS_API_KEY) {
|
||||
if (env.dev.isLocal) {
|
||||
logger.error('SendGrid API key not set.\nSending email:', msg)
|
||||
logger.info('SendGrid API key not set.\nSending email:', msg)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
import { DeepPartial } from 'typeorm'
|
||||
import { SnakeNamingStrategy } from 'typeorm-naming-strategies'
|
||||
import { appDataSource } from '../src/data_source'
|
||||
import { Filter } from '../src/entity/filter'
|
||||
import { Label } from '../src/entity/label'
|
||||
|
|
@ -7,7 +6,7 @@ import { LibraryItem } from '../src/entity/library_item'
|
|||
import { Reminder } from '../src/entity/reminder'
|
||||
import { User } from '../src/entity/user'
|
||||
import { UserDeviceToken } from '../src/entity/user_device_tokens'
|
||||
import { entityManager, getRepository, setClaims } from '../src/repository'
|
||||
import { getRepository, setClaims } from '../src/repository'
|
||||
import { userRepository } from '../src/repository/user'
|
||||
import { createUser } from '../src/services/create_user'
|
||||
import { saveLabelsInLibraryItem } from '../src/services/labels'
|
||||
|
|
@ -27,13 +26,18 @@ export const createTestConnection = async (): Promise<void> => {
|
|||
logging: ['query', 'info'],
|
||||
entities: [__dirname + '/../src/entity/**/*{.js,.ts}'],
|
||||
subscribers: [__dirname + '/../src/events/**/*{.js,.ts}'],
|
||||
namingStrategy: new SnakeNamingStrategy(),
|
||||
logger: process.env.PG_LOGGER as
|
||||
| 'advanced-console'
|
||||
| 'simple-console'
|
||||
| 'file'
|
||||
| 'debug'
|
||||
| undefined,
|
||||
})
|
||||
await appDataSource.initialize()
|
||||
}
|
||||
|
||||
export const deleteFiltersFromUser = async (userId: string) => {
|
||||
await entityManager.transaction(async (t) => {
|
||||
await appDataSource.transaction(async (t) => {
|
||||
await setClaims(t, userId)
|
||||
const filterRepo = t.getRepository(Filter)
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ describe('Webhooks API', () => {
|
|||
}
|
||||
`
|
||||
|
||||
const res = await graphqlRequest(query, authToken)
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
|
||||
expect(res.body.data.webhook.webhook.id).to.eql(webhook.id)
|
||||
expect(res.body.data.webhook.webhook.url).to.eql(webhook.url)
|
||||
|
|
@ -108,7 +108,7 @@ describe('Webhooks API', () => {
|
|||
}
|
||||
`
|
||||
|
||||
const res = await graphqlRequest(query, authToken)
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
const webhooks = await findWebhooks(user.id)
|
||||
|
||||
expect(res.body.data.webhooks.webhooks).to.eql(
|
||||
|
|
@ -165,7 +165,7 @@ describe('Webhooks API', () => {
|
|||
})
|
||||
|
||||
it('should create a webhook', async () => {
|
||||
const res = await graphqlRequest(query, authToken)
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
|
||||
expect(res.body.data.setWebhook.webhook).to.be.an('object')
|
||||
expect(res.body.data.setWebhook.webhook.url).to.eql(webhookUrl)
|
||||
|
|
@ -195,7 +195,7 @@ describe('Webhooks API', () => {
|
|||
})
|
||||
|
||||
it('should update a webhook', async () => {
|
||||
const res = await graphqlRequest(query, authToken)
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
|
||||
expect(res.body.data.setWebhook.webhook).to.be.an('object')
|
||||
expect(res.body.data.setWebhook.webhook.url).to.eql(webhookUrl)
|
||||
|
|
@ -240,7 +240,7 @@ describe('Webhooks API', () => {
|
|||
})
|
||||
|
||||
it('should delete a webhook', async () => {
|
||||
const res = await graphqlRequest(query, authToken)
|
||||
const res = await graphqlRequest(query, authToken).expect(200)
|
||||
const webhook = await findWebhookById(webhookId, user.id)
|
||||
|
||||
expect(res.body.data.deleteWebhook.webhook).to.be.an('object')
|
||||
|
|
|
|||
|
|
@ -16,7 +16,9 @@ describe('/article/save API', () => {
|
|||
// We need to mock the pupeeteer-parse
|
||||
// service here because in dev mode the task gets
|
||||
// called immediately.
|
||||
nock(env.queue.contentFetchUrl).post('/').reply(200)
|
||||
if (env.queue.contentFetchUrl) {
|
||||
nock(env.queue.contentFetchUrl).post('/').reply(200)
|
||||
}
|
||||
|
||||
before(async () => {
|
||||
// create test user and login
|
||||
|
|
|
|||
Loading…
Reference in a new issue