upgrade typeorm to 3.0 (#359)

* upgrade typeorm to 3.0

* use new datasource object in typeorm 3

* fix tests

* fix tests

* migrate before creating connection

* fail the test if migration failed
This commit is contained in:
Hongbo Wu
2022-04-06 10:32:41 +08:00
committed by GitHub
parent 01001bc4a0
commit 7569e988bf
42 changed files with 357 additions and 394 deletions

View File

@ -75,8 +75,8 @@
"snake-case": "^3.0.3",
"supertest": "^6.2.2",
"ts-loader": "^8.0.3",
"typeorm": "^0.2.37",
"typeorm-naming-strategies": "^2.0.0",
"typeorm": "^0.3.4",
"typeorm-naming-strategies": "^4.1.0",
"urlsafe-base64": "^1.0.0",
"uuid": "^8.3.1",
"voca": "^1.4.0",

View File

@ -1,16 +1,15 @@
import {
Entity,
BaseEntity,
PrimaryGeneratedColumn,
CreateDateColumn,
OneToOne,
Entity,
JoinColumn,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm'
import { User } from './user'
@Entity({ name: 'user_friends' })
export class Follower extends BaseEntity {
export class Follower {
@PrimaryGeneratedColumn('uuid')
id?: string

View File

@ -1,18 +1,17 @@
import {
Entity,
BaseEntity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
Entity,
JoinColumn,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm'
import { User } from '../user'
@Entity()
export class Group extends BaseEntity {
export class Group {
@PrimaryGeneratedColumn('uuid')
id?: string

View File

@ -1,11 +1,10 @@
import {
Entity,
BaseEntity,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
Entity,
JoinColumn,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm'
import { User } from '../user'
@ -13,7 +12,7 @@ import { Group } from './group'
import { Invite } from './invite'
@Entity()
export class GroupMembership extends BaseEntity {
export class GroupMembership {
@PrimaryGeneratedColumn('uuid')
id?: string

View File

@ -1,19 +1,18 @@
import {
Entity,
BaseEntity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
Entity,
JoinColumn,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm'
import { User } from '../user'
import { Group } from './group'
@Entity()
export class Invite extends BaseEntity {
export class Invite {
@PrimaryGeneratedColumn('uuid')
id?: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -12,7 +11,7 @@ import { User } from './user'
import { Page } from './page'
@Entity({ name: 'highlight' })
export class Highlight extends BaseEntity {
export class Highlight {
@PrimaryGeneratedColumn('uuid')
id?: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -10,7 +9,7 @@ import {
import { User } from './user'
@Entity({ name: 'labels' })
export class Label extends BaseEntity {
export class Label {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -10,7 +10,6 @@
// shared_with_highlights | boolean | | | false
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -27,7 +26,7 @@ import { Page } from './page'
import { Label } from './label'
@Entity({ name: 'links' })
export class Link extends BaseEntity {
export class Link {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
CreateDateColumn,
Entity,
JoinColumn,
@ -10,7 +9,7 @@ import { Link } from './link'
import { Label } from './label'
@Entity({ name: 'link_labels' })
export class LinkLabel extends BaseEntity {
export class LinkLabel {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -11,7 +10,7 @@ import {
import { User } from './user'
@Entity({ name: 'newsletter_emails' })
export class NewsletterEmail extends BaseEntity {
export class NewsletterEmail {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -8,7 +7,7 @@ import {
} from 'typeorm'
@Entity({ name: 'pages' })
export class Page extends BaseEntity {
export class Page {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -1,18 +1,17 @@
import {
Entity,
BaseEntity,
Column,
PrimaryGeneratedColumn,
CreateDateColumn,
UpdateDateColumn,
OneToOne,
Entity,
JoinColumn,
OneToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm'
import { User } from './user'
@Entity({ name: 'user_profile' })
export class Profile extends BaseEntity {
export class Profile {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -11,7 +10,7 @@ import {
import { User } from './user'
@Entity({ name: 'reminders' })
export class Reminder extends BaseEntity {
export class Reminder {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -9,7 +8,7 @@ import {
import { ReportType } from '../../generated/graphql'
@Entity()
export class AbuseReport extends BaseEntity {
export class AbuseReport {
@PrimaryGeneratedColumn('uuid')
id?: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -8,7 +7,7 @@ import {
} from 'typeorm'
@Entity()
export class ContentDisplayReport extends BaseEntity {
export class ContentDisplayReport {
@PrimaryGeneratedColumn('uuid')
id?: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -11,7 +10,7 @@ import {
import { User } from './user'
@Entity({ name: 'upload_files' })
export class UploadFile extends BaseEntity {
export class UploadFile {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -14,7 +13,7 @@ import { Profile } from './profile'
import { Label } from './label'
@Entity()
export class User extends BaseEntity {
export class User {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -1,5 +1,4 @@
import {
BaseEntity,
Column,
CreateDateColumn,
Entity,
@ -10,7 +9,7 @@ import {
import { User } from './user'
@Entity({ name: 'user_device_tokens' })
export class UserDeviceToken extends BaseEntity {
export class UserDeviceToken {
@PrimaryGeneratedColumn('uuid')
id!: string

View File

@ -1,4 +1,5 @@
import { EntityManager } from 'typeorm'
import { EntityManager, EntityTarget, Repository } from 'typeorm'
import { AppDataSource } from '../server'
export const setClaims = async (
t: EntityManager,
@ -9,3 +10,7 @@ export const setClaims = async (
.query('SELECT * from omnivore.set_claims($1, $2)', [uid, dbRole])
.then()
}
export const getRepository = <T>(entity: EntityTarget<T>): Repository<T> => {
return AppDataSource.getRepository(entity)
}

View File

@ -32,8 +32,7 @@ export class FollowOmnivoreUser implements EntitySubscriberInterface<Profile> {
await event.manager
.getRepository(Follower)
.create({ user: event.entity.user, followee: omnivoreProfile.user })
.save()
.save({ user: event.entity.user, followee: omnivoreProfile.user })
await event.manager.query(
`insert into omnivore.links (user_id, article_id, article_url, article_hash, slug)

View File

@ -20,10 +20,11 @@ import { analytics } from '../../utils/analytics'
import { env } from '../../env'
import { User } from '../../entity/user'
import { Label } from '../../entity/label'
import { getManager, getRepository, ILike } from 'typeorm'
import { setClaims } from '../../entity/utils'
import { ILike, In } from 'typeorm'
import { getRepository, setClaims } from '../../entity/utils'
import { deleteLabelInPages, getPageById, updatePage } from '../../elastic'
import { createPubSubClient } from '../../datalayer/pubsub'
import { AppDataSource } from '../../server'
export const labelsResolver = authorized<LabelsSuccess, LabelsError>(
async (_obj, _params, { claims: { uid }, log }) => {
@ -38,7 +39,8 @@ export const labelsResolver = authorized<LabelsSuccess, LabelsError>(
})
try {
const user = await User.findOne(uid, {
const user = await getRepository(User).findOne({
where: { id: uid },
relations: ['labels'],
})
if (!user) {
@ -69,7 +71,7 @@ export const createLabelResolver = authorized<
const { name, color, description } = input
try {
const user = await getRepository(User).findOne(uid)
const user = await getRepository(User).findOneBy({ id: uid })
if (!user) {
return {
errorCodes: [CreateLabelErrorCode.Unauthorized],
@ -77,11 +79,9 @@ export const createLabelResolver = authorized<
}
// Check if label already exists ignoring case of name
const existingLabel = await getRepository(Label).findOne({
where: {
user,
name: ILike(name),
},
const existingLabel = await getRepository(Label).findOneBy({
user: { id: user.id },
name: ILike(name),
})
if (existingLabel) {
return {
@ -89,14 +89,12 @@ export const createLabelResolver = authorized<
}
}
const label = await getRepository(Label)
.create({
user,
name,
color,
description: description || '',
})
.save()
const label = await getRepository(Label).save({
user,
name,
color,
description: description || '',
})
analytics.track({
userId: uid,
@ -128,14 +126,15 @@ export const deleteLabelResolver = authorized<
log.info('deleteLabelResolver')
try {
const user = await getRepository(User).findOne(uid)
const user = await getRepository(User).findOneBy({ id: uid })
if (!user) {
return {
errorCodes: [DeleteLabelErrorCode.Unauthorized],
}
}
const label = await getRepository(Label).findOne(labelId, {
const label = await getRepository(Label).findOne({
where: { id: labelId },
relations: ['user'],
})
if (!label) {
@ -150,7 +149,7 @@ export const deleteLabelResolver = authorized<
}
}
const result = await getManager().transaction(async (t) => {
const result = await AppDataSource.transaction(async (t) => {
await setClaims(t, uid)
return t.getRepository(Label).delete(labelId)
})
@ -197,7 +196,7 @@ export const setLabelsResolver = authorized<
const { linkId: pageId, labelIds } = input
try {
const user = await getRepository(User).findOne(uid)
const user = await getRepository(User).findOneBy({ id: uid })
if (!user) {
return {
errorCodes: [SetLabelsErrorCode.Unauthorized],
@ -211,10 +210,8 @@ export const setLabelsResolver = authorized<
}
}
const labels = await getRepository(Label).findByIds(labelIds, {
where: {
user,
},
const labels = await getRepository(Label).find({
where: { id: In(labelIds), user: { id: user.id } },
relations: ['user'],
})
if (labels.length !== labelIds.length) {

View File

@ -1,15 +1,15 @@
import { authorized } from '../../utils/helpers'
import {
CreateNewsletterEmailSuccess,
CreateNewsletterEmailError,
CreateNewsletterEmailErrorCode,
NewsletterEmailsSuccess,
NewsletterEmailsError,
NewsletterEmailsErrorCode,
CreateNewsletterEmailSuccess,
DeleteNewsletterEmailError,
DeleteNewsletterEmailErrorCode,
DeleteNewsletterEmailSuccess,
DeleteNewsletterEmailError,
MutationDeleteNewsletterEmailArgs,
NewsletterEmailsError,
NewsletterEmailsErrorCode,
NewsletterEmailsSuccess,
} from '../../generated/graphql'
import {
createNewsletterEmail,
@ -19,6 +19,8 @@ import {
import { NewsletterEmail } from '../../entity/newsletter_email'
import { analytics } from '../../utils/analytics'
import { env } from '../../env'
import { AppDataSource } from '../../server'
import { User } from '../../entity/user'
export const createNewsletterEmailResolver = authorized<
CreateNewsletterEmailSuccess,
@ -55,7 +57,16 @@ export const newsletterEmailsResolver = authorized<
console.log('newsletterEmailsResolver')
try {
const newsletterEmails = await getNewsletterEmails(claims.uid)
const user = await AppDataSource.getRepository(User).findOneBy({
id: claims.uid,
})
if (!user) {
return Promise.reject({
errorCode: NewsletterEmailsErrorCode.Unauthorized,
})
}
const newsletterEmails = await getNewsletterEmails(user.id)
return {
newsletterEmails: newsletterEmails,
@ -84,10 +95,14 @@ export const deleteNewsletterEmailResolver = authorized<
})
try {
const newsletterEmail = await NewsletterEmail.findOne(
args.newsletterEmailId,
{ relations: ['user'] }
)
const newsletterEmail = await AppDataSource.getRepository(
NewsletterEmail
).findOne({
where: {
id: args.newsletterEmailId,
},
relations: ['user'],
})
if (!newsletterEmail) {
return {

View File

@ -19,7 +19,7 @@ import { articleRouter } from './routers/article_router'
import { mobileAuthRouter } from './routers/auth/mobile/mobile_auth_router'
import { contentServiceRouter } from './routers/svc/content'
import { localDebugRouter } from './routers/local_debug_router'
import { Connection, createConnection } from 'typeorm'
import { DataSource } from 'typeorm'
import { SnakeNamingStrategy } from 'typeorm-naming-strategies'
import { linkServiceRouter } from './routers/svc/links'
import UserModel from './datalayer/user'
@ -57,21 +57,19 @@ export const initModels = (kx: Knex, cache = true): DataModels => ({
reminder: new ReminderModel(kx, cache),
})
const initEntities = async (): Promise<Connection> => {
return createConnection({
type: 'postgres',
host: env.pg.host,
port: env.pg.port,
schema: 'omnivore',
username: env.pg.userName,
password: env.pg.password,
database: env.pg.dbName,
logging: ['query', 'info'],
entities: [__dirname + '/entity/**/*{.js,.ts}'],
subscribers: [__dirname + '/events/**/*{.js,.ts}'],
namingStrategy: new SnakeNamingStrategy(),
})
}
export const AppDataSource = new DataSource({
type: 'postgres',
host: env.pg.host,
port: env.pg.port,
schema: 'omnivore',
username: env.pg.userName,
password: env.pg.password,
database: env.pg.dbName,
logging: ['query', 'info'],
entities: [__dirname + '/entity/**/*{.js,.ts}'],
subscribers: [__dirname + '/events/**/*{.js,.ts}'],
namingStrategy: new SnakeNamingStrategy(),
})
export const createApp = (): {
app: Express
@ -126,7 +124,7 @@ const main = async (): Promise<void> => {
// If creating the DB entities fails, we want this to throw
// so the container will be restarted and not come online
// as healthy.
await initEntities()
await AppDataSource.initialize()
await initElasticsearch()

View File

@ -1,13 +1,13 @@
import { getManager } from 'typeorm'
import { Link } from '../entity/link'
import { setClaims } from '../entity/utils'
import { AppDataSource } from '../server'
export const setLinkArchived = async (
userId: string,
linkId: string,
archived: boolean
): Promise<void> => {
await getManager().transaction(async (t) => {
await AppDataSource.transaction(async (t) => {
await setClaims(t, userId)
await t.getRepository(Link).update(
{

View File

@ -1,9 +1,9 @@
import { getManager } from 'typeorm'
import { User } from '../entity/user'
import { Group } from '../entity/groups/group'
import { Invite } from '../entity/groups/invite'
import { GroupMembership } from '../entity/groups/group_membership'
import { nanoid } from 'nanoid'
import { AppDataSource } from '../server'
export const createGroup = async (input: {
admin: User
@ -11,15 +11,12 @@ export const createGroup = async (input: {
maxMembers?: number
expiresInDays?: number
}): Promise<[Group, Invite]> => {
const [group, invite] = await getManager().transaction<[Group, Invite]>(
const [group, invite] = await AppDataSource.transaction<[Group, Invite]>(
async (t) => {
const group = await t
.getRepository(Group)
.create({
name: input.name,
createdBy: input.admin,
})
.save()
const group = await t.getRepository(Group).save({
name: input.name,
createdBy: input.admin,
})
const code = nanoid(8)
const expirationTime = (() => {
@ -27,25 +24,19 @@ export const createGroup = async (input: {
r.setDate(r.getDate() + (input.expiresInDays || 7))
return r
})()
const invite = await t
.getRepository(Invite)
.create({
group,
code,
createdBy: input.admin,
maxMembers: input.maxMembers || 50,
expirationTime: expirationTime,
})
.save()
const invite = await t.getRepository(Invite).save({
group,
code,
createdBy: input.admin,
maxMembers: input.maxMembers || 50,
expirationTime: expirationTime,
})
// Add the admin to the group as its first user
await t
.getRepository(GroupMembership)
.create({
user: input.admin,
group,
invite,
})
.save()
await t.getRepository(GroupMembership).save({
user: input.admin,
group,
invite,
})
return [group, invite]
}
)

View File

@ -1,12 +1,14 @@
import { AuthProvider } from '../routers/auth/auth_types'
import { MembershipTier } from '../datalayer/user/model'
import { EntityManager, getManager, getRepository } from 'typeorm'
import { EntityManager } from 'typeorm'
import { User } from '../entity/user'
import { Profile } from '../entity/profile'
import { SignupErrorCode } from '../generated/graphql'
import { validateUsername } from '../utils/usernamePolicy'
import { Invite } from '../entity/groups/invite'
import { GroupMembership } from '../entity/groups/group_membership'
import { AppDataSource } from '../server'
import { getRepository } from '../entity/utils'
export const createUser = async (input: {
provider: AuthProvider
@ -28,15 +30,12 @@ export const createUser = async (input: {
}
// create profile if user exists but profile does not exist
const profile = await getManager()
.getRepository(Profile)
.create({
username: input.username,
pictureUrl: input.pictureUrl,
bio: input.bio,
user: existingUser,
})
.save()
const profile = await getRepository(Profile).save({
username: input.username,
pictureUrl: input.pictureUrl,
bio: input.bio,
user: existingUser,
})
return [existingUser, profile]
}
@ -45,10 +44,10 @@ export const createUser = async (input: {
return Promise.reject({ errorCode: SignupErrorCode.InvalidUsername })
}
const [user, profile] = await getManager().transaction<[User, Profile]>(
const [user, profile] = await AppDataSource.transaction<[User, Profile]>(
async (t) => {
let hasInvite = false
let invite: Invite | undefined = undefined
let invite: Invite | null = null
if (input.inviteCode) {
const inviteCodeRepo = t.getRepository(Invite)
@ -60,37 +59,28 @@ export const createUser = async (input: {
hasInvite = true
}
}
const user = await t
.getRepository(User)
.create({
source: input.provider,
membership:
input.membershipTier ||
(hasInvite ? MembershipTier.Beta : MembershipTier.WaitList),
name: input.name,
email: input.email,
sourceUserId: input.sourceUserId,
password: input.password,
})
.save()
const profile = await t
.getRepository(Profile)
.create({
username: input.username,
pictureUrl: input.pictureUrl,
bio: input.bio,
user,
})
.save()
const user = await t.getRepository(User).save({
source: input.provider,
membership:
input.membershipTier ||
(hasInvite ? MembershipTier.Beta : MembershipTier.WaitList),
name: input.name,
email: input.email,
sourceUserId: input.sourceUserId,
password: input.password,
})
const profile = await t.getRepository(Profile).save({
username: input.username,
pictureUrl: input.pictureUrl,
bio: input.bio,
user,
})
if (hasInvite && invite) {
await t
.getRepository(GroupMembership)
.create({
user: user,
invite: invite,
group: invite.group,
})
.save()
await t.getRepository(GroupMembership).save({
user: user,
invite: invite,
group: invite.group,
})
}
return [user, profile]
}
@ -119,7 +109,7 @@ const validateInvite = async (
return true
}
const getUser = async (email: string): Promise<User | undefined> => {
const getUser = async (email: string): Promise<User | null> => {
const userRepo = getRepository(User)
return userRepo.findOne({

View File

@ -1,6 +1,6 @@
import { getRepository } from 'typeorm'
import { User } from '../entity/user'
import { Follower } from '../entity/follower'
import { getRepository } from '../entity/utils'
export const getUserFollowers = async (
user: User,
@ -9,7 +9,7 @@ export const getUserFollowers = async (
): Promise<User[]> => {
return (
await getRepository(Follower).find({
where: { user: user },
where: { user: { id: user.id } },
relations: ['user', 'followee'],
skip: offset,
take: count,
@ -24,7 +24,7 @@ export const getUserFollowing = async (
): Promise<User[]> => {
return (
await getRepository(Follower).find({
where: { followee: user },
where: { followee: { id: user.id } },
relations: ['user', 'followee'],
skip: offset,
take: count,

View File

@ -1,10 +1,11 @@
import DataLoader from 'dataloader'
import { Label } from '../entity/label'
import { getRepository, ILike, In } from 'typeorm'
import { Link } from '../entity/link'
import { ILike, In } from 'typeorm'
import { PageContext } from '../elastic/types'
import { User } from '../entity/user'
import { addLabelInPage } from '../elastic'
import { getRepository } from '../entity/utils'
import { Link } from '../entity/link'
import DataLoader from 'dataloader'
const batchGetLabelsFromLinkIds = async (
linkIds: readonly string[]
@ -30,13 +31,16 @@ export const addLabelToPage = async (
description?: string
}
): Promise<boolean> => {
const user = await getRepository(User).findOne(ctx.uid)
const user = await getRepository(User).findOneBy({
id: ctx.uid,
})
if (!user) {
return false
}
let labelEntity = await getRepository(Label).findOne({
where: {
user: user,
name: ILike(label.name),
},
let labelEntity = await getRepository(Label).findOneBy({
user: { id: user.id },
name: ILike(label.name),
})
if (!labelEntity) {

View File

@ -1,9 +1,11 @@
import { getRepository } from 'typeorm'
import { NewsletterEmail } from '../entity/newsletter_email'
import { nanoid } from 'nanoid'
import { User } from '../entity/user'
import { CreateNewsletterEmailErrorCode } from '../generated/graphql'
import { env } from '../env'
import { AppDataSource } from '../server'
import { getRepository } from '../entity/utils'
import addressparser = require('nodemailer/lib/addressparser')
const parsedAddress = (emailAddress: string): string | undefined => {
@ -17,7 +19,8 @@ const parsedAddress = (emailAddress: string): string | undefined => {
export const createNewsletterEmail = async (
userId: string
): Promise<NewsletterEmail> => {
const user = await getRepository(User).findOne(userId, {
const user = await getRepository(User).findOne({
where: { id: userId },
relations: ['profile'],
})
if (!user) {
@ -28,19 +31,17 @@ export const createNewsletterEmail = async (
// generate a random email address with username prefix
const emailAddress = createRandomEmailAddress(user.profile.username, 8)
return getRepository(NewsletterEmail)
.create({
address: emailAddress,
user: user,
})
.save()
return getRepository(NewsletterEmail).save({
address: emailAddress,
user: user,
})
}
export const getNewsletterEmails = async (
userId: string
): Promise<NewsletterEmail[]> => {
return getRepository(NewsletterEmail).find({
where: { user: userId },
where: { user: { id: userId } },
order: { createdAt: 'DESC' },
})
}
@ -69,7 +70,7 @@ export const updateConfirmationCode = async (
export const getNewsletterEmail = async (
emailAddress: string
): Promise<NewsletterEmail | undefined> => {
): Promise<NewsletterEmail | null> => {
const address = parsedAddress(emailAddress)
return getRepository(NewsletterEmail)
.createQueryBuilder('newsletter_email')

View File

@ -1,8 +1,8 @@
import { getRepository } from 'typeorm'
import { ReportItemInput, ReportType } from '../generated/graphql'
import { ContentDisplayReport } from '../entity/reports/content_display_report'
import { AbuseReport } from '../entity/reports/abuse_report'
import { getPageById } from '../elastic'
import { getRepository } from '../entity/utils'
export const saveContentDisplayReport = async (
uid: string,
@ -20,16 +20,14 @@ 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 repo
.create({
userId: uid,
elasticPageId: input.pageId,
content: page.content,
originalHtml: page.originalHtml || undefined,
originalUrl: page.url,
reportComment: input.reportComment,
})
.save()
const result = await repo.save({
userId: uid,
elasticPageId: input.pageId,
content: page.content,
originalHtml: page.originalHtml || undefined,
originalUrl: page.url,
reportComment: input.reportComment,
})
return !!result
}
@ -55,16 +53,14 @@ export const saveAbuseReport = 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 repo
.create({
reportedBy: uid,
sharedBy: input.sharedBy,
elasticPageId: input.pageId,
itemUrl: input.itemUrl,
reportTypes: [ReportType.Abusive],
reportComment: input.reportComment,
})
.save()
const result = await repo.save({
reportedBy: uid,
sharedBy: input.sharedBy,
elasticPageId: input.pageId,
itemUrl: input.itemUrl,
reportTypes: [ReportType.Abusive],
reportComment: input.reportComment,
})
return !!result
}

View File

@ -1,34 +1,36 @@
import { getManager, getRepository } from 'typeorm'
import { UserDeviceToken } from '../entity/user_device_tokens'
import { User } from '../entity/user'
import { SetDeviceTokenErrorCode } from '../generated/graphql'
import { setClaims } from '../entity/utils'
import { getRepository, setClaims } from '../entity/utils'
import { analytics } from '../utils/analytics'
import { env } from '../env'
import { AppDataSource } from '../server'
export const getDeviceToken = async (
id: string
): Promise<UserDeviceToken | undefined> => {
return getRepository(UserDeviceToken).findOne(id)
): Promise<UserDeviceToken | null> => {
return getRepository(UserDeviceToken).findOneBy({ id })
}
export const getDeviceTokenByToken = async (
token: string
): Promise<UserDeviceToken | undefined> => {
return getRepository(UserDeviceToken).findOne({ token })
): Promise<UserDeviceToken | null> => {
return getRepository(UserDeviceToken).findOneBy({ token })
}
export const getDeviceTokensByUserId = async (
userId: string
): Promise<UserDeviceToken[] | undefined> => {
return getRepository(UserDeviceToken).find({ where: { user: userId } })
return getRepository(UserDeviceToken).find({
where: { user: { id: userId } },
})
}
export const createDeviceToken = async (
userId: string,
token: string
): Promise<UserDeviceToken> => {
const user = await getRepository(User).findOne(userId)
const user = await getRepository(User).findOneBy({ id: userId })
if (!user) {
return Promise.reject({
errorCode: SetDeviceTokenErrorCode.Unauthorized,
@ -43,19 +45,17 @@ export const createDeviceToken = async (
},
})
return getRepository(UserDeviceToken)
.create({
token: token,
user: user,
})
.save()
return getRepository(UserDeviceToken).save({
token: token,
user: user,
})
}
export const deleteDeviceToken = async (
id: string,
userId: string
): Promise<boolean> => {
const user = await getRepository(User).findOne(userId)
const user = await getRepository(User).findOneBy({ id: userId })
if (!user) {
return Promise.reject({
errorCode: SetDeviceTokenErrorCode.Unauthorized,
@ -70,7 +70,7 @@ export const deleteDeviceToken = async (
},
})
return getManager().transaction(async (t) => {
return AppDataSource.transaction(async (t) => {
await setClaims(t, userId)
const result = await t.getRepository(UserDeviceToken).delete(id)

View File

@ -1,13 +1,5 @@
import {
createConnection,
getConnection,
getManager,
getRepository,
} from 'typeorm'
import { SnakeNamingStrategy } from 'typeorm-naming-strategies'
import Postgrator from 'postgrator'
import { User } from '../src/entity/user'
import { createUser } from '../src/services/create_user'
import { Profile } from '../src/entity/profile'
import { Page } from '../src/entity/page'
import { Link } from '../src/entity/link'
@ -15,6 +7,10 @@ import { Reminder } from '../src/entity/reminder'
import { NewsletterEmail } from '../src/entity/newsletter_email'
import { UserDeviceToken } from '../src/entity/user_device_tokens'
import { Label } from '../src/entity/label'
import { AppDataSource } from '../src/server'
import { getRepository } from '../src/entity/utils'
import { createUser } from '../src/services/create_user'
import { SnakeNamingStrategy } from 'typeorm-naming-strategies'
const runMigrations = async () => {
const migrationDirectory = __dirname + '/../../db/migrations'
@ -43,8 +39,10 @@ const runMigrations = async () => {
}
}
const createEntityConnection = async (): Promise<void> => {
await createConnection({
export const createTestConnection = async (): Promise<void> => {
await runMigrations()
AppDataSource.setOptions({
type: 'postgres',
host: process.env.PG_HOST,
port: Number(process.env.PG_PORT),
@ -57,21 +55,11 @@ const createEntityConnection = async (): Promise<void> => {
subscribers: [__dirname + '/../src/events/**/*{.js,.ts}'],
namingStrategy: new SnakeNamingStrategy(),
})
}
export const createTestConnection = async (): Promise<void> => {
try {
getConnection()
// eslint-disable-next-line no-empty
} catch (error) {}
await runMigrations()
await createEntityConnection()
await AppDataSource.initialize()
}
export const deleteTestUser = async (name: string) => {
await getConnection()
.createQueryBuilder()
await AppDataSource.createQueryBuilder()
.delete()
.from(User)
.where({ email: `${name}@fake.com` })
@ -93,68 +81,58 @@ export const createTestUser = async (
inviteCode: invite,
password: password,
})
return newUser
}
export const createUserWithoutProfile = async (name: string): Promise<User> => {
return getManager()
.getRepository(User)
.create({
source: 'GOOGLE',
sourceUserId: 'fake-user-id-' + name,
email: `${name}@fake.com`,
name: name,
})
.save()
return getRepository(User).save({
source: 'GOOGLE',
sourceUserId: 'fake-user-id-' + name,
email: `${name}@fake.com`,
name: name,
})
}
export const getProfile = async (user: User): Promise<Profile | undefined> => {
return Profile.findOne({ where: { user: user } })
export const getProfile = async (user: User): Promise<Profile | null> => {
return getRepository(Profile).findOneBy({ user: { id: user.id } })
}
export const createTestPage = async (): Promise<Page> => {
return getRepository(Page)
.create({
originalHtml: 'html',
content: 'Test content',
description: 'Test description',
title: 'Test title',
author: 'Test author',
url: 'Test url',
hash: 'Test hash',
})
.save()
return getRepository(Page).save({
originalHtml: 'html',
content: 'Test content',
description: 'Test description',
title: 'Test title',
author: 'Test author',
url: 'Test url',
hash: 'Test hash',
})
}
export const createTestLink = async (user: User, page: Page): Promise<Link> => {
return getRepository(Link)
.create({
user: user,
page: page,
slug: 'Test slug',
articleUrl: 'Test url',
articleHash: 'Test hash',
})
.save()
return getRepository(Link).save({
user: user,
page: page,
slug: 'Test slug',
articleUrl: 'Test url',
articleHash: 'Test hash',
})
}
export const createTestReminder = async (
user: User,
link?: string
): Promise<Reminder> => {
return getRepository(Reminder)
.create({
user: user,
link: link,
remindAt: new Date(),
})
.save()
return getRepository(Reminder).save({
user: user,
link: link,
remindAt: new Date(),
})
}
export const getReminder = async (
id: string
): Promise<Reminder | undefined> => {
return getRepository(Reminder).findOne(id)
export const getReminder = async (id: string): Promise<Reminder | null> => {
return getRepository(Reminder).findOneBy({ id })
}
export const createTestNewsletterEmail = async (
@ -162,44 +140,40 @@ export const createTestNewsletterEmail = async (
emailAddress?: string,
confirmationCode?: string
): Promise<NewsletterEmail> => {
return getRepository(NewsletterEmail)
.create({
user: user,
address: emailAddress,
confirmationCode: confirmationCode,
})
.save()
return getRepository(NewsletterEmail).save({
user: user,
address: emailAddress,
confirmationCode: confirmationCode,
})
}
export const getNewsletterEmail = async (
id: string
): Promise<NewsletterEmail | undefined> => {
return getRepository(NewsletterEmail).findOne(id)
): Promise<NewsletterEmail | null> => {
return getRepository(NewsletterEmail).findOneBy({ id })
}
export const createTestDeviceToken = async (
user: User
): Promise<UserDeviceToken> => {
return getRepository(UserDeviceToken)
.create({
user: user,
token: 'Test token',
})
.save()
return getRepository(UserDeviceToken).save({
user: user,
token: 'Test token',
})
}
export const getDeviceToken = async (
id: string
): Promise<UserDeviceToken | undefined> => {
return getRepository(UserDeviceToken).findOne(id)
): Promise<UserDeviceToken | null> => {
return getRepository(UserDeviceToken).findOneBy({ id })
}
export const getUser = async (id: string): Promise<User | undefined> => {
return getRepository(User).findOne(id)
export const getUser = async (id: string): Promise<User | null> => {
return getRepository(User).findOneBy({ id })
}
export const getLink = async (id: string): Promise<Link | undefined> => {
return getRepository(Link).findOne(id)
export const getLink = async (id: string): Promise<Link | null> => {
return getRepository(Link).findOneBy({ id })
}
export const createTestLabel = async (
@ -207,11 +181,9 @@ export const createTestLabel = async (
name: string,
color: string
): Promise<Label> => {
return getRepository(Label)
.create({
user: user,
name: name,
color: color,
})
.save()
return getRepository(Label).save({
user: user,
name: name,
color: color,
})
}

View File

@ -1,6 +1,6 @@
import { getConnection } from 'typeorm'
import { AppDataSource } from '../src/server'
export const mochaGlobalTeardown = async () => {
await getConnection().close()
await AppDataSource.destroy()
console.log('db connection closed')
}

View File

@ -1,12 +1,5 @@
import { createTestUser, deleteTestUser, getProfile, getUser } from '../db'
import { createTestUser, deleteTestUser } from '../db'
import { graphqlRequest, request } from '../util'
import { expect } from 'chai'
import {
LoginErrorCode,
SignupErrorCode,
UpdateUserErrorCode,
UpdateUserProfileErrorCode,
} from '../../src/generated/graphql'
import { User } from '../../src/entity/user'
import { hashPassword } from '../../src/utils/auth'
import 'mocha'
@ -33,7 +26,7 @@ describe('Sanitize Directive', () => {
})
describe('Update user with a bio that is too long', () => {
let bio = "".padStart(500, '*');
let bio = ''.padStart(500, '*')
let query: string
beforeEach(() => {

View File

@ -19,9 +19,9 @@ import {
} from '../../src/elastic'
import { PageType, UploadFileStatus } from '../../src/generated/graphql'
import { Page, PageContext } from '../../src/elastic/types'
import { getRepository } from 'typeorm'
import { UploadFile } from '../../src/entity/upload_file'
import { createPubSubClient } from '../../src/datalayer/pubsub'
import { getRepository } from '../../src/entity/utils'
chai.use(chaiString)

View File

@ -7,11 +7,11 @@ import {
} from '../util'
import { Label } from '../../src/entity/label'
import { expect } from 'chai'
import { getRepository } from 'typeorm'
import 'mocha'
import { User } from '../../src/entity/user'
import { Page } from '../../src/elastic/types'
import { getPageById } from '../../src/elastic'
import { getRepository } from '../../src/entity/utils'
describe('Labels API', () => {
const username = 'fakeUser'
@ -76,7 +76,9 @@ describe('Labels API', () => {
it('should return labels', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
const labels = await getRepository(Label).find({ where: { user } })
const labels = await getRepository(Label).findBy({
user: { id: user.id },
})
expect(res.body.data.labels.labels).to.eql(
labels.map((label) => ({
id: label.id,
@ -137,9 +139,9 @@ describe('Labels API', () => {
it('should create label', async () => {
const res = await graphqlRequest(query, authToken).expect(200)
const label = await getRepository(Label).findOne(
res.body.data.createLabel.label.id
)
const label = await getRepository(Label).findOneBy({
id: res.body.data.createLabel.label.id,
})
expect(label).to.exist
})
})
@ -203,7 +205,7 @@ describe('Labels API', () => {
it('should delete label', async () => {
await graphqlRequest(query, authToken).expect(200)
const label = await getRepository(Label).findOne(labelId)
const label = await getRepository(Label).findOneBy({ id: labelId })
expect(label).to.not.exist
})
})

View File

@ -164,7 +164,7 @@ describe('Newsletters API', () => {
const newsletterEmail = await getNewsletterEmail(
response.body.data.deleteNewsletterEmail.newsletterEmail.id
)
expect(newsletterEmail).to.be.undefined
expect(newsletterEmail).to.be.null
})
})

View File

@ -3,9 +3,9 @@ import { Page } from '../../src/elastic/types'
import { createTestUser, deleteTestUser } from '../db'
import { createTestElasticPage, graphqlRequest, request } from '../util'
import { ReportType } from '../../src/generated/graphql'
import { getRepository } from 'typeorm'
import { ContentDisplayReport } from '../../src/entity/reports/content_display_report'
import { expect } from 'chai'
import { getRepository } from '../../src/entity/utils'
describe('Report API', () => {
const username = 'fakeUser'
@ -70,7 +70,7 @@ describe('Report API', () => {
await graphqlRequest(query, authToken).expect(200)
expect(
await getRepository(ContentDisplayReport).find({
await getRepository(ContentDisplayReport).findBy({
elasticPageId: pageId,
})
).to.exist

View File

@ -75,7 +75,7 @@ describe('Device tokens API', () => {
const deviceToken = await getDeviceToken(
response.body.data.setDeviceToken.deviceToken.id
)
expect(deviceToken).to.be.undefined
expect(deviceToken).to.be.null
})
})
@ -105,7 +105,7 @@ describe('Device tokens API', () => {
const deviceToken = await getDeviceToken(
response.body.data.setDeviceToken.deviceToken.id
)
expect(deviceToken).not.to.be.undefined
expect(deviceToken).not.to.be.null
})
})

View File

@ -10,8 +10,8 @@ import { SharedArticleErrorCode } from '../../src/generated/graphql'
import { Page } from '../../src/entity/page'
import { Link } from '../../src/entity/link'
import { Highlight } from '../../src/entity/highlight'
import { getRepository } from 'typeorm'
import 'mocha'
import { getRepository } from '../../src/entity/utils'
describe('User feed article API', () => {
const existingUsername = 'fakeUser'

View File

@ -1,7 +1,6 @@
import 'mocha'
import { expect } from 'chai'
import 'chai/register-should'
import { labelsLoader } from '../../src/services/labels'
import {
createTestLabel,
createTestLink,
@ -9,10 +8,11 @@ import {
createTestUser,
deleteTestUser,
} from '../db'
import { getRepository } from 'typeorm'
import { LinkLabel } from '../../src/entity/link_label'
import { Label } from '../../src/entity/label'
import { Link } from '../../src/entity/link'
import { labelsLoader } from '../../src/services/labels'
import { getRepository } from '../../src/entity/utils'
describe('batch get labels from linkIds', () => {
let username = 'testUser'

View File

@ -6663,11 +6663,6 @@
resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.29.11.tgz#d654a112973f5e004bf8438122bd7e56a8e5cd7e"
integrity sha512-9cwk3c87qQKZrT251EDoibiYRILjCmxBvvcb4meofCmx1vdnNcR9gyildy5vOHASpOKMsn42CugxUvcwK5eu1g==
"@types/zen-observable@0.8.3":
version "0.8.3"
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.3.tgz#781d360c282436494b32fe7d9f7f8e64b3118aa3"
integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==
"@typescript-eslint/eslint-plugin@^5.9.0":
version "5.9.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.0.tgz#382182d5cb062f52aac54434cfc47c28898c8006"
@ -9167,6 +9162,11 @@ date-fns@^2.16.1:
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.23.0.tgz#4e886c941659af0cf7b30fafdd1eaa37e88788a9"
integrity sha512-5ycpauovVyAk0kXNZz6ZoB9AYMZB4DObse7P3BPWmyEjXNORTI8EJ6X0uaSAq4sCHzM1uajzrkr6HnsLQpxGXA==
date-fns@^2.28.0:
version "2.28.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.28.0.tgz#9570d656f5fc13143e50c975a3b6bbeb46cd08b2"
integrity sha512-8d35hViGYx/QH0icHYCeLmsLmMUheMmTyV9Fcm6gvNwdw31yXXH+O85sOBJ+OLnLQMKZowvpKb6FgMIQjcpvQw==
dateformat@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
@ -9649,6 +9649,11 @@ dotenv@^10.0.0:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81"
integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==
dotenv@^16.0.0:
version "16.0.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.0.0.tgz#c619001253be89ebb638d027b609c75c26e47411"
integrity sha512-qD9WU0MPM4SWLPJy/r2Be+2WgQj8plChsyrCNQzW/0WjvcJQiKQJ9mH3ZgB3fxbUUxgc/11ZJ0Fi5KiimWGz2Q==
dotenv@^8.2.0:
version "8.2.0"
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
@ -11120,6 +11125,18 @@ glob@7.1.7, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
once "^1.3.0"
path-is-absolute "^1.0.0"
glob@^7.2.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.0.4"
once "^1.3.0"
path-is-absolute "^1.0.0"
global-dirs@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-3.0.0.tgz#70a76fe84ea315ab37b1f5576cbde7d48ef72686"
@ -18646,7 +18663,7 @@ tslib@^1.0.0, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@~2.3.0:
tslib@^2, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@^2.3.1, tslib@~2.3.0:
version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
@ -18754,33 +18771,33 @@ typedarray@^0.0.6:
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typeorm-naming-strategies@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/typeorm-naming-strategies/-/typeorm-naming-strategies-2.0.0.tgz#c7c10bc768ddce2592ef9ad4d2dca55fd5fa6ad6"
integrity sha512-nsJ5jDjhBBEG6olFmxojkO4yrW7hEv38sH7ZXWWx9wnDoo9uaoH/mo2mBYAh/VKgwoFHBLu+CYxGmzXz2GUMcA==
typeorm-naming-strategies@^4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/typeorm-naming-strategies/-/typeorm-naming-strategies-4.1.0.tgz#1ec6eb296c8d7b69bb06764d5b9083ff80e814a9"
integrity sha512-vPekJXzZOTZrdDvTl1YoM+w+sUIfQHG4kZTpbFYoTsufyv9NIBRe4Q+PdzhEAFA2std3D9LZHEb1EjE9zhRpiQ==
typeorm@^0.2.37:
version "0.2.45"
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.45.tgz#e5bbb3af822dc4646bad96cfa48cd22fa4687cea"
integrity sha512-c0rCO8VMJ3ER7JQ73xfk0zDnVv0WDjpsP6Q1m6CVKul7DB9iVdWLRjPzc8v2eaeBuomsbZ2+gTaYr8k1gm3bYA==
typeorm@^0.3.4:
version "0.3.4"
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.3.4.tgz#6608f7efb15c40f3fa2863cefb45ff78a208c40c"
integrity sha512-6v3HH12viDhIQwQDod/B0Plt1o7IYIVDxP7zwatD6fzN+IDdqTTinW/sWNw84Edpbhh2t7XILTaQEqj0NXFP/Q==
dependencies:
"@sqltools/formatter" "^1.2.2"
app-root-path "^3.0.0"
buffer "^6.0.3"
chalk "^4.1.0"
cli-highlight "^2.1.11"
debug "^4.3.1"
dotenv "^8.2.0"
glob "^7.1.6"
js-yaml "^4.0.0"
date-fns "^2.28.0"
debug "^4.3.3"
dotenv "^16.0.0"
glob "^7.2.0"
js-yaml "^4.1.0"
mkdirp "^1.0.4"
reflect-metadata "^0.1.13"
sha.js "^2.4.11"
tslib "^2.1.0"
tslib "^2.3.1"
uuid "^8.3.2"
xml2js "^0.4.23"
yargs "^17.0.1"
zen-observable-ts "^1.0.0"
yargs "^17.3.1"
typescript@^2.9.2:
version "2.9.2"
@ -19812,7 +19829,7 @@ yargs@^15.0.2, yargs@^15.3.1:
y18n "^4.0.0"
yargs-parser "^18.1.2"
yargs@^17.0.0, yargs@^17.0.1:
yargs@^17.0.0:
version "17.3.1"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9"
integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==
@ -19825,6 +19842,19 @@ yargs@^17.0.0, yargs@^17.0.1:
y18n "^5.0.5"
yargs-parser "^21.0.0"
yargs@^17.3.1:
version "17.4.0"
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.0.tgz#9fc9efc96bd3aa2c1240446af28499f0e7593d00"
integrity sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==
dependencies:
cliui "^7.0.2"
escalade "^3.1.1"
get-caller-file "^2.0.5"
require-directory "^2.1.1"
string-width "^4.2.3"
y18n "^5.0.5"
yargs-parser "^21.0.0"
yauzl@^2.10.0:
version "2.10.0"
resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
@ -19853,16 +19883,3 @@ yup@^0.31.0:
lodash-es "^4.17.11"
property-expr "^2.0.4"
toposort "^2.0.2"
zen-observable-ts@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz#2d1aa9d79b87058e9b75698b92791c1838551f83"
integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==
dependencies:
"@types/zen-observable" "0.8.3"
zen-observable "0.8.15"
zen-observable@0.8.15:
version "0.8.15"
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==