Fixed some tests

This commit is contained in:
Amit Jakubowicz 2019-11-07 16:18:22 +01:00
parent 91329860de
commit e61ed72dc9
16 changed files with 369 additions and 130 deletions

5
.prettierrc Normal file
View File

@ -0,0 +1,5 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": false
}

63
@types/graphql.d.ts vendored
View File

@ -70,7 +70,7 @@ declare namespace GQL {
status: any;
location: ILocation;
occurrences: Array<IEventOccurrence | null> | null;
meta: IEventMeta | null;
tags: Array<IEventTag | null> | null;
}
interface IInfoOnCalendarEventArguments {
@ -107,9 +107,17 @@ declare namespace GQL {
end: string;
}
interface IEventMeta {
__typename: 'EventMeta';
tags: Array<string | null>;
interface IEventTag {
__typename: 'EventTag';
ID: string;
name: string;
translations: Array<IEventTagTranslation>;
}
interface IEventTagTranslation {
__typename: 'EventTagTranslation';
language: string;
text: string;
}
interface IUserRole {
@ -144,6 +152,8 @@ declare namespace GQL {
createEvent: ICalendarEvent | null;
updateEvent: ICalendarEvent | null;
deleteEvent: IUser;
createEventTag: IEventTag | null;
modifyEventTag: IEventTag | null;
}
interface ISignupOnMutationArguments {
@ -178,6 +188,14 @@ declare namespace GQL {
id: string;
}
interface ICreateEventTagOnMutationArguments {
input: ICreateEventTagInput;
}
interface IModifyEventTagOnMutationArguments {
input: IModifyEventTagInput;
}
interface ISignupInput {
email: string;
username: string;
@ -215,7 +233,7 @@ declare namespace GQL {
time: IEventTimeInput;
infos: Array<IEventInformationInput | null>;
location: IEventLocationInput;
meta: IEventMetaInput;
tagNames: Array<string>;
status: string;
}
@ -238,19 +256,44 @@ declare namespace GQL {
name?: string | null;
}
interface IEventMetaInput {
tags: Array<string | null>;
}
interface IUpdateEventInput {
id: string;
time?: IEventTimeInput | null;
infos?: Array<IEventInformationInput> | null;
location?: IEventLocationInput | null;
meta?: IEventMetaInput | null;
tagNames: Array<string>;
status?: string | null;
}
interface ICreateEventTagInput {
name: string;
translations: Array<ICreateModifyEventTagTranslationInput>;
}
interface ICreateModifyEventTagTranslationInput {
language: string;
text: string;
}
interface IModifyEventTagInput {
id: string;
name: string;
translations: Array<ICreateModifyEventTagTranslationInput>;
}
interface ITag {
__typename: 'Tag';
id: string;
name: string;
translations: Array<any>;
}
interface ITagTranslation {
__typename: 'TagTranslation';
language: string;
text: string;
}
interface IRevokeRoleInput {
userId: string;
roleType: any;

View File

@ -32,8 +32,8 @@
"ramda": "^0.26.1",
"random-string": "^0.2.0",
"rrule": "^2.6.2",
"typeorm": "^0.2.18",
"typescript": "^3.5.3",
"typeorm": "^0.2.20",
"typescript": "^3.7.2",
"uuid": "^3.3.2"
},
"devDependencies": {
@ -47,8 +47,7 @@
"prettier": "^1.18.2",
"ts-jest": "^24.0.2",
"ts-node": "^8.3.0",
"tslint": "^5.18.0",
"typescript": "^3.5.3"
"tslint": "^5.18.0"
},
"jest": {
"globalSetup": "./jest-setup.js",

View File

@ -18,6 +18,7 @@ describe('Authentication', () => {
connection = await createConnection({
...testConfig,
logging: null
})
})
@ -27,7 +28,8 @@ describe('Authentication', () => {
const server = await createServer({
typeormConnection: connection,
sendEmail: sendEmailMock as PostOffice,
domain: 'example.com'
domain: 'example.com',
sessionManager: jest.fn(() => Promise.resolve(true)) as any
})
testClient = await createTestClient(server as any)
})

View File

@ -9,16 +9,7 @@ import {
import {User} from "../Auth/User.entity"
import {DateTime} from "luxon"
import {rrulestr} from "rrule"
export const toUTC = (isoTime: string, ianaTZ: string): DateTime => {
const parsed = DateTime.fromISO(isoTime, {zone: ianaTZ})
if (parsed.invalidReason) {
throw new Error(
`${parsed.invalidReason}: ${isoTime} with time-zone: ${ianaTZ}`
)
}
return parsed
}
import { EventTag } from "./EventTag.entity";
export const breakTime = (isoString: string) => {
const tSplit = isoString.split('T')
@ -58,11 +49,6 @@ export class EventTime {
exceptions?: string
}
export class EventMeta {
@Column({type: "varchar", array: true})
tags: string[]
}
@Entity()
export class Event extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
@ -84,8 +70,8 @@ export class Event extends BaseEntity {
@Column(type => EventTime)
time: EventTime
@Column(type => EventMeta)
meta: EventMeta
@OneToMany(type => EventTag, tag => tag.events)
tags: Promise<EventTag[]>
@Column({
default: "confirmed"
@ -95,7 +81,7 @@ export class Event extends BaseEntity {
@Column(type => EventLocation)
location: EventLocation
getOccurrences() {
getOccurrences(): EventOccurrence[] {
const occurences = []
console.log('this.time.recurrence', this.time.recurrence)
if (!this.time.recurrence) {

View File

@ -0,0 +1,32 @@
import { BaseEntity, Column, Entity, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Event } from "./Event.entity";
@Entity()
export class EventTag extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id: number
@Column({type: "varchar", unique: true})
name: string
@ManyToMany(type => Event, event => event.tags)
events: Promise<Event[]>
@OneToMany(type => EventTagTranslation, translation => translation.tag)
translations: Promise<EventTagTranslation[]>
}
@Entity()
export class EventTagTranslation extends BaseEntity {
@PrimaryGeneratedColumn("uuid")
id: number
@ManyToOne(type => EventTag, tag => tag.translations)
tag: Promise<EventTag>
@Column("varchar")
language: string
@Column("varchar")
text: string
}

View File

@ -1,18 +1,7 @@
import { createConnection } from "typeorm"
import { User } from "../../Auth/User.entity"
import { Event, EventInformation, EventOccurrence } from "../Event.entity"
import { DateTime } from "luxon"
import testConfig from "../../__tests__/testORMConfig";
const toUTC = (isoTime: string, ianaTZ: string) => {
const parsed = DateTime.fromISO(isoTime, { zone: ianaTZ })
if (parsed.invalidReason) {
throw new Error(
`${parsed.invalidReason}: ${isoTime} with time-zone: ${ianaTZ}`
)
}
return parsed.toUTC().toISO()
}
import testConfig from "../../__tests__/testORMConfig"
let owner: User = null
@ -20,7 +9,7 @@ let connection
describe("Recurring events", () => {
beforeAll(async () => {
connection = await createConnection({
...testConfig
...testConfig,
})
owner = new User()
owner.username = "onetimeguy"
@ -39,27 +28,26 @@ describe("Recurring events", () => {
info.title = "Test one time event"
info.description = "This is a test event that has no repetition rules"
event.owner = owner
event.owner = Promise.resolve(owner)
event.time = {
timeZone: "Europe/Madrid",
start: "2019-02-01T10:00Z",
end: "2019-02-01T11:00Z"
end: "2019-02-01T11:00Z",
}
await event.save()
await event.updateOccurrences()
event.occurrences = Promise.resolve(event.getOccurrences())
await event.save()
const occurences = await EventOccurrence.find({
where: { eventId: event.id }
where: { eventId: event.id },
})
expect(occurences).toHaveLength(1)
expect((await occurences[0].event).time.timeZone).toEqual(event.time.timeZone)
expect((await occurences[0].event).time.timeZone).toEqual(
event.time.timeZone
)
done()
})
it("Event happening once a week", () => {
it("Event happening once a week", async () => {
const event = new Event()
const info = new EventInformation()
info.language = "en"
@ -67,7 +55,7 @@ describe("Recurring events", () => {
info.description = "This event takes place every monday at 2pm"
event.infos = Promise.resolve([info])
event.owner = owner
event.owner = Promise.resolve(owner)
// from 2019-03-04 to 2019-03-25 every monday
// March 2019
@ -84,10 +72,10 @@ describe("Recurring events", () => {
start: "2019-03-04T14:00",
end: "2019-03-04T15:00",
recurrence:
"DTSTART:20190304T140000Z\nRRULE:FREQ=WEEKLY;BYDAY=MO;INTERVAL=1;UNTIL=20190325T230000Z" // until 15/04/2019
"DTSTART:20190304T140000Z\nRRULE:FREQ=WEEKLY;BYDAY=MO;INTERVAL=1;UNTIL=20190325T230000Z", // until 15/04/2019
}
event.updateOccurrences()
expect(event.occurrences).toHaveLength(4)
event.occurrences = Promise.resolve(event.getOccurrences())
expect(await event.occurrences).toHaveLength(4)
})
})

View File

@ -0,0 +1,42 @@
import { Connection, createConnection } from "typeorm"
import { createServer } from "../../graphql"
import { PostOffice } from "../../post_office"
import { createTestClient } from "apollo-server-testing"
import { User } from "../../Auth/User.entity"
import { Session } from "../../Auth/Session.entity"
import { Event, EventInformation } from "../../Calendar/Event.entity"
import gql from "graphql-tag"
import testConfig from "../../__tests__/testORMConfig"
let testClient
let connection: Connection = null
let sendEmailMock
let sessionManagerMock
describe("Tags resolver", () => {
beforeAll(async () => {
return (connection = await createConnection({
...testConfig
}))
})
beforeEach(async () => {
sendEmailMock = jest.fn(() => Promise.resolve(true))
sessionManagerMock = jest.fn(() => Promise.resolve(true))
const server = await createServer({
typeormConnection: connection,
sendEmail: sendEmailMock as PostOffice,
sessionManager: sessionManagerMock
})
return (testClient = await createTestClient(server as any))
})
afterAll(async () => {
await connection.close()
})
it("Create some tags", async done => {
done()
})
})

View File

@ -0,0 +1,86 @@
import { Context, ResolverMap } from "../@types/graphql-utils"
import { EventTag, EventTagTranslation } from "./EventTag.entity"
const adminOrThrow = async (context: Context) => {
const roles = await context.user?.roles
if (!roles?.find(role => role.type === "admin")) {
throw new Error("Only admin can create and modify tags")
}
}
const resolvers: ResolverMap = {
Mutation: {
createEventTag: async (
_,
req: GQL.ICreateEventTagOnMutationArguments,
context: Context,
info: any
): Promise<EventTag> => {
await adminOrThrow(context)
const tag = new EventTag()
tag.name = req.input.name
tag.events = Promise.resolve([])
const translations: EventTagTranslation[] = req.input.translations.map(
translationInput => {
const translation = new EventTagTranslation()
translation.tag = Promise.resolve(tag)
translation.language = translationInput.language
translation.text = translationInput.text
return translation
}
)
tag.translations = Promise.resolve(translations)
return tag.save()
},
modifyEventTag: async (
_,
req: GQL.IModifyEventTagOnMutationArguments,
context: Context
) => {
await adminOrThrow(context)
const tag = await EventTag.findOne(req.input.id)
const translations = await tag.translations
const langToExistingTranslation: {[lang: string]: EventTagTranslation} = {}
const langToInputTranslations: {[lang: string]: GQL.ICreateModifyEventTagTranslationInput} = {}
translations.forEach(existingTranslation => langToExistingTranslation[existingTranslation.language] = existingTranslation)
req.input.translations.forEach(translationInput => langToInputTranslations[translationInput.language] = translationInput)
const removedTranslationsPromises = Object.keys(langToExistingTranslation).map((existingLang) => {
if (!langToInputTranslations[existingLang]) {
return langToExistingTranslation[existingLang].remove()
}
return null
})
await Promise.all(removedTranslationsPromises.filter(Boolean))
const changedTranslationsPromises = Object.keys(langToExistingTranslation).map(existingLang => {
const existingTranslation = langToExistingTranslation[existingLang]
const matchingInput = langToInputTranslations[existingLang]
if (!(existingTranslation && matchingInput)) {
throw new Error("Coding error, existing translation and matching input must both exist")
}
if (existingTranslation.text !== matchingInput.text ) {
existingTranslation.text = matchingInput.text
return existingTranslation.save()
}
return null
})
await Promise.all(changedTranslationsPromises.filter(Boolean))
const newTranslationsPromises = Object.keys(langToInputTranslations).map(inputLang => {
if (!langToExistingTranslation[inputLang]) {
const newTranslation = new EventTagTranslation()
newTranslation.tag = Promise.resolve(tag)
newTranslation.language = inputLang
newTranslation.text = langToInputTranslations[inputLang].text
return newTranslation
}
return null
})
await Promise.all(newTranslationsPromises.filter(Boolean))
},
},
}

View File

@ -3,8 +3,16 @@ import {
EventInformation,
EventOccurrence
} from "../Calendar/Event.entity"
import { EventTag } from "../Calendar/EventTag.entity";
export default class EventsService {
export const getTags = async (tagNames: string[]): Promise<EventTag[]> => {
const tagPromises = tagNames.map(tagName =>
EventTag.findOne({
where: {
name: tagName,
},
})
)
const tags = await Promise.all(tagPromises)
return tags
}

View File

@ -11,6 +11,7 @@ import testConfig from "../../__tests__/testORMConfig"
let testClient
let connection: Connection = null
let sendEmailMock
let sessionManagerMock
const createKiteflyingEvent = async () => {
const owner = new User()
@ -26,12 +27,12 @@ const createKiteflyingEvent = async () => {
await session.save()
const event = new Event()
event.owner = owner
event.infos = [{
event.owner = Promise.resolve(owner)
event.infos = Promise.resolve([{
language: "en",
title: "Kite Flying Test Event",
description: "Description for test event starting at 3pm"
}]
} as EventInformation])
event.time = {
timeZone: "Europe/Madrid",
start: "2019-10-10T13:00",
@ -55,19 +56,20 @@ const createKinttingEvent = async () => {
await session.save()
const event = new Event()
event.owner = owner
event.infos = [{
event.owner = Promise.resolve(owner)
event.infos = Promise.resolve([{
language: "en",
title: "Knitting Test Event",
description: "Description for test event starting at 3pm"
}]
} as EventInformation])
event.time = {
timeZone: "Europe/Madrid",
start: "2019-10-10T14:00",
end: "2019-10-10T15:00"
}
event.status = "Scheduled"
return await event.updateOccurrences().save()
event.occurrences = Promise.resolve(event.getOccurrences())
return await event.save()
}
describe("Events resolver", () => {
@ -79,10 +81,12 @@ describe("Events resolver", () => {
beforeEach(async () => {
sendEmailMock = jest.fn(() => Promise.resolve(true))
sessionManagerMock = jest.fn(() => Promise.resolve(true))
const server = await createServer({
typeormConnection: connection,
sendEmail: sendEmailMock as PostOffice
sendEmail: sendEmailMock as PostOffice,
sessionManager: sessionManagerMock
})
return (testClient = await createTestClient(server as any))
})
@ -99,7 +103,8 @@ describe("Events resolver", () => {
query {
events(filter: { limit: 10 }) {
id
info {
info(lang: "en") {
language
title
description
}
@ -127,7 +132,7 @@ describe("Events resolver", () => {
query GetEvent($ownerId: ID!) {
events(filter: { limit: 10, owner: $ownerId }) {
id
info {
info(lang: "en") {
title
description
}

View File

@ -1,6 +1,6 @@
import {User} from "../../Auth/User.entity"
import {Session} from "../../Auth/Session.entity"
import {Event, EventInformation, toUTC} from "../../Calendar/Event.entity"
import {Event, EventInformation} from "../../Calendar/Event.entity"
import {Frequency, RRule} from 'rrule'
import {Connection, createConnection} from "typeorm"
import testConfig from "../../__tests__/testORMConfig";
@ -49,12 +49,12 @@ describe('Occurrences', async () => {
recurrence: new RRule({
freq: Frequency.WEEKLY,
interval: 1,
dtstart: new Date(toUTC(new Date("2019-03-01T13:00Z"), "Europe/Madrid"))
dtstart: new Date("2019-03-01T13:00Z+02:00")
}).toString()
}
event.status = "Scheduled"
event.occurrences = Promise.resolve(event.getOccurrences())
event.updateOccurrences()
await event.save()
})
})

View File

@ -1,15 +1,14 @@
import {
Event,
EventInformation,
EventOccurrence
EventOccurrence,
} from "../Calendar/Event.entity"
import { Context, ResolverMap } from "../@types/graphql-utils"
import EventsService from './EventsService'
import { equals } from 'ramda'
import { User } from "../Auth/User.entity";
import { hasAnyRole } from "../Auth/authUtils";
const eventsService = new EventsService()
import { getTags } from "./EventsService"
import { equals } from "ramda"
import { User } from "../Auth/User.entity"
import { hasAnyRole } from "../Auth/authUtils"
import { EventTag } from "../Calendar/EventTag.entity"
const resolvers: ResolverMap = {
Query: {
@ -22,17 +21,14 @@ const resolvers: ResolverMap = {
where:
req.filter && req.filter.owner
? `"ownerId"='${req.filter.owner}'`
: null
: null,
})
},
occurrences: async (
_,
req: GQL.IOccurrencesOnQueryArguments,
) => {
occurrences: async (_, req: GQL.IOccurrencesOnQueryArguments) => {
const { from, to } = req.filter
return EventOccurrence.find({
where: `during && tstzrange('${from}', '${to}')`,
take: req.filter.limit
take: req.filter.limit,
})
},
occurrence: async (_, req: GQL.IOccurrenceOnQueryArguments) => {
@ -41,7 +37,7 @@ const resolvers: ResolverMap = {
throw new Error(`No occurrence found for id ${req.id}`)
}
return occ
}
},
},
CalendarEvent: {
@ -59,10 +55,10 @@ const resolvers: ResolverMap = {
EventOccurrence: {
start: (eOcc: EventOccurrence) => {
return eOcc.during.split(',')[0].substring(2, 21)
return eOcc.during.split(",")[0].substring(2, 21)
},
end: (eOcc: EventOccurrence) => {
return eOcc.during.split(',')[1].substring(2, 21)
return eOcc.during.split(",")[1].substring(2, 21)
},
},
@ -70,13 +66,19 @@ const resolvers: ResolverMap = {
createEvent: async (
_,
{ input }: GQL.ICreateEventOnMutationArguments,
context: Context,
context: Context
) => {
if (!context.user) {
throw Error("not authenticated")
}
if (!hasAnyRole(await context.user.roles, ['admin','embassador','organizer'])) {
if (
!hasAnyRole(await context.user.roles, [
"admin",
"embassador",
"organizer",
])
) {
throw Error("Cannot create event, please validate as organizer")
}
@ -96,11 +98,12 @@ const resolvers: ResolverMap = {
recurrence: input.time.recurrence,
timeZone: input.time.timeZone,
start: input.time.start,
end: input.time.end
end: input.time.end,
}
if (input.tagNames) {
event.tags = getTags(input.tagNames)
}
event.meta = input.meta
event.location = input.location
event.occurrences = Promise.resolve(event.getOccurrences())
await event.save()
return event
@ -108,9 +111,9 @@ const resolvers: ResolverMap = {
updateEvent: async (
_,
{ input }: GQL.IUpdateEventOnMutationArguments,
context: Context,
context: Context
) => {
const {id, ...fields} = input
const { id, ...fields } = input
if (!context.user) {
throw Error("not authenticated")
@ -123,10 +126,16 @@ const resolvers: ResolverMap = {
throw Error("No change detected")
}
if (input.time && !equals(input.time, event.time)) {
console.log(`Event time changed from: ${JSON.stringify(event.time)} to: ${JSON.stringify(input.time)}`)
console.log(
`Event time changed from: ${JSON.stringify(
event.time
)} to: ${JSON.stringify(input.time)}`
)
event.time = input.time
const existingOccurrences = await event.occurrences
await Promise.all(existingOccurrences.map(occ => EventOccurrence.delete(occ.id)))
await Promise.all(
existingOccurrences.map(occ => EventOccurrence.delete(occ.id))
)
event.occurrences = Promise.resolve(event.getOccurrences())
}
if (input.infos) {
@ -143,31 +152,27 @@ const resolvers: ResolverMap = {
if (input.location) {
event.location = input.location
}
if (input.meta) {
event.meta = input.meta
if (input.tagNames) {
event.tags = getTags(input.tagNames)
}
if (input.location) {
event.location = input.location
}
console.log(JSON.stringify(event,null,'\t'))
console.log(JSON.stringify(event, null, "\t"))
return event.save()
},
deleteEvent: async (
_,
id: string,
context: Context,
): Promise<User> => {
deleteEvent: async (_, id: string, context: Context): Promise<User> => {
const event = await Event.findOne(id)
if ((await event.owner).id !== context.user.id) {
throw Error("Only the owner can delete this event")
}
console.log('will try to remove event now', event.id)
console.log("will try to remove event now", event.id)
const removeEventResult = await Event.remove(event)
console.log('removeEventResult', JSON.stringify(removeEventResult))
console.log("removeEventResult", JSON.stringify(removeEventResult))
return context.user
}
}
},
},
}
export default resolvers

View File

@ -4,7 +4,8 @@ import {ConnectionOptions} from "typeorm"
export const testConfig: ConnectionOptions = {
...config,
database: 'qpa-test',
dropSchema: true
dropSchema: true,
synchronize: true
}
export default testConfig

View File

@ -7,12 +7,25 @@ type User {
roles: [UserRole!]
}
type Tag {
id: ID!
name: String!
translations: [Translations!]!
}
type TagTranslation {
language: String!
text: String!
}
type UserRole {
user: User!
type: RoleType!
}
scalar Date
scalar RoleType
scalar Translations
type UserSession {
hash: String!
@ -49,11 +62,18 @@ type CalendarEvent {
status: EventStatus!
location: Location!
occurrences: [EventOccurrence]
meta: EventMeta
tags: [EventTag]
}
type EventMeta {
tags: [String]!
type EventTag {
ID: ID!
name: String!
translations: [EventTagTranslation!]!
}
type EventTagTranslation {
language: String!
text: String!
}
type EventOccurrence {
@ -132,15 +152,11 @@ input EventLocationInput {
name: String
}
input EventMetaInput {
tags: [String]!
}
input CreateEventInput {
time: EventTimeInput!
infos: [EventInformationInput]!
location: EventLocationInput!
meta: EventMetaInput!
tagNames: [String!]!
status: String!
}
@ -149,7 +165,7 @@ input UpdateEventInput {
time: EventTimeInput
infos: [EventInformationInput!]
location: EventLocationInput
meta: EventMetaInput
tagNames: [String!]!
status: String
}
@ -168,6 +184,23 @@ type Error {
message: String!
}
input CreateModifyEventTagTranslationInput {
language: String!
text: String!
}
input CreateEventTagInput {
name: String!
translations: [CreateModifyEventTagTranslationInput!]!
}
input ModifyEventTagInput {
id: ID!
name: String!
translations: [CreateModifyEventTagTranslationInput!]!
}
type Mutation {
# Auth
signup(input: SignupInput!): [Error]
@ -180,6 +213,10 @@ type Mutation {
createEvent(input: CreateEventInput!): CalendarEvent
updateEvent(input: UpdateEventInput!): CalendarEvent
deleteEvent(id: ID!): User!
# Tags
createEventTag(input: CreateEventTagInput!): EventTag
modifyEventTag(input: ModifyEventTagInput!): EventTag
}

View File

@ -5400,10 +5400,10 @@ type-is@^1.6.16, type-is@~1.6.17, type-is@~1.6.18:
media-typer "0.3.0"
mime-types "~2.1.24"
typeorm@^0.2.18:
version "0.2.18"
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.18.tgz#8ae1d21104117724af41ddc11035c40a705e1de8"
integrity sha512-S553GwtG5ab268+VmaLCN7gKDqFPIzUw0eGMTobJ9yr0Np62Ojfx8j1Oa9bIeh5p7Pz1/kmGabAHoP1MYK05pA==
typeorm@^0.2.20:
version "0.2.20"
resolved "https://registry.yarnpkg.com/typeorm/-/typeorm-0.2.20.tgz#efb60f2e55a7d08fc365f281ec2a71c87a9ebba5"
integrity sha512-VxB+9qH8D+PM19MIx18Zs3Fqv/ZINnnQvUGmBEiLYDrB9etdSdamgSTCIhWdFNndeJ6ldH4jbD0Z6HWsepMPlA==
dependencies:
app-root-path "^2.0.1"
buffer "^5.1.0"
@ -5420,10 +5420,10 @@ typeorm@^0.2.18:
yargonaut "^1.1.2"
yargs "^13.2.1"
typescript@^3.5.3:
version "3.5.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
typescript@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb"
integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==
uglify-js@^3.1.4:
version "3.6.0"