Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b06afcfb1 | |||
| daea6d3765 |
@@ -5,5 +5,6 @@
|
|||||||
.swc
|
.swc
|
||||||
node_modules
|
node_modules
|
||||||
ca-cert*
|
ca-cert*
|
||||||
|
.data/*
|
||||||
|
|
||||||
dist
|
dist
|
||||||
|
|||||||
Vendored
+3
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"cSpell.words": ["awilix"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
FROM postgres:14.5-alpine as builder
|
||||||
|
|
||||||
|
RUN apk add --no-cache 'git=~2.36' \
|
||||||
|
'build-base=~0.5' \
|
||||||
|
'clang=~13.0' \
|
||||||
|
'llvm13=~13.0'
|
||||||
|
|
||||||
|
WORKDIR /
|
||||||
|
RUN git clone --branch 1.1.9 https://github.com/aiven/aiven-extras.git aiven-extras
|
||||||
|
|
||||||
|
WORKDIR /aiven-extras
|
||||||
|
RUN git checkout 36598ab \
|
||||||
|
&& git clean -df \
|
||||||
|
&& make \
|
||||||
|
&& make install
|
||||||
|
|
||||||
|
FROM postgres:14.5-alpine
|
||||||
|
|
||||||
|
COPY --from=builder /aiven-extras/aiven_extras.control /usr/local/share/postgresql/extension/aiven_extras.control
|
||||||
|
COPY --from=builder /aiven-extras/sql/aiven_extras.sql /usr/local/share/postgresql/extension/aiven_extras--1.1.9.sql
|
||||||
|
COPY --from=builder /aiven-extras/aiven_extras.so /usr/local/lib/postgresql/aiven_extras.so
|
||||||
|
|
||||||
|
EXPOSE 5432
|
||||||
|
|
||||||
|
CMD ["postgres"]
|
||||||
+34
-16
@@ -1,35 +1,53 @@
|
|||||||
version: "3.9"
|
version: '3.9'
|
||||||
|
|
||||||
services:
|
services:
|
||||||
postgres:
|
main-db:
|
||||||
image: postgres:16-alpine
|
build:
|
||||||
|
context: aiven_postgres
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
volumes:
|
||||||
|
- ./.data/main-db:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5454:5432
|
- 5454:5432
|
||||||
volumes:
|
|
||||||
- ./.postgres-data:/var/lib/postgresql/data
|
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=speckle
|
- POSTGRES_PASSWORD=speckle
|
||||||
- POSTGRES_USER=speckle
|
- POSTGRES_USER=speckle
|
||||||
- POSTGRES_DB=speckle_main
|
- POSTGRES_DB=speckle
|
||||||
|
extra_hosts:
|
||||||
|
- host.docker.internal:host-gateway
|
||||||
|
|
||||||
region-1:
|
region-1-db:
|
||||||
image: postgres:16-alpine
|
build:
|
||||||
|
context: aiven_postgres
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
volumes:
|
||||||
|
- ./.data/region-1-db:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5455:5432
|
- 5455:5432
|
||||||
volumes:
|
|
||||||
- ./.postgres-region-1:/var/lib/postgresql/data
|
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=speckle
|
- POSTGRES_PASSWORD=speckle
|
||||||
- POSTGRES_USER=speckle
|
- POSTGRES_USER=speckle
|
||||||
- POSTGRES_DB=speckle_main
|
- POSTGRES_DB=speckle
|
||||||
|
depends_on:
|
||||||
|
- main-db
|
||||||
|
|
||||||
region-2:
|
extra_hosts:
|
||||||
image: postgres:16-alpine
|
- host.docker.internal:host-gateway
|
||||||
|
|
||||||
|
region-2-db:
|
||||||
|
build:
|
||||||
|
context: aiven_postgres
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
volumes:
|
||||||
|
- ./.data/region-2-db:/var/lib/postgresql/data
|
||||||
ports:
|
ports:
|
||||||
- 5456:5432
|
- 5456:5432
|
||||||
volumes:
|
|
||||||
- ./.postgres-region-2:/var/lib/postgresql/data
|
|
||||||
environment:
|
environment:
|
||||||
- POSTGRES_PASSWORD=speckle
|
- POSTGRES_PASSWORD=speckle
|
||||||
- POSTGRES_USER=speckle
|
- POSTGRES_USER=speckle
|
||||||
- POSTGRES_DB=speckle_main
|
- POSTGRES_DB=speckle
|
||||||
|
depends_on:
|
||||||
|
- main-db
|
||||||
|
|
||||||
|
extra_hosts:
|
||||||
|
- host.docker.internal:host-gateway
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@apollo/server": "^4.10.0",
|
"@apollo/server": "^4.10.0",
|
||||||
|
"awilix": "^11.0.0",
|
||||||
"crypto-random-string": "^3.0.0",
|
"crypto-random-string": "^3.0.0",
|
||||||
"dataloader": "^2.2.2",
|
"dataloader": "^2.2.2",
|
||||||
"dotenv": "^16.4.1",
|
"dotenv": "^16.4.1",
|
||||||
|
|||||||
Generated
+3202
-2563
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,19 @@
|
|||||||
|
import awilix from 'awilix'
|
||||||
|
import { saveResourceAclFactory, saveUserFactory } from './repositories'
|
||||||
|
import { Knex } from 'knex'
|
||||||
|
import { getMainDbClient } from './services/databaseManagement'
|
||||||
|
|
||||||
|
export const container = awilix.createContainer({
|
||||||
|
strict: true,
|
||||||
|
injectionMode: awilix.InjectionMode.PROXY
|
||||||
|
})
|
||||||
|
|
||||||
|
container.register({
|
||||||
|
db: awilix.asFunction(getMainDbClient).singleton(),
|
||||||
|
saveResource: awilix
|
||||||
|
.asFunction((regionDb: Knex) => saveUserFactory({ db: regionDb }))
|
||||||
|
.scoped(),
|
||||||
|
saveResourceAcl: awilix.asFunction(saveResourceAclFactory).scoped()
|
||||||
|
})
|
||||||
|
|
||||||
|
container.resolve('saveResource')
|
||||||
+239
-224
@@ -12,236 +12,251 @@ import {
|
|||||||
ResourceRegion
|
ResourceRegion
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
export class RegionRepo {
|
export const saveResourceFactory =
|
||||||
db: Knex
|
({ db }: { db: Knex }) =>
|
||||||
|
async (resource: Resource): Promise<void> => {
|
||||||
constructor (db: Knex) {
|
await db<Resource>('resources').insert(resource)
|
||||||
this.db = db
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveResource (resource: Resource): Promise<void> {
|
|
||||||
await this.db<Resource>('resources').insert(resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
async findResource (resourceId: string): Promise<Resource | null> {
|
|
||||||
return (
|
|
||||||
(await this.db<Resource>('resources')
|
|
||||||
.where({ id: resourceId })
|
|
||||||
.first()) ?? null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveComment (comment: Comment): Promise<void> {
|
|
||||||
await this.db<Comment>('comments').insert(comment)
|
|
||||||
}
|
|
||||||
|
|
||||||
async countComments (resourceId: string): Promise<number> {
|
|
||||||
const [rawCount] = await this.db<Comment>('comments')
|
|
||||||
.count()
|
|
||||||
.where({ resourceId })
|
|
||||||
return parseInt(rawCount.count as string)
|
|
||||||
}
|
|
||||||
|
|
||||||
async queryComments ({
|
|
||||||
resourceId,
|
|
||||||
limit,
|
|
||||||
cursor
|
|
||||||
}: {
|
|
||||||
resourceId: string
|
|
||||||
limit: number
|
|
||||||
cursor: string | null
|
|
||||||
}): Promise<Comment[]> {
|
|
||||||
const query = this.db<Comment>('comments').where({ resourceId })
|
|
||||||
if (cursor) {
|
|
||||||
query.andWhere('createdAt', '<', cursor)
|
|
||||||
}
|
}
|
||||||
return await query.limit(limit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export class MainRepo extends RegionRepo {
|
export const findResourceFactory =
|
||||||
async findUser (userId: string): Promise<UserRecord | null> {
|
({ db }: { db: Knex }) =>
|
||||||
return (
|
async (resourceId: string): Promise<Resource | null> => {
|
||||||
(await this.db<UserRecord>('users').where('id', '=', userId).first()) ??
|
return (
|
||||||
|
(await db<Resource>('resources').where({ id: resourceId }).first()) ??
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
async queryUsers (): Promise<UserRecord[]> {
|
|
||||||
return await this.db<UserRecord>('users').select()
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveUser (user: UserRecord): Promise<void> {
|
|
||||||
await this.db<UserRecord>('users').insert(user)
|
|
||||||
}
|
|
||||||
|
|
||||||
async getUsersResourceAcl ({
|
|
||||||
resourceId,
|
|
||||||
userId
|
|
||||||
}: ResourceAcl): Promise<ResourceAcl | null> {
|
|
||||||
return (
|
|
||||||
(await this.db<ResourceAcl>('resource_acl')
|
|
||||||
.where({ userId, resourceId })
|
|
||||||
.first()) ?? null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveResourceAcl (resourceAcl: ResourceAcl): Promise<void> {
|
|
||||||
await this.db<ResourceAcl>('resource_acl').insert(resourceAcl)
|
|
||||||
}
|
|
||||||
|
|
||||||
async countUsersResources (userId: string): Promise<number> {
|
|
||||||
const [rawCount] = await this.db<ResourceAcl>('resource_acl')
|
|
||||||
.count()
|
|
||||||
.where({ userId })
|
|
||||||
return parseInt(rawCount.count as string)
|
|
||||||
}
|
|
||||||
|
|
||||||
async findUsersResource ({
|
|
||||||
resourceId,
|
|
||||||
userId
|
|
||||||
}: ResourceAcl): Promise<ResourceAcl | null> {
|
|
||||||
return (
|
|
||||||
(await this.db<ResourceAcl>('resource_acl')
|
|
||||||
.where({ userId, resourceId })
|
|
||||||
.first()) ?? null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async queryResources ({
|
|
||||||
userId,
|
|
||||||
limit,
|
|
||||||
cursor
|
|
||||||
}: {
|
|
||||||
userId: string
|
|
||||||
limit: number
|
|
||||||
cursor: string | null
|
|
||||||
}): Promise<Resource[]> {
|
|
||||||
let query = this.db<Resource & ResourceAcl>('resources')
|
|
||||||
.join('resource_acl', 'resources.id', 'resource_acl.resourceId')
|
|
||||||
.where({ userId })
|
|
||||||
if (cursor) {
|
|
||||||
query = query.andWhere('createdAt', '<', cursor)
|
|
||||||
}
|
}
|
||||||
const items = await query.orderBy('createdAt', 'desc').limit(limit)
|
|
||||||
return items
|
|
||||||
}
|
|
||||||
|
|
||||||
async countResourceComments (resourceId: string): Promise<number> {
|
export const saveCommentFactory =
|
||||||
const [rawCount] = await this.db<Comment>('comments')
|
({ db }: { db: Knex }) =>
|
||||||
.count()
|
async (comment: Comment): Promise<void> => {
|
||||||
.where({ resourceId })
|
await db<Comment>('comments').insert(comment)
|
||||||
return parseInt(rawCount.count as string)
|
|
||||||
}
|
|
||||||
|
|
||||||
async queryComments ({
|
|
||||||
resourceId,
|
|
||||||
limit,
|
|
||||||
cursor
|
|
||||||
}: {
|
|
||||||
resourceId: string
|
|
||||||
limit: number
|
|
||||||
cursor: string | null
|
|
||||||
}): Promise<Comment[]> {
|
|
||||||
let query = this.db<Comment>('comments').where({ resourceId })
|
|
||||||
if (cursor) {
|
|
||||||
query = query.andWhere('createdAt', '<', cursor)
|
|
||||||
}
|
}
|
||||||
return await query.orderBy('createdAt', 'desc').limit(limit)
|
|
||||||
}
|
|
||||||
|
|
||||||
async queryRegions (
|
export const countCommentsFactory =
|
||||||
params:
|
({ db }: { db: Knex }) =>
|
||||||
| {
|
async (resourceId: string): Promise<number> => {
|
||||||
connectionString?: string | undefined
|
const [rawCount] = await db<Comment>('comments')
|
||||||
}
|
.count()
|
||||||
| undefined = undefined
|
|
||||||
): Promise<Region[]> {
|
|
||||||
const query = this.db<Region>('regions')
|
|
||||||
if ((params != null) && params.connectionString) query.where(params)
|
|
||||||
return await query.select()
|
|
||||||
}
|
|
||||||
|
|
||||||
async findRegion (id: string): Promise<Region | null> {
|
|
||||||
return (await this.db<Region>('regions').where({ id }).first()) ?? null
|
|
||||||
}
|
|
||||||
|
|
||||||
async queryOrganizationsRegions (): Promise<OrganizationsRegions[]> {
|
|
||||||
return await this.db<OrganizationsRegions>('organizations_regions').select()
|
|
||||||
}
|
|
||||||
|
|
||||||
async findOrganizationRegion ({
|
|
||||||
regionId,
|
|
||||||
organizationId
|
|
||||||
}: OrganizationsRegions): Promise<OrganizationsRegions | null> {
|
|
||||||
return (
|
|
||||||
(await this.db<OrganizationsRegions>('organizations_regions')
|
|
||||||
.where({ regionId, organizationId })
|
|
||||||
.first()) ?? null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveRegion (region: Region): Promise<void> {
|
|
||||||
await this.db<Region>('regions').insert(region)
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveOrganization (organization: Organization) {
|
|
||||||
await this.db<Organization>('organizations').insert(organization)
|
|
||||||
}
|
|
||||||
|
|
||||||
async findOrganization (id: string): Promise<Organization | null> {
|
|
||||||
return (
|
|
||||||
(await this.db<Organization>('organizations').where({ id }).first()) ??
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async queryOrganizations (): Promise<Organization[]> {
|
|
||||||
return await this.db<Organization>('organizations').select()
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveOrganizationRegion (or: OrganizationsRegions): Promise<void> {
|
|
||||||
return await this.db<OrganizationsRegions>('organizations_regions').insert(
|
|
||||||
or
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveOrganizationAcl (orgAcl: OrganizationAcl): Promise<void> {
|
|
||||||
await this.db<OrganizationsRegions>('organization_acl').insert(orgAcl)
|
|
||||||
}
|
|
||||||
|
|
||||||
async findOrganizationAcl ({
|
|
||||||
userId,
|
|
||||||
organizationId
|
|
||||||
}: OrganizationAcl): Promise<OrganizationAcl | null> {
|
|
||||||
return (
|
|
||||||
(await this.db<OrganizationAcl>('organization_acl')
|
|
||||||
.where({ userId, organizationId })
|
|
||||||
.first()) ?? null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async saveOrganizationResourceAcl (
|
|
||||||
item: OrganizationResourceAcl
|
|
||||||
): Promise<void> {
|
|
||||||
await this.db<OrganizationResourceAcl>('organization_resource_acl').insert(
|
|
||||||
item
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
async findResourceRegion ({
|
|
||||||
resourceId
|
|
||||||
}: {
|
|
||||||
resourceId: string
|
|
||||||
}): Promise<ResourceRegion | null> {
|
|
||||||
return (
|
|
||||||
(await this.db<ResourceRegion>('resource_region')
|
|
||||||
.where({ resourceId })
|
.where({ resourceId })
|
||||||
.first()) ?? null
|
return parseInt(rawCount.count as string)
|
||||||
)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async saveResourceRegion (item: ResourceRegion): Promise<void> {
|
export const findUserFactory =
|
||||||
await this.db<ResourceRegion>('resource_region').insert(item)
|
({ db }: { db: Knex }) =>
|
||||||
}
|
async (userId: string): Promise<UserRecord | null> => {
|
||||||
}
|
return (
|
||||||
|
(await db<UserRecord>('users').where('id', '=', userId).first()) ?? null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryUsersFactoy =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (): Promise<UserRecord[]> => {
|
||||||
|
return await db<UserRecord>('users').select()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveUserFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (user: UserRecord): Promise<void> => {
|
||||||
|
await db<UserRecord>('users').insert(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getUsersResourceAclFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async ({ resourceId, userId }: ResourceAcl): Promise<ResourceAcl | null> => {
|
||||||
|
return (
|
||||||
|
(await db<ResourceAcl>('resource_acl')
|
||||||
|
.where({ userId, resourceId })
|
||||||
|
.first()) ?? null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveResourceAclFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (resourceAcl: ResourceAcl): Promise<void> => {
|
||||||
|
await db<ResourceAcl>('resource_acl').insert(resourceAcl)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const countUsersResourcesFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (userId: string): Promise<number> => {
|
||||||
|
const [rawCount] = await db<ResourceAcl>('resource_acl')
|
||||||
|
.count()
|
||||||
|
.where({ userId })
|
||||||
|
return parseInt(rawCount.count as string)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findUsersResourceFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async ({ resourceId, userId }: ResourceAcl): Promise<ResourceAcl | null> => {
|
||||||
|
return (
|
||||||
|
(await db<ResourceAcl>('resource_acl')
|
||||||
|
.where({ userId, resourceId })
|
||||||
|
.first()) ?? null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryResourcesFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async ({
|
||||||
|
userId,
|
||||||
|
limit,
|
||||||
|
cursor
|
||||||
|
}: {
|
||||||
|
userId: string
|
||||||
|
limit: number
|
||||||
|
cursor: string | null
|
||||||
|
}): Promise<Resource[]> => {
|
||||||
|
let query = db<Resource & ResourceAcl>('resources')
|
||||||
|
.join('resource_acl', 'resources.id', 'resource_acl.resourceId')
|
||||||
|
.where({ userId })
|
||||||
|
if (cursor !== null) {
|
||||||
|
query = query.andWhere('createdAt', '<', cursor)
|
||||||
|
}
|
||||||
|
const items = await query.orderBy('createdAt', 'desc').limit(limit)
|
||||||
|
return items
|
||||||
|
}
|
||||||
|
|
||||||
|
export const countResourceCommentsFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (resourceId: string): Promise<number> => {
|
||||||
|
const [rawCount] = await db<Comment>('comments')
|
||||||
|
.count()
|
||||||
|
.where({ resourceId })
|
||||||
|
return parseInt(rawCount.count as string)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryCommentsFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async ({
|
||||||
|
resourceId,
|
||||||
|
limit,
|
||||||
|
cursor
|
||||||
|
}: {
|
||||||
|
resourceId: string
|
||||||
|
limit: number
|
||||||
|
cursor: string | null
|
||||||
|
}): Promise<Comment[]> => {
|
||||||
|
let query = db<Comment>('comments').where({ resourceId })
|
||||||
|
if (cursor !== null) {
|
||||||
|
query = query.andWhere('createdAt', '<', cursor)
|
||||||
|
}
|
||||||
|
return await query.orderBy('createdAt', 'desc').limit(limit)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryRegionsFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (
|
||||||
|
params:
|
||||||
|
| {
|
||||||
|
connectionString?: string | undefined
|
||||||
|
}
|
||||||
|
| undefined = undefined
|
||||||
|
): Promise<Region[]> => {
|
||||||
|
let query = db<Region>('regions')
|
||||||
|
if (params?.connectionString !== undefined) query = query.where(params)
|
||||||
|
return await query.select()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findRegionFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (id: string): Promise<Region | null> => {
|
||||||
|
return (await db<Region>('regions').where({ id }).first()) ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryOrganizationsRegionsFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (): Promise<OrganizationsRegions[]> => {
|
||||||
|
return await db<OrganizationsRegions>('organizations_regions').select()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findOrganizationRegionFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async ({
|
||||||
|
regionId,
|
||||||
|
organizationId
|
||||||
|
}: OrganizationsRegions): Promise<OrganizationsRegions | null> => {
|
||||||
|
return (
|
||||||
|
(await db<OrganizationsRegions>('organizations_regions')
|
||||||
|
.where({ regionId, organizationId })
|
||||||
|
.first()) ?? null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveRegionFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (region: Region): Promise<void> => {
|
||||||
|
await db<Region>('regions').insert(region)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveOrganizationFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (organization: Organization): Promise<void> => {
|
||||||
|
await db<Organization>('organizations').insert(organization)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findOrganizationFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (id: string): Promise<Organization | null> => {
|
||||||
|
return (
|
||||||
|
(await db<Organization>('organizations').where({ id }).first()) ?? null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const queryOrganizationsFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (): Promise<Organization[]> => {
|
||||||
|
return await db<Organization>('organizations').select()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveOrganizationRegionFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (or: OrganizationsRegions): Promise<void> => {
|
||||||
|
return await db<OrganizationsRegions>('organizations_regions').insert(or)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveOrganizationAclFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (orgAcl: OrganizationAcl): Promise<void> => {
|
||||||
|
await db<OrganizationsRegions>('organization_acl').insert(orgAcl)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findOrganizationAclFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async ({
|
||||||
|
userId,
|
||||||
|
organizationId
|
||||||
|
}: OrganizationAcl): Promise<OrganizationAcl | null> => {
|
||||||
|
return (
|
||||||
|
(await db<OrganizationAcl>('organization_acl')
|
||||||
|
.where({ userId, organizationId })
|
||||||
|
.first()) ?? null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveOrganizationResourceAclFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (item: OrganizationResourceAcl): Promise<void> => {
|
||||||
|
await db<OrganizationResourceAcl>('organization_resource_acl').insert(item)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const findResourceRegionFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async ({
|
||||||
|
resourceId
|
||||||
|
}: {
|
||||||
|
resourceId: string
|
||||||
|
}): Promise<ResourceRegion | null> => {
|
||||||
|
return (
|
||||||
|
(await db<ResourceRegion>('resource_region')
|
||||||
|
.where({ resourceId })
|
||||||
|
.first()) ?? null
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export const saveResourceRegionFactory =
|
||||||
|
({ db }: { db: Knex }) =>
|
||||||
|
async (item: ResourceRegion): Promise<void> => {
|
||||||
|
await db<ResourceRegion>('resource_region').insert(item)
|
||||||
|
}
|
||||||
|
|||||||
+78
-46
@@ -1,6 +1,9 @@
|
|||||||
import { RegionRepo, MainRepo } from './repositories'
|
import { getCommentsFactory } from './services/comments'
|
||||||
import { getComments } from './services/comments'
|
import awilix from 'awilix'
|
||||||
import { createResource, getResources } from './services/resources'
|
import {
|
||||||
|
createResourceFactory,
|
||||||
|
getResourcesFactory
|
||||||
|
} from './services/resources'
|
||||||
import { GraphQLError } from 'graphql'
|
import { GraphQLError } from 'graphql'
|
||||||
import {
|
import {
|
||||||
Resource,
|
Resource,
|
||||||
@@ -16,29 +19,51 @@ import {
|
|||||||
import {
|
import {
|
||||||
createOrganization,
|
createOrganization,
|
||||||
registerRegion,
|
registerRegion,
|
||||||
getMainRepo,
|
getResourceDb,
|
||||||
getRegionRepo,
|
getMainDbClient,
|
||||||
getResourceRepo
|
getRegionDb
|
||||||
} from './services/databaseManagement'
|
} from './services/databaseManagement'
|
||||||
import { authorizeUserOrgRegion } from './services/authz'
|
import { authorizeUserOrgRegionFactory } from './services/authz'
|
||||||
import cryptoRandomString from 'crypto-random-string'
|
import cryptoRandomString from 'crypto-random-string'
|
||||||
|
import {
|
||||||
|
countCommentsFactory,
|
||||||
|
countUsersResourcesFactory,
|
||||||
|
findOrganizationAclFactory,
|
||||||
|
findOrganizationRegionFactory,
|
||||||
|
findResourceFactory,
|
||||||
|
findUserFactory,
|
||||||
|
getUsersResourceAclFactory,
|
||||||
|
queryCommentsFactory,
|
||||||
|
queryOrganizationsFactory,
|
||||||
|
queryRegionsFactory,
|
||||||
|
queryResourcesFactory,
|
||||||
|
queryUsersFactoy,
|
||||||
|
saveCommentFactory,
|
||||||
|
saveOrganizationAclFactory,
|
||||||
|
saveOrganizationRegionFactory,
|
||||||
|
saveOrganizationResourceAclFactory,
|
||||||
|
saveResourceAclFactory,
|
||||||
|
saveResourceRegionFactory,
|
||||||
|
saveUserFactory
|
||||||
|
} from './repositories'
|
||||||
|
import { container } from './iocContainer'
|
||||||
|
|
||||||
|
const db = getMainDbClient()
|
||||||
// Resolvers define how to fetch the types defined in your schema.
|
// Resolvers define how to fetch the types defined in your schema.
|
||||||
// This resolver retrieves books from the "books" array above.
|
// This resolver retrieves books from the "books" array above.
|
||||||
export const resolvers = {
|
export const resolvers = {
|
||||||
Query: {
|
Query: {
|
||||||
async users () {
|
async users () {
|
||||||
return await getMainRepo().queryUsers()
|
return await queryUsersFactoy({ db })()
|
||||||
},
|
},
|
||||||
async user (_: unknown, args: { id: string }) {
|
async user (_: unknown, args: { id: string }) {
|
||||||
return await getMainRepo().findUser(args.id)
|
return await findUserFactory({ db })(args.id)
|
||||||
},
|
},
|
||||||
async resource (
|
async resource (
|
||||||
_: unknown,
|
_: unknown,
|
||||||
args: { id: string, userId: string }
|
args: { id: string, userId: string }
|
||||||
): Promise<Resource> {
|
): Promise<Resource> {
|
||||||
const mainRepo = getMainRepo()
|
const maybeAcl = await getUsersResourceAclFactory({ db })({
|
||||||
const maybeAcl = await mainRepo.getUsersResourceAcl({
|
|
||||||
userId: args.userId,
|
userId: args.userId,
|
||||||
resourceId: args.id
|
resourceId: args.id
|
||||||
})
|
})
|
||||||
@@ -52,8 +77,10 @@ export const resolvers = {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
const resourceRepo = await getResourceRepo(args.id)
|
const resourceDb = await getResourceDb(args.id)
|
||||||
const maybeResource = await resourceRepo.findResource(args.id)
|
const maybeResource = await findResourceFactory({ db: resourceDb })(
|
||||||
|
args.id
|
||||||
|
)
|
||||||
if (maybeResource == null) {
|
if (maybeResource == null) {
|
||||||
throw new GraphQLError('Resource not found', {
|
throw new GraphQLError('Resource not found', {
|
||||||
extensions: { code: 'RESOURCE_NOT_FOUND' }
|
extensions: { code: 'RESOURCE_NOT_FOUND' }
|
||||||
@@ -62,18 +89,17 @@ export const resolvers = {
|
|||||||
return maybeResource
|
return maybeResource
|
||||||
},
|
},
|
||||||
async organizations () {
|
async organizations () {
|
||||||
return await getMainRepo().queryOrganizations()
|
return await queryOrganizationsFactory({ db })()
|
||||||
},
|
},
|
||||||
async regions () {
|
async regions () {
|
||||||
return await getMainRepo().queryRegions()
|
return await queryRegionsFactory({ db })()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
User: {
|
User: {
|
||||||
async resources (parent: UserRecord, args: PaginationArgs) {
|
async resources (parent: UserRecord, args: PaginationArgs) {
|
||||||
const mainRepo = getMainRepo()
|
return await getResourcesFactory(
|
||||||
return await getResources(
|
countUsersResourcesFactory({ db }),
|
||||||
mainRepo.countUsersResources.bind(mainRepo),
|
queryResourcesFactory({ db })
|
||||||
mainRepo.queryResources.bind(mainRepo)
|
|
||||||
)({ userId: parent.id, ...args })
|
)({ userId: parent.id, ...args })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -82,10 +108,10 @@ export const resolvers = {
|
|||||||
parent: Resource,
|
parent: Resource,
|
||||||
{ limit, cursor }: PaginationArgs
|
{ limit, cursor }: PaginationArgs
|
||||||
): Promise<CommentCollection> {
|
): Promise<CommentCollection> {
|
||||||
const resourceRepo = await getResourceRepo(parent.id)
|
const resourceDb = await getResourceDb(parent.id)
|
||||||
return await getComments(
|
return await getCommentsFactory(
|
||||||
resourceRepo.countComments.bind(resourceRepo),
|
countCommentsFactory({ db: resourceDb }),
|
||||||
resourceRepo.queryComments.bind(resourceRepo)
|
queryCommentsFactory({ db: resourceDb })
|
||||||
)({
|
)({
|
||||||
resourceId: parent.id,
|
resourceId: parent.id,
|
||||||
limit,
|
limit,
|
||||||
@@ -99,7 +125,7 @@ export const resolvers = {
|
|||||||
{ input: { name } }: { input: UserCreateArgs }
|
{ input: { name } }: { input: UserCreateArgs }
|
||||||
) {
|
) {
|
||||||
const id = cryptoRandomString({ length: 10 })
|
const id = cryptoRandomString({ length: 10 })
|
||||||
await getMainRepo().saveUser({ id, name })
|
await saveUserFactory({ db })({ id, name })
|
||||||
return id
|
return id
|
||||||
},
|
},
|
||||||
async registerRegion (
|
async registerRegion (
|
||||||
@@ -116,40 +142,45 @@ export const resolvers = {
|
|||||||
return await createOrganization(args.name)
|
return await createOrganization(args.name)
|
||||||
},
|
},
|
||||||
async addRegionToOrganization (_: unknown, args: OrganizationsRegions) {
|
async addRegionToOrganization (_: unknown, args: OrganizationsRegions) {
|
||||||
await getMainRepo().saveOrganizationRegion(args)
|
await saveOrganizationRegionFactory({ db })(args)
|
||||||
},
|
},
|
||||||
async addUserToOrganization (
|
async addUserToOrganization (
|
||||||
_: unknown,
|
_: unknown,
|
||||||
{ input: args }: { input: OrganizationAcl }
|
{ input: args }: { input: OrganizationAcl }
|
||||||
) {
|
) {
|
||||||
await getMainRepo().saveOrganizationAcl(args)
|
await saveOrganizationAclFactory({ db })(args)
|
||||||
},
|
},
|
||||||
async createResource (
|
async createResource (
|
||||||
_: unknown,
|
_: unknown,
|
||||||
{ input: args }: { input: ResourceCreateArgs }
|
{ input: args }: { input: ResourceCreateArgs }
|
||||||
) {
|
) {
|
||||||
const mainRepo = getMainRepo()
|
await authorizeUserOrgRegionFactory(
|
||||||
await authorizeUserOrgRegion(
|
findOrganizationAclFactory({ db }),
|
||||||
mainRepo.findOrganizationAcl.bind(mainRepo),
|
findOrganizationRegionFactory({ db })
|
||||||
mainRepo.findOrganizationRegion.bind(mainRepo)
|
|
||||||
)(args)
|
)(args)
|
||||||
|
|
||||||
const repo = args.regionId
|
const resourceDb =
|
||||||
? await getRegionRepo({ regionId: args.regionId })
|
args.regionId !== null
|
||||||
: mainRepo
|
? await getRegionDb({ regionId: args.regionId })
|
||||||
|
: db
|
||||||
|
|
||||||
const resourceId = await createResource(
|
const requestContainer = container.createScope()
|
||||||
repo.saveResource.bind(repo),
|
requestContainer.register({ resourceDb: awilix.asValue(resourceDb) })
|
||||||
mainRepo.saveResourceAcl.bind(mainRepo)
|
|
||||||
)(args)
|
|
||||||
|
|
||||||
if (args.organizationId) {
|
const saveResource = requestContainer.resolve('saveResource')
|
||||||
await mainRepo.saveOrganizationResourceAcl({
|
|
||||||
|
const resourceId = await createResourceFactory({
|
||||||
|
saveResource,
|
||||||
|
saveResourceAcl: saveResourceAclFactory({ db })
|
||||||
|
})(args)
|
||||||
|
|
||||||
|
if (args.organizationId !== null) {
|
||||||
|
await saveOrganizationResourceAclFactory({ db })({
|
||||||
organizationId: args.organizationId,
|
organizationId: args.organizationId,
|
||||||
resourceId
|
resourceId
|
||||||
})
|
})
|
||||||
if (args.regionId) {
|
if (args.regionId !== null) {
|
||||||
await mainRepo.saveResourceRegion({
|
await saveResourceRegionFactory({ db })({
|
||||||
resourceId,
|
resourceId,
|
||||||
// i know its not null here, the authz function ensures it
|
// i know its not null here, the authz function ensures it
|
||||||
regionId: args.regionId
|
regionId: args.regionId
|
||||||
@@ -162,15 +193,16 @@ export const resolvers = {
|
|||||||
_: unknown,
|
_: unknown,
|
||||||
{ input: args }: { input: CommentCreateArgs }
|
{ input: args }: { input: CommentCreateArgs }
|
||||||
) {
|
) {
|
||||||
const mainRepo = getMainRepo()
|
const resourceAcl = await getUsersResourceAclFactory({ db })(args)
|
||||||
const resourceAcl = await mainRepo.getUsersResourceAcl(args)
|
if (resourceAcl == null) {
|
||||||
if (resourceAcl == null) { throw new Error("The user doesn't have access to the given resource") }
|
throw new Error("The user doesn't have access to the given resource")
|
||||||
|
}
|
||||||
// 2. get resource db client
|
// 2. get resource db client
|
||||||
const resourceRepo = await getResourceRepo(args.resourceId)
|
const resourceDb = await getResourceDb(args.resourceId)
|
||||||
// 3. save comment to db
|
// 3. save comment to db
|
||||||
const id = cryptoRandomString({ length: 10 })
|
const id = cryptoRandomString({ length: 10 })
|
||||||
const createdAt = new Date()
|
const createdAt = new Date()
|
||||||
await resourceRepo.saveComment({ id, createdAt, ...args })
|
await saveCommentFactory({ db: resourceDb })({ id, createdAt, ...args })
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-4
@@ -4,7 +4,7 @@ import {
|
|||||||
UserOrgRegionArgs
|
UserOrgRegionArgs
|
||||||
} from '../types'
|
} from '../types'
|
||||||
|
|
||||||
export const authorizeUserOrgRegion =
|
export const authorizeUserOrgRegionFactory =
|
||||||
(
|
(
|
||||||
orgAclGetter: (params: OrganizationAcl) => Promise<OrganizationAcl | null>,
|
orgAclGetter: (params: OrganizationAcl) => Promise<OrganizationAcl | null>,
|
||||||
orgRegionGetter: (
|
orgRegionGetter: (
|
||||||
@@ -12,12 +12,18 @@ export const authorizeUserOrgRegion =
|
|||||||
) => Promise<OrganizationsRegions | null>
|
) => Promise<OrganizationsRegions | null>
|
||||||
) =>
|
) =>
|
||||||
async ({ userId, regionId, organizationId }: UserOrgRegionArgs) => {
|
async ({ userId, regionId, organizationId }: UserOrgRegionArgs) => {
|
||||||
if (!organizationId && regionId) { throw new Error("public org doesn't support regions") }
|
if (!organizationId && regionId) {
|
||||||
|
throw new Error("public org doesn't support regions")
|
||||||
|
}
|
||||||
if (organizationId) {
|
if (organizationId) {
|
||||||
if (!regionId) throw new Error('organizations can only write to regions')
|
if (!regionId) throw new Error('organizations can only write to regions')
|
||||||
const orgAcl = await orgAclGetter({ organizationId, userId })
|
const orgAcl = await orgAclGetter({ organizationId, userId })
|
||||||
if (orgAcl == null) { throw new Error("user doesn't have access to this organization") }
|
if (orgAcl == null) {
|
||||||
|
throw new Error("user doesn't have access to this organization")
|
||||||
|
}
|
||||||
const orgRegion = await orgRegionGetter({ organizationId, regionId })
|
const orgRegion = await orgRegionGetter({ organizationId, regionId })
|
||||||
if (orgRegion == null) { throw new Error('organization doesnt have access to this region') }
|
if (orgRegion == null) {
|
||||||
|
throw new Error('organization doesnt have access to this region')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ interface GetCommentsArgs extends PaginationArgs {
|
|||||||
resourceId: string
|
resourceId: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getComments =
|
export const getCommentsFactory =
|
||||||
(
|
(
|
||||||
countComments: (resourceId: string) => Promise<number>,
|
countComments: (resourceId: string) => Promise<number>,
|
||||||
queryComments: (params: GetCommentsArgs) => Promise<Comment[]>
|
queryComments: (params: GetCommentsArgs) => Promise<Comment[]>
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
import { POSTGRES_URL } from '../config'
|
import { POSTGRES_URL } from '../config'
|
||||||
import { RegionRepo, MainRepo } from '../repositories'
|
|
||||||
import knex, { Knex } from 'knex'
|
import knex, { Knex } from 'knex'
|
||||||
import cryptoRandomString from 'crypto-random-string'
|
import cryptoRandomString from 'crypto-random-string'
|
||||||
|
import {
|
||||||
|
findRegionFactory,
|
||||||
|
findResourceRegionFactory,
|
||||||
|
queryRegionsFactory,
|
||||||
|
saveOrganizationFactory,
|
||||||
|
saveRegionFactory
|
||||||
|
} from '../repositories'
|
||||||
|
|
||||||
const migrateToLatest = async (db: Knex): Promise<void> => {
|
const migrateToLatest = async (db: Knex): Promise<void> => {
|
||||||
const plannedMigrations: Array<{ file: string }> = (
|
const plannedMigrations: Array<{ file: string }> = (
|
||||||
@@ -21,11 +27,11 @@ const migrateToLatest = async (db: Knex): Promise<void> => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const migrateAll = async (): Promise<void> => {
|
export const migrateAll = async (): Promise<void> => {
|
||||||
await migrateToLatest(mainRepo.db)
|
await migrateToLatest(db)
|
||||||
const repos = await getAllRepositories()
|
const dbClients = await getAllDbClients()
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
...repos.map(async (repo) => await migrateToLatest(repo.db))
|
...dbClients.map(async (db) => await migrateToLatest(db))
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,29 +58,33 @@ const createDatabaseConfig = (
|
|||||||
return config
|
return config
|
||||||
}
|
}
|
||||||
|
|
||||||
const mainRepo = new MainRepo(knex(createDatabaseConfig(POSTGRES_URL, null)))
|
const db = knex(createDatabaseConfig(POSTGRES_URL, null))
|
||||||
|
|
||||||
const _repoStore: Map<string, RegionRepo> = new Map()
|
const dbClientStore: Map<string, Knex> = new Map()
|
||||||
export const getRegionRepo = async ({
|
|
||||||
|
const findRegion = findRegionFactory({ db })
|
||||||
|
|
||||||
|
export const getRegionDb = async ({
|
||||||
regionId
|
regionId
|
||||||
}: {
|
}: {
|
||||||
regionId: string | undefined
|
regionId: string | undefined
|
||||||
}): Promise<RegionRepo> => {
|
}): Promise<Knex> => {
|
||||||
if (!regionId) return mainRepo
|
if (!regionId) return db
|
||||||
const maybeRepo = _repoStore.get(regionId)
|
const maybeClient = dbClientStore.get(regionId)
|
||||||
if (maybeRepo != null) return maybeRepo
|
if (maybeClient != null) return maybeClient
|
||||||
const maybeRegion = await mainRepo.findRegion(regionId)
|
const maybeRegion = await findRegion(regionId)
|
||||||
if (maybeRegion == null) throw Error(`region ${regionId} not found`)
|
if (maybeRegion == null) throw Error(`region ${regionId} not found`)
|
||||||
const repo = new RegionRepo(
|
const client = knex(
|
||||||
knex(
|
createDatabaseConfig(maybeRegion.connectionString, maybeRegion.sslCaCert)
|
||||||
createDatabaseConfig(maybeRegion.connectionString, maybeRegion.sslCaCert)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
_repoStore.set(regionId, repo)
|
dbClientStore.set(regionId, client)
|
||||||
return repo
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMainRepo = (): MainRepo => mainRepo
|
export const getMainDbClient = (): Knex => db
|
||||||
|
|
||||||
|
const queryRegions = queryRegionsFactory({ db })
|
||||||
|
const saveRegion = saveRegionFactory({ db })
|
||||||
|
|
||||||
export const registerRegion = async ({
|
export const registerRegion = async ({
|
||||||
name,
|
name,
|
||||||
@@ -85,30 +95,28 @@ export const registerRegion = async ({
|
|||||||
connectionString: string
|
connectionString: string
|
||||||
sslCaCert: string | null
|
sslCaCert: string | null
|
||||||
}): Promise<string> => {
|
}): Promise<string> => {
|
||||||
const regions = await mainRepo.queryRegions({ connectionString })
|
const regions = await queryRegions({ connectionString })
|
||||||
if (regions.length > 0) throw new Error('This region is already registered')
|
if (regions.length > 0) throw new Error('This region is already registered')
|
||||||
const id = cryptoRandomString({ length: 10 })
|
const id = cryptoRandomString({ length: 10 })
|
||||||
const repo = new RegionRepo(
|
const newDb = knex(createDatabaseConfig(connectionString, sslCaCert))
|
||||||
knex(createDatabaseConfig(connectionString, sslCaCert))
|
await migrateToLatest(newDb)
|
||||||
)
|
dbClientStore.set(id, newDb)
|
||||||
await migrateToLatest(repo.db)
|
|
||||||
_repoStore.set(id, repo)
|
|
||||||
|
|
||||||
const sslmode = sslCaCert ? 'require' : 'disable'
|
const sslmode = sslCaCert ? 'require' : 'disable'
|
||||||
await setUpUserReplication({
|
await setUpUserReplication({
|
||||||
from: mainRepo.db,
|
from: db,
|
||||||
to: repo.db,
|
to: newDb,
|
||||||
regionName: name,
|
regionName: name,
|
||||||
sslmode
|
sslmode
|
||||||
})
|
})
|
||||||
await setUpResourceReplication({
|
await setUpResourceReplication({
|
||||||
from: repo.db,
|
from: newDb,
|
||||||
to: mainRepo.db,
|
to: db,
|
||||||
regionName: name,
|
regionName: name,
|
||||||
sslmode
|
sslmode
|
||||||
})
|
})
|
||||||
|
|
||||||
await mainRepo.saveRegion({
|
await saveRegion({
|
||||||
id,
|
id,
|
||||||
name,
|
name,
|
||||||
connectionString,
|
connectionString,
|
||||||
@@ -117,9 +125,11 @@ export const registerRegion = async ({
|
|||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const saveOrganization = saveOrganizationFactory({ db })
|
||||||
|
|
||||||
export const createOrganization = async (name: string): Promise<string> => {
|
export const createOrganization = async (name: string): Promise<string> => {
|
||||||
const id = cryptoRandomString({ length: 10 })
|
const id = cryptoRandomString({ length: 10 })
|
||||||
await mainRepo.saveOrganization({ id, name })
|
await saveOrganization({ id, name })
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,17 +206,17 @@ const setUpResourceReplication = async ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAllRepositories = async (): Promise<RegionRepo[]> => {
|
export const getAllDbClients = async (): Promise<Knex[]> => {
|
||||||
const regions = await mainRepo.queryRegions({})
|
const regions = await queryRegions({})
|
||||||
const regionRepos = await Promise.all(
|
const regionClients = await Promise.all(
|
||||||
regions.map(async (region) => await getRegionRepo({ regionId: region.id }))
|
regions.map(async (region) => await getRegionDb({ regionId: region.id }))
|
||||||
)
|
)
|
||||||
return [mainRepo, ...regionRepos]
|
return [db, ...regionClients]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getResourceRepo = async (
|
const findResourceRegion = findResourceRegionFactory({ db })
|
||||||
resourceId: string
|
|
||||||
): Promise<RegionRepo> => {
|
export const getResourceDb = async (resourceId: string): Promise<Knex> => {
|
||||||
const resourceRegion = await mainRepo.findResourceRegion({ resourceId })
|
const resourceRegion = await findResourceRegion({ resourceId })
|
||||||
return (resourceRegion != null) ? await getRegionRepo(resourceRegion) : getMainRepo()
|
return resourceRegion != null ? await getRegionDb(resourceRegion) : db
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import {
|
|||||||
interface GetResourcesArgs extends PaginationArgs {
|
interface GetResourcesArgs extends PaginationArgs {
|
||||||
userId: string
|
userId: string
|
||||||
}
|
}
|
||||||
export const getResources =
|
export const getResourcesFactory =
|
||||||
(
|
(
|
||||||
countResources: (userId: string) => Promise<number>,
|
countResources: (userId: string) => Promise<number>,
|
||||||
queryResources: (params: GetResourcesArgs) => Promise<Resource[]>
|
queryResources: (params: GetResourcesArgs) => Promise<Resource[]>
|
||||||
@@ -29,11 +29,14 @@ export const getResources =
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createResource =
|
export const createResourceFactory =
|
||||||
(
|
({
|
||||||
resourceSaver: (resource: Resource) => Promise<void>,
|
saveResource,
|
||||||
resourceAclSaver: (resourceAcl: ResourceAcl) => Promise<void>
|
saveResourceAcl
|
||||||
) =>
|
}: {
|
||||||
|
saveResource: (resource: Resource) => Promise<void>
|
||||||
|
saveResourceAcl: (resourceAcl: ResourceAcl) => Promise<void>
|
||||||
|
}) =>
|
||||||
async ({ userId, name }: ResourceCreateArgs): Promise<string> => {
|
async ({ userId, name }: ResourceCreateArgs): Promise<string> => {
|
||||||
// 1. if no org, create project in main region, validate that, regionId is null
|
// 1. if no org, create project in main region, validate that, regionId is null
|
||||||
// 2. if org, validate if user has access to the org
|
// 2. if org, validate if user has access to the org
|
||||||
@@ -41,7 +44,7 @@ export const createResource =
|
|||||||
// 4. create resource
|
// 4. create resource
|
||||||
const id = cryptoRandomString({ length: 10 })
|
const id = cryptoRandomString({ length: 10 })
|
||||||
const resource = { id, name, createdAt: new Date() }
|
const resource = { id, name, createdAt: new Date() }
|
||||||
await resourceSaver(resource)
|
await saveResource(resource)
|
||||||
await resourceAclSaver({ resourceId: id, userId })
|
await saveResourceAcl({ resourceId: id, userId })
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
import { expect, beforeAll, describe, it } from 'vitest'
|
|
||||||
import {
|
|
||||||
getOrganizationRegionsFrom,
|
|
||||||
getRegionsFrom
|
|
||||||
} from '../../src/repositories'
|
|
||||||
import {
|
|
||||||
getMainDbClient,
|
|
||||||
migrateAll
|
|
||||||
} from '../../src/services/databaseManagement'
|
|
||||||
import { Knex } from 'knex'
|
|
||||||
|
|
||||||
describe('regions', () => {
|
|
||||||
let dbClient: Knex
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
|
||||||
dbClient = await getMainDbClient()
|
|
||||||
})
|
|
||||||
it('gets all regions', async () => {
|
|
||||||
const regions = await getRegionsFrom(dbClient)()
|
|
||||||
expect(regions.length).toBeGreaterThan(0)
|
|
||||||
})
|
|
||||||
it('gets organizations regions', async () => {
|
|
||||||
const orgRegions = await getOrganizationRegionsFrom(dbClient)()
|
|
||||||
expect(orgRegions.length).toBeGreaterThan(0)
|
|
||||||
})
|
|
||||||
it('migrates all', async () => {
|
|
||||||
await migrateAll()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Reference in New Issue
Block a user