Skip to content

Commit

Permalink
Merge branch 'OpenBeta:develop' into fix/expose-hidden-db-fields
Browse files Browse the repository at this point in the history
  • Loading branch information
l4u532 committed Jan 9, 2024
2 parents cce1291 + 5055dbe commit 865f99a
Show file tree
Hide file tree
Showing 10 changed files with 81 additions and 43 deletions.
12 changes: 12 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@
],
"console": "integratedTerminal",
},
{
"type": "node",
"request": "launch",
"name": "Launch API Server (serve-dev)",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/src/main.ts",
"preLaunchTask": "tsc: build - tsconfig.json",
"outFiles": ["${workspaceFolder}/build/**/*.js"],
"runtimeExecutable": "yarn",
"runtimeArgs": ["run", "serve-dev"],
"console": "integratedTerminal"
},
{
"name": "Debug Jest Tests",
"type": "node",
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,18 @@ These links explain the structure and key abstractions of our codebase. It's a g

- GraphQL online playground: https://graphiql-online.com/

- Bypassing auth when developing locally

Start up your local server with `yarn serve-dev` (instead of `yarn serve`)
```bash
# Run this in open-tacos project
yarn serve-dev
```

This allows the current user to run any `Query` or `Mutation` (irrespective of the current user’s UUID)

(How it works: `auth/middleware.ts` and `auth/permissions.ts` are conditionally swapped out on server initialization (`server.ts`), based on whether the env var `LOCAL_DEV_BYPASS_AUTH` is set)

- Full stack development

Connect your [frontend](https://github.com/OpenBeta/open-tacos) dev env to this local server
Expand Down
5 changes: 5 additions & 0 deletions db-migrations/0005-area-sorting.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* Issue: 375
*/

db.areas.dropIndexes('metadata.leftRightIndex_1')
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
"build-release": "tsc -p tsconfig.release.json",
"clean": "tsc -b --clean && rm -rf build/*",
"serve": "yarn build && node --experimental-json-modules build/main.js",
"serve-dev": "echo \"🚨 LOCAL_DEV_BYPASS_AUTH enabled 🚨\" && LOCAL_DEV_BYPASS_AUTH=true yarn serve",
"refresh-db": "./refresh-db.sh",
"seed-usa": "yarn build && node build/db/import/usa/USADay0Seed.js",
"seed-db": "./seed-db.sh",
Expand Down
21 changes: 21 additions & 0 deletions src/auth/local-dev/middleware.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* This file is a mod of src/auth/middleware.ts and is used when starting the server via `yarn serve-dev`
* It bypasses the authentication for local development
*/
import muuid, { MUUID } from 'uuid-mongodb'
import { AuthUserType } from '../../types.js'
import { logger } from '../../logger.js'

export const localDevBypassAuthMiddleware = (() => {
const testUUID: MUUID = muuid.v4()

return async ({ req }): Promise<any> => {
const user: AuthUserType = {
roles: ['user_admin', 'org_admin', 'editor'],
uuid: testUUID,
isBuilder: false
}
logger.info(`The user.roles for this session is: ${user.roles.toString()}`)
return { user }
}
})()
19 changes: 19 additions & 0 deletions src/auth/local-dev/permissions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* This file is a mod of src/auth/permissions.ts and is used when starting the server via `yarn serve-dev`
* It bypasses the authorization for local development and allows all queries and mutations
*/
import { shield, allow } from 'graphql-shield'

const localDevBypassAuthPermissions = shield({
Query: {
'*': allow
},
Mutation: {
'*': allow
}
}, {
allowExternalErrors: true,
fallbackRule: allow
})

export default localDevBypassAuthPermissions
5 changes: 3 additions & 2 deletions src/db/AreaSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,9 @@ export const AreaSchema = new Schema<AreaType>({
}, { timestamps: true })

AreaSchema.index({ _deleting: 1 }, { expireAfterSeconds: 0 })
AreaSchema.index({ 'metadata.leftRightIndex': 1 }, {
unique: true,
AreaSchema.index({
'metadata.leftRightIndex': 1
}, {
partialFilterExpression: {
'metadata.leftRightIndex': {
$gt: -1
Expand Down
17 changes: 1 addition & 16 deletions src/model/MutableAreaDataSource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,21 +427,6 @@ export default class MutableAreaDataSource extends AreaDataSource {
const opType = OperationType.orderAreas
const change = await changelogDataSource.create(session, user, opType)
const updates: any[] = []
let expectedOpCount = input.length

// Clear existing indices so we can re-order without running into duplicate key errors.
if (input.some(i => i.leftRightIndex >= 0)) {
updates.push({
updateMany: {
filter: { 'metadata.area_id': { $in: input.map(i => muuid.from(i.areaId)) } },
update: {
$set: { 'metadata.leftRightIndex': -1 }
// Don't record change since this is an intermediate step.
}
}
})
expectedOpCount = expectedOpCount * 2
}

input.forEach(({ areaId, leftRightIndex }, index) => {
updates.push({
Expand All @@ -465,7 +450,7 @@ export default class MutableAreaDataSource extends AreaDataSource {

const rs = (await this.areaModel.bulkWrite(updates, { session })).toJSON()

if (rs.ok === 1 && rs.nMatched === rs.nModified && rs.nMatched === expectedOpCount) {
if (rs.ok === 1 && rs.nMatched === rs.nModified) {
return input.map(item => item.areaId)
} else {
throw new Error(`Expect to update ${input.length} areas but found ${rs.nMatched}.`)
Expand Down
22 changes: 0 additions & 22 deletions src/model/__tests__/updateAreas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -286,28 +286,6 @@ describe('Areas', () => {
leftRightIndex: change2.leftRightIndex
})
}))

// Able to overwrite existing leftRightIndices without duplicate key errors
const change3: UpdateSortingOrderType = {
areaId: a1.metadata.area_id.toUUID().toString(),
leftRightIndex: 9
}
const change4: UpdateSortingOrderType = {
areaId: a2.metadata.area_id.toUUID().toString(),
leftRightIndex: 10
}

await expect(areas.updateSortingOrder(testUser, [change3, change4])).resolves.toStrictEqual(
[a1.metadata.area_id.toUUID().toString(), a2.metadata.area_id.toUUID().toString()])

// Make sure we can't have duplicate leftToRight indices >= 0
await expect(
areas.updateSortingOrder(testUser, [{ ...change3, leftRightIndex: change4.leftRightIndex }]))
.rejects.toThrowError(/E11000/)

// But we can have duplicate indices < 0 to indicate unsorted
await areas.updateSortingOrder(testUser,
[{ ...change3, leftRightIndex: -1 }, { ...change4, leftRightIndex: -1 }])
})

it('should update self and childrens pathTokens', async () => {
Expand Down
10 changes: 7 additions & 3 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@ import ChangeLogDataSource from './model/ChangeLogDataSource.js'
import MutableMediaDataSource from './model/MutableMediaDataSource.js'
import MutableClimbDataSource from './model/MutableClimbDataSource.js'
import TickDataSource from './model/TickDataSource.js'
import { createContext, permissions } from './auth/index.js'
import { createContext } from './auth/middleware.js'
import permissions from './auth/permissions.js'
import { localDevBypassAuthMiddleware } from './auth/local-dev/middleware.js'
import localDevBypassAuthPermissions from './auth/local-dev/permissions.js'
import XMediaDataSource from './model/XMediaDataSource.js'
import PostDataSource from './model/PostDataSource.js'
import MutableOrgDS from './model/MutableOrganizationDataSource.js'
Expand All @@ -19,7 +22,7 @@ import UserDataSource from './model/UserDataSource.js'
export async function createServer (): Promise<ApolloServer> {
const schema = applyMiddleware(
graphqlSchema,
permissions.generate(graphqlSchema)
(process.env.LOCAL_DEV_BYPASS_AUTH === 'true' ? localDevBypassAuthPermissions : permissions).generate(graphqlSchema)
)
const dataSources: () => DataSources<Context> = () => ({
climbs: MutableClimbDataSource.getInstance(),
Expand All @@ -36,10 +39,11 @@ export async function createServer (): Promise<ApolloServer> {
xmedia: new XMediaDataSource(mongoose.connection.db.collection('xmedia')),
post: new PostDataSource(mongoose.connection.db.collection('post'))
})

const server = new ApolloServer({
introspection: true,
schema,
context: createContext,
context: process.env.LOCAL_DEV_BYPASS_AUTH === 'true' ? localDevBypassAuthMiddleware : createContext,
dataSources,
cache: 'bounded'
})
Expand Down

0 comments on commit 865f99a

Please sign in to comment.