-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #6450 from Ocelot-Social-Community/messages
feat(backend): messages
- Loading branch information
Showing
7 changed files
with
391 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import gql from 'graphql-tag' | ||
|
||
export const createMessageMutation = () => { | ||
return gql` | ||
mutation ( | ||
$roomId: ID! | ||
$content: String! | ||
) { | ||
CreateMessage( | ||
roomId: $roomId | ||
content: $content | ||
) { | ||
id | ||
content | ||
} | ||
} | ||
` | ||
} | ||
|
||
export const messageQuery = () => { | ||
return gql` | ||
query($roomId: ID!) { | ||
Message(roomId: $roomId) { | ||
id | ||
content | ||
author { | ||
id | ||
} | ||
} | ||
} | ||
` | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,269 @@ | ||
import { createTestClient } from 'apollo-server-testing' | ||
import Factory, { cleanDatabase } from '../../db/factories' | ||
import { getNeode, getDriver } from '../../db/neo4j' | ||
import { createRoomMutation } from '../../graphql/rooms' | ||
import { createMessageMutation, messageQuery } from '../../graphql/messages' | ||
import createServer from '../../server' | ||
|
||
const driver = getDriver() | ||
const neode = getNeode() | ||
|
||
let query | ||
let mutate | ||
let authenticatedUser | ||
let chattingUser, otherChattingUser, notChattingUser | ||
|
||
beforeAll(async () => { | ||
await cleanDatabase() | ||
|
||
const { server } = createServer({ | ||
context: () => { | ||
return { | ||
driver, | ||
neode, | ||
user: authenticatedUser, | ||
} | ||
}, | ||
}) | ||
query = createTestClient(server).query | ||
mutate = createTestClient(server).mutate | ||
}) | ||
|
||
afterAll(async () => { | ||
await cleanDatabase() | ||
driver.close() | ||
}) | ||
|
||
|
||
describe('Message', () => { | ||
let roomId: string | ||
|
||
beforeAll(async () => { | ||
[chattingUser, otherChattingUser, notChattingUser] = await Promise.all([ | ||
Factory.build( | ||
'user', | ||
{ | ||
id: 'chatting-user', | ||
name: 'Chatting User', | ||
}, | ||
), | ||
Factory.build( | ||
'user', | ||
{ | ||
id: 'other-chatting-user', | ||
name: 'Other Chatting User', | ||
}, | ||
), | ||
Factory.build( | ||
'user', | ||
{ | ||
id: 'not-chatting-user', | ||
name: 'Not Chatting User', | ||
}, | ||
), | ||
]) | ||
}) | ||
|
||
describe('create message', () => { | ||
describe('unauthenticated', () => { | ||
it('throws authorization error', async () => { | ||
await expect(mutate({ mutation: createMessageMutation(), variables: { | ||
roomId: 'some-id', content: 'Some bla bla bla', } })).resolves.toMatchObject({ | ||
errors: [{ message: 'Not Authorized!' }], | ||
}) | ||
}) | ||
}) | ||
|
||
describe('authenticated', () => { | ||
beforeAll(async () => { | ||
authenticatedUser = await chattingUser.toJson() | ||
}) | ||
|
||
describe('room does not exist', () => { | ||
it('returns null', async () => { | ||
await expect(mutate({ mutation: createMessageMutation(), variables: { | ||
roomId: 'some-id', content: 'Some bla bla bla', } })).resolves.toMatchObject({ | ||
errors: undefined, | ||
data: { | ||
CreateMessage: null, | ||
}, | ||
}) | ||
}) | ||
}) | ||
|
||
describe('room exists', () => { | ||
beforeAll(async () => { | ||
const room = await mutate({ | ||
mutation: createRoomMutation(), | ||
variables: { | ||
userId: 'other-chatting-user', | ||
}, | ||
}) | ||
roomId = room.data.CreateRoom.id | ||
}) | ||
|
||
describe('user chats in room', () => { | ||
it('returns the message', async () => { | ||
await expect(mutate({ | ||
mutation: createMessageMutation(), | ||
variables: { | ||
roomId, | ||
content: 'Some nice message to other chatting user', | ||
} })).resolves.toMatchObject({ | ||
errors: undefined, | ||
data: { | ||
CreateMessage: { | ||
id: expect.any(String), | ||
content: 'Some nice message to other chatting user', | ||
}, | ||
}, | ||
}) | ||
}) | ||
}) | ||
|
||
describe('user does not chat in room', () => { | ||
beforeAll(async () => { | ||
authenticatedUser = await notChattingUser.toJson() | ||
}) | ||
|
||
it('returns null', async () => { | ||
await expect(mutate({ | ||
mutation: createMessageMutation(), | ||
variables: { | ||
roomId, | ||
content: 'I have no access to this room!', | ||
} })).resolves.toMatchObject({ | ||
errors: undefined, | ||
data: { | ||
CreateMessage: null, | ||
}, | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
|
||
describe('message query', () => { | ||
describe('unauthenticated', () => { | ||
beforeAll(() => { | ||
authenticatedUser = null | ||
}) | ||
|
||
it('throws authorization error', async () => { | ||
await expect(query({ | ||
query: messageQuery(), | ||
variables: { | ||
roomId: 'some-id' } | ||
})).resolves.toMatchObject({ | ||
errors: [{ message: 'Not Authorized!' }], | ||
}) | ||
}) | ||
}) | ||
|
||
describe('authenticated', () => { | ||
beforeAll(async () => { | ||
authenticatedUser = await otherChattingUser.toJson() | ||
}) | ||
|
||
describe('room does not exists', () => { | ||
it('returns null', async () => { | ||
await expect(query({ | ||
query: messageQuery(), | ||
variables: { | ||
roomId: 'some-id' | ||
}, | ||
})).resolves.toMatchObject({ | ||
errors: undefined, | ||
data: { | ||
Message: [], | ||
}, | ||
}) | ||
}) | ||
}) | ||
|
||
describe('room exists with authenticated user chatting', () => { | ||
it('returns the messages', async () => { | ||
await expect(query({ | ||
query: messageQuery(), | ||
variables: { | ||
roomId, | ||
}, | ||
})).resolves.toMatchObject({ | ||
errors: undefined, | ||
data: { | ||
Message: [{ | ||
id: expect.any(String), | ||
content: 'Some nice message to other chatting user', | ||
author: { | ||
id: 'chatting-user', | ||
}, | ||
}], | ||
}, | ||
}) | ||
}) | ||
|
||
describe('more messages', () => { | ||
beforeAll(async () => { | ||
await mutate({ | ||
mutation: createMessageMutation(), | ||
variables: { | ||
roomId, | ||
content: 'Another nice message to other chatting user', | ||
} | ||
}) | ||
}) | ||
|
||
it('returns the messages', async () => { | ||
await expect(query({ | ||
query: messageQuery(), | ||
variables: { | ||
roomId, | ||
}, | ||
})).resolves.toMatchObject({ | ||
errors: undefined, | ||
data: { | ||
Message: [ | ||
{ | ||
id: expect.any(String), | ||
content: 'Some nice message to other chatting user', | ||
author: { | ||
id: 'chatting-user', | ||
}, | ||
}, | ||
{ | ||
id: expect.any(String), | ||
content: 'Another nice message to other chatting user', | ||
author: { | ||
id: 'other-chatting-user', | ||
}, | ||
} | ||
], | ||
}, | ||
}) | ||
}) | ||
}) | ||
}) | ||
|
||
describe('room exists, authenticated user not in room', () => { | ||
beforeAll(async () => { | ||
authenticatedUser = await notChattingUser.toJson() | ||
}) | ||
|
||
it('returns null', async () => { | ||
await expect(query({ | ||
query: messageQuery(), | ||
variables: { | ||
roomId, | ||
}, | ||
})).resolves.toMatchObject({ | ||
errors: undefined, | ||
data: { | ||
Message: [], | ||
}, | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import { neo4jgraphql } from 'neo4j-graphql-js' | ||
import Resolver from './helpers/Resolver' | ||
|
||
export default { | ||
Query: { | ||
Message: async (object, params, context, resolveInfo) => { | ||
const { roomId } = params | ||
delete params.roomId | ||
if (!params.filter) params.filter = {} | ||
params.filter.room = { | ||
id: roomId, | ||
users_some: { | ||
id: context.user.id, | ||
}, | ||
} | ||
return neo4jgraphql(object, params, context, resolveInfo) | ||
}, | ||
}, | ||
Mutation: { | ||
CreateMessage: async (_parent, params, context, _resolveInfo) => { | ||
const { roomId, content } = params | ||
const { user: { id: currentUserId } } = context | ||
const session = context.driver.session() | ||
const writeTxResultPromise = session.writeTransaction(async (transaction) => { | ||
const createMessageCypher = ` | ||
MATCH (currentUser:User { id: $currentUserId })-[:CHATS_IN]->(room:Room { id: $roomId }) | ||
MERGE (currentUser)-[:CREATED]->(message:Message)-[:INSIDE]->(room) | ||
ON CREATE SET | ||
message.createdAt = toString(datetime()), | ||
message.id = apoc.create.uuid(), | ||
message.content = $content | ||
RETURN message { .* } | ||
` | ||
const createMessageTxResponse = await transaction.run( | ||
createMessageCypher, | ||
{ currentUserId, roomId, content } | ||
) | ||
const [message] = await createMessageTxResponse.records.map((record) => | ||
record.get('message'), | ||
) | ||
return message | ||
}) | ||
try { | ||
const message = await writeTxResultPromise | ||
return message | ||
} catch (error) { | ||
throw new Error(error) | ||
} finally { | ||
session.close() | ||
} | ||
}, | ||
}, | ||
Message: { | ||
...Resolver('Message', { | ||
hasOne: { | ||
author: '<-[:CREATED]-(related:User)', | ||
room: '-[:INSIDE]->(related:Room)', | ||
} | ||
}), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.