mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-12 23:35:58 +00:00
refactor(backend): user graphql (#8354)
* refactor user graphql - remove isLoggedIn query - currentUser query only for authenticated, currenUser always returns a User - currentUser query implementation uses neo4jgraphql with id parameter - remove custom email field from user - fix bug in frontend when there is no categories * remove comment * remove unused filter * fix currentuser test * fixedswitchUserRole mutation * fix categories
This commit is contained in:
parent
fa0280f9e9
commit
117c0d75e7
@ -384,7 +384,7 @@ export default shield(
|
||||
Tag: allow,
|
||||
reports: isModerator,
|
||||
statistics: allow,
|
||||
currentUser: allow,
|
||||
currentUser: isAuthenticated,
|
||||
Group: isAuthenticated,
|
||||
GroupMembers: isAllowedSeeingGroupMembers,
|
||||
GroupCount: isAuthenticated,
|
||||
@ -392,7 +392,6 @@ export default shield(
|
||||
profilePagePosts: allow,
|
||||
Comment: allow,
|
||||
User: or(noEmailFilter, isAdmin),
|
||||
isLoggedIn: allow,
|
||||
Badge: allow,
|
||||
PostsEmotionsCountByEmotion: allow,
|
||||
PostsEmotionsByCurrentUser: isAuthenticated,
|
||||
|
||||
@ -64,55 +64,6 @@ afterEach(async () => {
|
||||
await cleanDatabase()
|
||||
})
|
||||
|
||||
describe('isLoggedIn', () => {
|
||||
const isLoggedInQuery = gql`
|
||||
{
|
||||
isLoggedIn
|
||||
}
|
||||
`
|
||||
const respondsWith = async (expected) => {
|
||||
await expect(query({ query: isLoggedInQuery })).resolves.toMatchObject(expected)
|
||||
}
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('returns false', async () => {
|
||||
await respondsWith({ data: { isLoggedIn: false } })
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated', () => {
|
||||
beforeEach(async () => {
|
||||
user = await Factory.build('user', { id: 'u3' })
|
||||
const userBearerToken = encode({ id: 'u3' })
|
||||
req = { headers: { authorization: `Bearer ${userBearerToken}` } }
|
||||
})
|
||||
|
||||
it('returns true', async () => {
|
||||
await respondsWith({ data: { isLoggedIn: true } })
|
||||
})
|
||||
|
||||
describe('but user is disabled', () => {
|
||||
beforeEach(async () => {
|
||||
await disable('u3')
|
||||
})
|
||||
|
||||
it('returns false', async () => {
|
||||
await respondsWith({ data: { isLoggedIn: false } })
|
||||
})
|
||||
})
|
||||
|
||||
describe('but user is deleted', () => {
|
||||
beforeEach(async () => {
|
||||
await user.update({ updatedAt: new Date().toISOString(), deleted: true })
|
||||
})
|
||||
|
||||
it('returns false', async () => {
|
||||
await respondsWith({ data: { isLoggedIn: false } })
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('currentUser', () => {
|
||||
const currentUserQuery = gql`
|
||||
{
|
||||
@ -135,8 +86,8 @@ describe('currentUser', () => {
|
||||
}
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('returns null', async () => {
|
||||
await respondsWith({ data: { currentUser: null } })
|
||||
it('throws "Not Authorized!"', async () => {
|
||||
await respondsWith({ errors: [{ message: 'Not Authorized!' }] })
|
||||
})
|
||||
})
|
||||
|
||||
@ -200,10 +151,32 @@ describe('currentUser', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('returns empty array for all categories', async () => {
|
||||
it('returns all categories by default', async () => {
|
||||
await respondsWith({
|
||||
data: {
|
||||
currentUser: expect.objectContaining({ activeCategories: [] }),
|
||||
currentUser: expect.objectContaining({
|
||||
activeCategories: [
|
||||
'cat1',
|
||||
'cat10',
|
||||
'cat11',
|
||||
'cat12',
|
||||
'cat13',
|
||||
'cat14',
|
||||
'cat15',
|
||||
'cat16',
|
||||
'cat17',
|
||||
'cat18',
|
||||
'cat19',
|
||||
'cat2',
|
||||
'cat3',
|
||||
'cat4',
|
||||
'cat5',
|
||||
'cat6',
|
||||
'cat7',
|
||||
'cat8',
|
||||
'cat9',
|
||||
],
|
||||
}),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { AuthenticationError } from 'apollo-server'
|
||||
import bcrypt from 'bcryptjs'
|
||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
|
||||
import { getNeode } from '@db/neo4j'
|
||||
import encode from '@jwt/encode'
|
||||
@ -11,38 +12,8 @@ const neode = getNeode()
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
isLoggedIn: (_, args, { driver, user }) => {
|
||||
return Boolean(user && user.id)
|
||||
},
|
||||
currentUser: async (object, params, context, resolveInfo) => {
|
||||
const { user, driver } = context
|
||||
if (!user) return null
|
||||
const session = driver.session()
|
||||
const currentUserTransactionPromise = session.readTransaction(async (transaction) => {
|
||||
const result = await transaction.run(
|
||||
`
|
||||
MATCH (user:User {id: $id})
|
||||
OPTIONAL MATCH (category:Category) WHERE NOT ((user)-[:NOT_INTERESTED_IN]->(category))
|
||||
OPTIONAL MATCH (cats:Category)
|
||||
WITH user, [(user)<-[:OWNED_BY]-(medium:SocialMedia) | properties(medium) ] AS media, category, toString(COUNT(cats)) AS categoryCount
|
||||
RETURN user {.*, socialMedia: media, activeCategories: collect(category.id) } AS user, categoryCount
|
||||
`,
|
||||
{ id: user.id },
|
||||
)
|
||||
const [categoryCount] = result.records.map((record) => record.get('categoryCount'))
|
||||
const [currentUser] = result.records.map((record) => record.get('user'))
|
||||
// frontend expects empty array when all categories are selected
|
||||
if (currentUser.activeCategories.length === parseInt(categoryCount))
|
||||
currentUser.activeCategories = []
|
||||
return currentUser
|
||||
})
|
||||
try {
|
||||
const currentUser = await currentUserTransactionPromise
|
||||
return currentUser
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
},
|
||||
currentUser: async (object, params, context, resolveInfo) =>
|
||||
neo4jgraphql(object, { id: context.user.id }, context, resolveInfo),
|
||||
},
|
||||
Mutation: {
|
||||
login: async (_, { email, password }, { driver, req, user }) => {
|
||||
|
||||
@ -66,12 +66,12 @@ export default {
|
||||
const result = txc.run(
|
||||
`
|
||||
MATCH (user:User)-[:PRIMARY_EMAIL]->(e:EmailAddress {email: $args.email})
|
||||
RETURN user`,
|
||||
RETURN user {.*, email: e.email}`,
|
||||
{ args },
|
||||
)
|
||||
return result
|
||||
})
|
||||
return readTxResult.records.map((r) => r.get('user').properties)
|
||||
return readTxResult.records.map((r) => r.get('user'))
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
@ -291,14 +291,14 @@ export default {
|
||||
const switchUserRoleResponse = await transaction.run(
|
||||
`
|
||||
MATCH (user:User {id: $id})
|
||||
OPTIONAL MATCH (user)-[:PRIMARY_EMAIL]->(e:EmailAddress)
|
||||
SET user.role = $role
|
||||
SET user.updatedAt = toString(datetime())
|
||||
RETURN user {.*}
|
||||
RETURN user {.*, email: e.email}
|
||||
`,
|
||||
{ id, role },
|
||||
)
|
||||
const [user] = switchUserRoleResponse.records.map((record) => record.get('user'))
|
||||
return user
|
||||
return switchUserRoleResponse.records.map((record) => record.get('user'))[0]
|
||||
})
|
||||
try {
|
||||
const user = await writeTxResultPromise
|
||||
@ -383,14 +383,6 @@ export default {
|
||||
},
|
||||
},
|
||||
User: {
|
||||
email: async (parent, params, context, resolveInfo) => {
|
||||
if (typeof parent.email !== 'undefined') return parent.email
|
||||
const { id } = parent
|
||||
const statement = `MATCH(u:User {id: $id})-[:PRIMARY_EMAIL]->(e:EmailAddress) RETURN e`
|
||||
const result = await neode.cypher(statement, { id })
|
||||
const [{ email }] = result.records.map((r) => r.get('e').properties)
|
||||
return email
|
||||
},
|
||||
emailNotificationSettings: async (parent, params, context, resolveInfo) => {
|
||||
return [
|
||||
{
|
||||
|
||||
@ -199,8 +199,7 @@ type Query {
|
||||
availableRoles: [UserRole]!
|
||||
mutedUsers: [User]
|
||||
blockedUsers: [User]
|
||||
isLoggedIn: Boolean!
|
||||
currentUser: User
|
||||
currentUser: User!
|
||||
}
|
||||
|
||||
enum Deletable {
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
import { createTestClient } from 'apollo-server-testing'
|
||||
|
||||
import createServer from './server'
|
||||
|
||||
/**
|
||||
* This file is for demonstration purposes. It does not really test the
|
||||
* `isLoggedIn` query but demonstrates how we can use `apollo-server-testing`.
|
||||
* All we need to do is to get an instance of `ApolloServer` and maybe we want
|
||||
* stub out `context` as shown below.
|
||||
*
|
||||
*/
|
||||
|
||||
let user
|
||||
let action
|
||||
describe('isLoggedIn', () => {
|
||||
beforeEach(() => {
|
||||
action = async () => {
|
||||
const { server } = createServer({
|
||||
context: () => {
|
||||
return {
|
||||
user,
|
||||
}
|
||||
},
|
||||
})
|
||||
const { query } = createTestClient(server)
|
||||
|
||||
const isLoggedIn = `{ isLoggedIn }`
|
||||
return query({ query: isLoggedIn })
|
||||
}
|
||||
})
|
||||
|
||||
it('returns false', async () => {
|
||||
const expected = expect.objectContaining({ data: { isLoggedIn: false } })
|
||||
await expect(action()).resolves.toEqual(expected)
|
||||
})
|
||||
|
||||
describe('when authenticated', () => {
|
||||
it('returns true', async () => {
|
||||
user = { id: '123' }
|
||||
const expected = expect.objectContaining({ data: { isLoggedIn: true } })
|
||||
await expect(action()).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -100,7 +100,7 @@ export default {
|
||||
await this.$store.dispatch('auth/login', { email, password })
|
||||
if (this.currentUser && this.currentUser.activeCategories) {
|
||||
this.resetCategories()
|
||||
if (this.currentUser.activeCategories.length > 0) {
|
||||
if (this.currentUser.activeCategories.length < 19) {
|
||||
this.currentUser.activeCategories.forEach((categoryId) => {
|
||||
this.toggleCategory(categoryId)
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user