mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge pull request #1016 from Human-Connection/277_reward_badges
Refactor reward/unreward Badges in backend
This commit is contained in:
commit
4c91d8fbc5
@ -146,6 +146,7 @@ const permissions = shield(
|
||||
Comment: allow,
|
||||
User: or(noEmailFilter, isAdmin),
|
||||
isLoggedIn: allow,
|
||||
Badge: allow,
|
||||
},
|
||||
Mutation: {
|
||||
'*': deny,
|
||||
@ -160,9 +161,6 @@ const permissions = shield(
|
||||
UpdatePost: isAuthor,
|
||||
DeletePost: isAuthor,
|
||||
report: isAuthenticated,
|
||||
CreateBadge: isAdmin,
|
||||
UpdateBadge: isAdmin,
|
||||
DeleteBadge: isAdmin,
|
||||
CreateSocialMedia: isAuthenticated,
|
||||
DeleteSocialMedia: isAuthenticated,
|
||||
// AddBadgeRewarded: isAdmin,
|
||||
|
||||
7
backend/src/models/Badge.js
Normal file
7
backend/src/models/Badge.js
Normal file
@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
id: { type: 'string', primary: true, lowercase: true },
|
||||
status: { type: 'string', valid: ['permanent', 'temporary'] },
|
||||
type: { type: 'string', valid: ['role', 'crowdfunding'] },
|
||||
icon: { type: 'string', required: true },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
}
|
||||
@ -43,6 +43,12 @@ module.exports = {
|
||||
target: 'User',
|
||||
direction: 'in',
|
||||
},
|
||||
rewarded: {
|
||||
type: 'relationship',
|
||||
relationship: 'REWARDED',
|
||||
target: 'Badge',
|
||||
direction: 'in',
|
||||
},
|
||||
invitedBy: { type: 'relationship', relationship: 'INVITED', target: 'User', direction: 'in' },
|
||||
createdAt: { type: 'string', isoDate: true, default: () => new Date().toISOString() },
|
||||
updatedAt: {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// NOTE: We cannot use `fs` here to clean up the code. Cypress breaks on any npm
|
||||
// module that is not browser-compatible. Node's `fs` module is server-side only
|
||||
export default {
|
||||
Badge: require('./Badge.js'),
|
||||
User: require('./User.js'),
|
||||
InvitationCode: require('./InvitationCode.js'),
|
||||
EmailAddress: require('./EmailAddress.js'),
|
||||
|
||||
@ -12,11 +12,25 @@ export default applyScalars(
|
||||
resolvers,
|
||||
config: {
|
||||
query: {
|
||||
exclude: ['InvitationCode', 'EmailAddress', 'Notfication', 'Statistics', 'LoggedInUser'],
|
||||
exclude: [
|
||||
'Badge',
|
||||
'InvitationCode',
|
||||
'EmailAddress',
|
||||
'Notfication',
|
||||
'Statistics',
|
||||
'LoggedInUser',
|
||||
],
|
||||
// add 'User' here as soon as possible
|
||||
},
|
||||
mutation: {
|
||||
exclude: ['InvitationCode', 'EmailAddress', 'Notfication', 'Statistics', 'LoggedInUser'],
|
||||
exclude: [
|
||||
'Badge',
|
||||
'InvitationCode',
|
||||
'EmailAddress',
|
||||
'Notfication',
|
||||
'Statistics',
|
||||
'LoggedInUser',
|
||||
],
|
||||
// add 'User' here as soon as possible
|
||||
},
|
||||
debug: CONFIG.DEBUG,
|
||||
|
||||
9
backend/src/schema/resolvers/badges.js
Normal file
9
backend/src/schema/resolvers/badges.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { neo4jgraphql } from 'neo4j-graphql-js'
|
||||
|
||||
export default {
|
||||
Query: {
|
||||
Badge: async (object, args, context, resolveInfo) => {
|
||||
return neo4jgraphql(object, args, context, resolveInfo, false)
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -1,200 +0,0 @@
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import Factory from '../../seed/factories'
|
||||
import { host, login } from '../../jest/helpers'
|
||||
|
||||
const factory = Factory()
|
||||
let client
|
||||
|
||||
describe('badges', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', {
|
||||
email: 'user@example.org',
|
||||
role: 'user',
|
||||
password: '1234',
|
||||
})
|
||||
await factory.create('User', {
|
||||
id: 'u2',
|
||||
role: 'moderator',
|
||||
email: 'moderator@example.org',
|
||||
})
|
||||
await factory.create('User', {
|
||||
id: 'u3',
|
||||
role: 'admin',
|
||||
email: 'admin@example.org',
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
await factory.cleanDatabase()
|
||||
})
|
||||
|
||||
describe('CreateBadge', () => {
|
||||
const variables = {
|
||||
id: 'b1',
|
||||
key: 'indiegogo_en_racoon',
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
icon: '/img/badges/indiegogo_en_racoon.svg',
|
||||
}
|
||||
|
||||
const mutation = `
|
||||
mutation(
|
||||
$id: ID
|
||||
$key: String!
|
||||
$type: BadgeType!
|
||||
$status: BadgeStatus!
|
||||
$icon: String!
|
||||
) {
|
||||
CreateBadge(id: $id, key: $key, type: $type, status: $status, icon: $icon) {
|
||||
id,
|
||||
key,
|
||||
type,
|
||||
status,
|
||||
icon
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
client = new GraphQLClient(host)
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated admin', () => {
|
||||
beforeEach(async () => {
|
||||
const headers = await login({ email: 'admin@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
it('creates a badge', async () => {
|
||||
const expected = {
|
||||
CreateBadge: {
|
||||
icon: '/img/badges/indiegogo_en_racoon.svg',
|
||||
id: 'b1',
|
||||
key: 'indiegogo_en_racoon',
|
||||
status: 'permanent',
|
||||
type: 'crowdfunding',
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated moderator', () => {
|
||||
beforeEach(async () => {
|
||||
const headers = await login({ email: 'moderator@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('UpdateBadge', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.authenticateAs({ email: 'admin@example.org', password: '1234' })
|
||||
await factory.create('Badge', { id: 'b1' })
|
||||
})
|
||||
const variables = {
|
||||
id: 'b1',
|
||||
key: 'whatever',
|
||||
}
|
||||
|
||||
const mutation = `
|
||||
mutation($id: ID!, $key: String!) {
|
||||
UpdateBadge(id: $id, key: $key) {
|
||||
id
|
||||
key
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
client = new GraphQLClient(host)
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated moderator', () => {
|
||||
beforeEach(async () => {
|
||||
const headers = await login({ email: 'moderator@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated admin', () => {
|
||||
beforeEach(async () => {
|
||||
const headers = await login({ email: 'admin@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
it('updates a badge', async () => {
|
||||
const expected = {
|
||||
UpdateBadge: {
|
||||
id: 'b1',
|
||||
key: 'whatever',
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('DeleteBadge', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.authenticateAs({ email: 'admin@example.org', password: '1234' })
|
||||
await factory.create('Badge', { id: 'b1' })
|
||||
})
|
||||
const variables = {
|
||||
id: 'b1',
|
||||
}
|
||||
|
||||
const mutation = `
|
||||
mutation($id: ID!) {
|
||||
DeleteBadge(id: $id) {
|
||||
id
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
it('throws authorization error', async () => {
|
||||
client = new GraphQLClient(host)
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated moderator', () => {
|
||||
beforeEach(async () => {
|
||||
const headers = await login({ email: 'moderator@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow('Not Authorised')
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated admin', () => {
|
||||
beforeEach(async () => {
|
||||
const headers = await login({ email: 'admin@example.org', password: '1234' })
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
it('deletes a badge', async () => {
|
||||
const expected = {
|
||||
DeleteBadge: {
|
||||
id: 'b1',
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@ -1,47 +1,47 @@
|
||||
import { neode } from '../../bootstrap/neo4j'
|
||||
import { UserInputError } from 'apollo-server'
|
||||
|
||||
const instance = neode()
|
||||
|
||||
const getUserAndBadge = async ({ badgeKey, userId }) => {
|
||||
let user = await instance.first('User', 'id', userId)
|
||||
const badge = await instance.first('Badge', 'id', badgeKey)
|
||||
if (!user) throw new UserInputError("Couldn't find a user with that id")
|
||||
if (!badge) throw new UserInputError("Couldn't find a badge with that id")
|
||||
return { user, badge }
|
||||
}
|
||||
|
||||
export default {
|
||||
Mutation: {
|
||||
reward: async (_object, params, context, _resolveInfo) => {
|
||||
const { fromBadgeId, toUserId } = params
|
||||
const session = context.driver.session()
|
||||
|
||||
let transactionRes = await session.run(
|
||||
`MATCH (badge:Badge {id: $badgeId}), (rewardedUser:User {id: $rewardedUserId})
|
||||
MERGE (badge)-[:REWARDED]->(rewardedUser)
|
||||
RETURN rewardedUser {.id}`,
|
||||
{
|
||||
badgeId: fromBadgeId,
|
||||
rewardedUserId: toUserId,
|
||||
},
|
||||
)
|
||||
|
||||
const [rewardedUser] = transactionRes.records.map(record => {
|
||||
return record.get('rewardedUser')
|
||||
})
|
||||
|
||||
session.close()
|
||||
|
||||
return rewardedUser.id
|
||||
const { user, badge } = await getUserAndBadge(params)
|
||||
await user.relateTo(badge, 'rewarded')
|
||||
return user.toJson()
|
||||
},
|
||||
|
||||
unreward: async (_object, params, context, _resolveInfo) => {
|
||||
const { fromBadgeId, toUserId } = params
|
||||
const { badgeKey, userId } = params
|
||||
const { user } = await getUserAndBadge(params)
|
||||
const session = context.driver.session()
|
||||
|
||||
let transactionRes = await session.run(
|
||||
`MATCH (badge:Badge {id: $badgeId})-[reward:REWARDED]->(rewardedUser:User {id: $rewardedUserId})
|
||||
DELETE reward
|
||||
RETURN rewardedUser {.id}`,
|
||||
{
|
||||
badgeId: fromBadgeId,
|
||||
rewardedUserId: toUserId,
|
||||
},
|
||||
)
|
||||
const [rewardedUser] = transactionRes.records.map(record => {
|
||||
return record.get('rewardedUser')
|
||||
})
|
||||
session.close()
|
||||
|
||||
return rewardedUser.id
|
||||
try {
|
||||
// silly neode cannot remove relationships
|
||||
await session.run(
|
||||
`
|
||||
MATCH (badge:Badge {id: $badgeKey})-[reward:REWARDED]->(rewardedUser:User {id: $userId})
|
||||
DELETE reward
|
||||
RETURN rewardedUser
|
||||
`,
|
||||
{
|
||||
badgeKey,
|
||||
userId,
|
||||
},
|
||||
)
|
||||
} catch (err) {
|
||||
throw err
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
return user.toJson()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,12 +1,20 @@
|
||||
import { GraphQLClient } from 'graphql-request'
|
||||
import Factory from '../../seed/factories'
|
||||
import { host, login } from '../../jest/helpers'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
const factory = Factory()
|
||||
let user
|
||||
let badge
|
||||
|
||||
describe('rewards', () => {
|
||||
const variables = {
|
||||
from: 'indiegogo_en_rhino',
|
||||
to: 'u1',
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await factory.create('User', {
|
||||
user = await factory.create('User', {
|
||||
id: 'u1',
|
||||
role: 'user',
|
||||
email: 'user@example.org',
|
||||
@ -22,9 +30,8 @@ describe('rewards', () => {
|
||||
role: 'admin',
|
||||
email: 'admin@example.org',
|
||||
})
|
||||
await factory.create('Badge', {
|
||||
id: 'b6',
|
||||
key: 'indiegogo_en_rhino',
|
||||
badge = await factory.create('Badge', {
|
||||
id: 'indiegogo_en_rhino',
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
icon: '/img/badges/indiegogo_en_rhino.svg',
|
||||
@ -35,21 +42,19 @@ describe('rewards', () => {
|
||||
await factory.cleanDatabase()
|
||||
})
|
||||
|
||||
describe('RewardBadge', () => {
|
||||
const mutation = `
|
||||
mutation(
|
||||
$from: ID!
|
||||
$to: ID!
|
||||
) {
|
||||
reward(fromBadgeId: $from, toUserId: $to)
|
||||
describe('reward', () => {
|
||||
const mutation = gql`
|
||||
mutation($from: ID!, $to: ID!) {
|
||||
reward(badgeKey: $from, userId: $to) {
|
||||
id
|
||||
badges {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
const variables = {
|
||||
from: 'b6',
|
||||
to: 'u1',
|
||||
}
|
||||
let client
|
||||
|
||||
it('throws authorization error', async () => {
|
||||
@ -65,74 +70,94 @@ describe('rewards', () => {
|
||||
client = new GraphQLClient(host, { headers })
|
||||
})
|
||||
|
||||
describe('badge for id does not exist', () => {
|
||||
it('rejects with a telling error message', async () => {
|
||||
await expect(
|
||||
client.request(mutation, {
|
||||
...variables,
|
||||
from: 'bullshit',
|
||||
}),
|
||||
).rejects.toThrow("Couldn't find a badge with that id")
|
||||
})
|
||||
})
|
||||
|
||||
describe('user for id does not exist', () => {
|
||||
it('rejects with a telling error message', async () => {
|
||||
await expect(
|
||||
client.request(mutation, {
|
||||
...variables,
|
||||
to: 'bullshit',
|
||||
}),
|
||||
).rejects.toThrow("Couldn't find a user with that id")
|
||||
})
|
||||
})
|
||||
|
||||
it('rewards a badge to user', async () => {
|
||||
const variables = {
|
||||
from: 'b6',
|
||||
to: 'u1',
|
||||
}
|
||||
const expected = {
|
||||
reward: 'u1',
|
||||
reward: {
|
||||
id: 'u1',
|
||||
badges: [{ id: 'indiegogo_en_rhino' }],
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
})
|
||||
|
||||
it('rewards a second different badge to same user', async () => {
|
||||
await factory.create('Badge', {
|
||||
id: 'b1',
|
||||
key: 'indiegogo_en_racoon',
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
id: 'indiegogo_en_racoon',
|
||||
icon: '/img/badges/indiegogo_en_racoon.svg',
|
||||
})
|
||||
const variables = {
|
||||
from: 'b1',
|
||||
to: 'u1',
|
||||
}
|
||||
const expected = {
|
||||
reward: 'u1',
|
||||
reward: {
|
||||
id: 'u1',
|
||||
badges: [{ id: 'indiegogo_en_racoon' }, { id: 'indiegogo_en_rhino' }],
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
await client.request(mutation, variables)
|
||||
await expect(
|
||||
client.request(mutation, {
|
||||
...variables,
|
||||
from: 'indiegogo_en_racoon',
|
||||
}),
|
||||
).resolves.toEqual(expected)
|
||||
})
|
||||
|
||||
it('rewards the same badge as well to another user', async () => {
|
||||
const variables1 = {
|
||||
from: 'b6',
|
||||
to: 'u1',
|
||||
}
|
||||
await client.request(mutation, variables1)
|
||||
|
||||
const variables2 = {
|
||||
from: 'b6',
|
||||
to: 'u2',
|
||||
}
|
||||
const expected = {
|
||||
reward: 'u2',
|
||||
reward: {
|
||||
id: 'u2',
|
||||
badges: [{ id: 'indiegogo_en_rhino' }],
|
||||
},
|
||||
}
|
||||
await expect(client.request(mutation, variables2)).resolves.toEqual(expected)
|
||||
await expect(
|
||||
client.request(mutation, {
|
||||
...variables,
|
||||
to: 'u2',
|
||||
}),
|
||||
).resolves.toEqual(expected)
|
||||
})
|
||||
it('returns the original reward if a reward is attempted a second time', async () => {
|
||||
const variables = {
|
||||
from: 'b6',
|
||||
to: 'u1',
|
||||
}
|
||||
|
||||
it('creates no duplicate reward relationships', async () => {
|
||||
await client.request(mutation, variables)
|
||||
await client.request(mutation, variables)
|
||||
|
||||
const query = `{
|
||||
User( id: "u1" ) {
|
||||
badgesCount
|
||||
const query = gql`
|
||||
{
|
||||
User(id: "u1") {
|
||||
badgesCount
|
||||
badges {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const expected = { User: [{ badgesCount: 1 }] }
|
||||
const expected = { User: [{ badgesCount: 1, badges: [{ id: 'indiegogo_en_rhino' }] }] }
|
||||
|
||||
await expect(client.request(query)).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('authenticated moderator', () => {
|
||||
const variables = {
|
||||
from: 'b6',
|
||||
to: 'u1',
|
||||
}
|
||||
let client
|
||||
beforeEach(async () => {
|
||||
const headers = await login({ email: 'moderator@example.org', password: '1234' })
|
||||
@ -147,27 +172,41 @@ describe('rewards', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('RemoveReward', () => {
|
||||
describe('unreward', () => {
|
||||
beforeEach(async () => {
|
||||
await factory.relate('User', 'Badges', { from: 'b6', to: 'u1' })
|
||||
await user.relateTo(badge, 'rewarded')
|
||||
})
|
||||
const variables = {
|
||||
from: 'b6',
|
||||
to: 'u1',
|
||||
}
|
||||
const expected = {
|
||||
unreward: 'u1',
|
||||
}
|
||||
const expected = { unreward: { id: 'u1', badges: [] } }
|
||||
|
||||
const mutation = `
|
||||
mutation(
|
||||
$from: ID!
|
||||
$to: ID!
|
||||
) {
|
||||
unreward(fromBadgeId: $from, toUserId: $to)
|
||||
const mutation = gql`
|
||||
mutation($from: ID!, $to: ID!) {
|
||||
unreward(badgeKey: $from, userId: $to) {
|
||||
id
|
||||
badges {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
describe('check test setup', () => {
|
||||
it('user has one badge', async () => {
|
||||
const query = gql`
|
||||
{
|
||||
User(id: "u1") {
|
||||
badgesCount
|
||||
badges {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
const expected = { User: [{ badgesCount: 1, badges: [{ id: 'indiegogo_en_rhino' }] }] }
|
||||
const client = new GraphQLClient(host)
|
||||
await expect(client.request(query)).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
describe('unauthenticated', () => {
|
||||
let client
|
||||
|
||||
@ -188,12 +227,9 @@ describe('rewards', () => {
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
})
|
||||
|
||||
it('fails to remove a not existing badge from user', async () => {
|
||||
it('does not crash when unrewarding multiple times', async () => {
|
||||
await client.request(mutation, variables)
|
||||
|
||||
await expect(client.request(mutation, variables)).rejects.toThrow(
|
||||
"Cannot read property 'id' of undefined",
|
||||
)
|
||||
await expect(client.request(mutation, variables)).resolves.toEqual(expected)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@ -139,7 +139,7 @@ export default {
|
||||
organizationsCreated: '-[:CREATED_ORGA]->(related:Organization)',
|
||||
organizationsOwned: '-[:OWNING_ORGA]->(related:Organization)',
|
||||
categories: '-[:CATEGORIZED]->(related:Category)',
|
||||
badges: '-[:REWARDED]->(related:Badge)',
|
||||
badges: '<-[:REWARDED]-(related:Badge)',
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
@ -147,7 +147,7 @@ describe('users', () => {
|
||||
}
|
||||
`
|
||||
beforeEach(async () => {
|
||||
asAuthor = await factory.create('User', {
|
||||
await factory.create('User', {
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
id: 'u343',
|
||||
@ -191,6 +191,7 @@ describe('users', () => {
|
||||
describe('attempting to delete my own account', () => {
|
||||
let expectedResponse
|
||||
beforeEach(async () => {
|
||||
asAuthor = Factory()
|
||||
await asAuthor.authenticateAs({
|
||||
email: 'test@example.org',
|
||||
password: '1234',
|
||||
|
||||
@ -1,4 +0,0 @@
|
||||
enum BadgeStatus {
|
||||
permanent
|
||||
temporary
|
||||
}
|
||||
@ -1,4 +0,0 @@
|
||||
enum BadgeType {
|
||||
role
|
||||
crowdfunding
|
||||
}
|
||||
@ -28,8 +28,6 @@ type Mutation {
|
||||
report(id: ID!, description: String): Report
|
||||
disable(id: ID!): ID
|
||||
enable(id: ID!): ID
|
||||
reward(fromBadgeId: ID!, toUserId: ID!): ID
|
||||
unreward(fromBadgeId: ID!, toUserId: ID!): ID
|
||||
# Shout the given Type and ID
|
||||
shout(id: ID!, type: ShoutTypeEnum): Boolean!
|
||||
# Unshout the given Type and ID
|
||||
|
||||
@ -1,324 +0,0 @@
|
||||
scalar Upload
|
||||
|
||||
type Query {
|
||||
isLoggedIn: Boolean!
|
||||
# Get the currently logged in User based on the given JWT Token
|
||||
currentUser: User
|
||||
# Get the latest Network Statistics
|
||||
statistics: Statistics!
|
||||
findPosts(filter: String!, limit: Int = 10): [Post]! @cypher(
|
||||
statement: """
|
||||
CALL db.index.fulltext.queryNodes('full_text_search', $filter)
|
||||
YIELD node as post, score
|
||||
MATCH (post)<-[:WROTE]-(user:User)
|
||||
WHERE score >= 0.2
|
||||
AND NOT user.deleted = true AND NOT user.disabled = true
|
||||
AND NOT post.deleted = true AND NOT post.disabled = true
|
||||
RETURN post
|
||||
LIMIT $limit
|
||||
"""
|
||||
)
|
||||
CommentByPost(postId: ID!): [Comment]!
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
# Get a JWT Token for the given Email and password
|
||||
login(email: String!, password: String!): String!
|
||||
signup(email: String!, password: String!): Boolean!
|
||||
changePassword(oldPassword:String!, newPassword: String!): String!
|
||||
report(id: ID!, description: String): Report
|
||||
disable(id: ID!): ID
|
||||
enable(id: ID!): ID
|
||||
reward(fromBadgeId: ID!, toUserId: ID!): ID
|
||||
unreward(fromBadgeId: ID!, toUserId: ID!): ID
|
||||
# Shout the given Type and ID
|
||||
shout(id: ID!, type: ShoutTypeEnum): Boolean!
|
||||
# Unshout the given Type and ID
|
||||
unshout(id: ID!, type: ShoutTypeEnum): Boolean!
|
||||
# Follow the given Type and ID
|
||||
follow(id: ID!, type: FollowTypeEnum): Boolean!
|
||||
# Unfollow the given Type and ID
|
||||
unfollow(id: ID!, type: FollowTypeEnum): Boolean!
|
||||
}
|
||||
|
||||
type Statistics {
|
||||
countUsers: Int!
|
||||
countPosts: Int!
|
||||
countComments: Int!
|
||||
countNotifications: Int!
|
||||
countOrganizations: Int!
|
||||
countProjects: Int!
|
||||
countInvites: Int!
|
||||
countFollows: Int!
|
||||
countShouts: Int!
|
||||
}
|
||||
|
||||
type Notification {
|
||||
id: ID!
|
||||
read: Boolean,
|
||||
user: User @relation(name: "NOTIFIED", direction: "OUT")
|
||||
post: Post @relation(name: "NOTIFIED", direction: "IN")
|
||||
createdAt: String
|
||||
}
|
||||
|
||||
scalar Date
|
||||
scalar Time
|
||||
scalar DateTime
|
||||
|
||||
enum VisibilityEnum {
|
||||
public
|
||||
friends
|
||||
private
|
||||
}
|
||||
|
||||
enum UserGroupEnum {
|
||||
admin
|
||||
moderator
|
||||
user
|
||||
}
|
||||
|
||||
type Location {
|
||||
id: ID!
|
||||
name: String!
|
||||
nameEN: String
|
||||
nameDE: String
|
||||
nameFR: String
|
||||
nameNL: String
|
||||
nameIT: String
|
||||
nameES: String
|
||||
namePT: String
|
||||
namePL: String
|
||||
type: String!
|
||||
lat: Float
|
||||
lng: Float
|
||||
parent: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
|
||||
}
|
||||
|
||||
type User {
|
||||
id: ID!
|
||||
actorId: String
|
||||
name: String
|
||||
email: String!
|
||||
slug: String
|
||||
password: String!
|
||||
avatar: String
|
||||
avatarUpload: Upload
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
disabledBy: User @relation(name: "DISABLED", direction: "IN")
|
||||
role: UserGroupEnum
|
||||
publicKey: String
|
||||
privateKey: String
|
||||
|
||||
location: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l")
|
||||
locationName: String
|
||||
about: String
|
||||
socialMedia: [SocialMedia]! @relation(name: "OWNED", direction: "OUT")
|
||||
|
||||
createdAt: String
|
||||
updatedAt: String
|
||||
|
||||
notifications(read: Boolean): [Notification]! @relation(name: "NOTIFIED", direction: "IN")
|
||||
|
||||
friends: [User]! @relation(name: "FRIENDS", direction: "BOTH")
|
||||
friendsCount: Int! @cypher(statement: "MATCH (this)<-[:FRIENDS]->(r:User) RETURN COUNT(DISTINCT r)")
|
||||
|
||||
following: [User]! @relation(name: "FOLLOWS", direction: "OUT")
|
||||
followingCount: Int! @cypher(statement: "MATCH (this)-[:FOLLOWS]->(r:User) RETURN COUNT(DISTINCT r)")
|
||||
|
||||
followedBy: [User]! @relation(name: "FOLLOWS", direction: "IN")
|
||||
followedByCount: Int! @cypher(statement: "MATCH (this)<-[:FOLLOWS]-(r:User) RETURN COUNT(DISTINCT r)")
|
||||
|
||||
# Is the currently logged in user following that user?
|
||||
followedByCurrentUser: Boolean! @cypher(
|
||||
statement: """
|
||||
MATCH (this)<-[:FOLLOWS]-(u:User {id: $cypherParams.currentUserId})
|
||||
RETURN COUNT(u) >= 1
|
||||
"""
|
||||
)
|
||||
|
||||
#contributions: [WrittenPost]!
|
||||
#contributions2(first: Int = 10, offset: Int = 0): [WrittenPost2]!
|
||||
# @cypher(
|
||||
# statement: "MATCH (this)-[w:WROTE]->(p:Post) RETURN p as Post, w.timestamp as timestamp"
|
||||
# )
|
||||
contributions: [Post]! @relation(name: "WROTE", direction: "OUT")
|
||||
contributionsCount: Int! @cypher(
|
||||
statement: """
|
||||
MATCH (this)-[:WROTE]->(r:Post)
|
||||
WHERE (NOT exists(r.deleted) OR r.deleted = false)
|
||||
AND (NOT exists(r.disabled) OR r.disabled = false)
|
||||
RETURN COUNT(r)
|
||||
"""
|
||||
)
|
||||
|
||||
comments: [Comment]! @relation(name: "WROTE", direction: "OUT")
|
||||
commentsCount: Int! @cypher(statement: "MATCH (this)-[:WROTE]->(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)")
|
||||
|
||||
shouted: [Post]! @relation(name: "SHOUTED", direction: "OUT")
|
||||
shoutedCount: Int! @cypher(statement: "MATCH (this)-[:SHOUTED]->(r:Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
|
||||
|
||||
organizationsCreated: [Organization] @relation(name: "CREATED_ORGA", direction: "OUT")
|
||||
organizationsOwned: [Organization] @relation(name: "OWNING_ORGA", direction: "OUT")
|
||||
|
||||
blacklisted: [User]! @relation(name: "BLACKLISTED", direction: "OUT")
|
||||
|
||||
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
|
||||
|
||||
badges: [Badge]! @relation(name: "REWARDED", direction: "IN")
|
||||
badgesCount: Int! @cypher(statement: "MATCH (this)<-[:REWARDED]-(r:Badge) RETURN COUNT(r)")
|
||||
}
|
||||
|
||||
type Post {
|
||||
id: ID!
|
||||
activityId: String
|
||||
objectId: String
|
||||
author: User @relation(name: "WROTE", direction: "IN")
|
||||
title: String!
|
||||
slug: String
|
||||
content: String!
|
||||
contentExcerpt: String
|
||||
image: String
|
||||
imageUpload: Upload
|
||||
visibility: VisibilityEnum
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
disabledBy: User @relation(name: "DISABLED", direction: "IN")
|
||||
createdAt: String
|
||||
updatedAt: String
|
||||
|
||||
relatedContributions: [Post]! @cypher(
|
||||
statement: """
|
||||
MATCH (this)-[:TAGGED|CATEGORIZED]->(categoryOrTag)<-[:TAGGED|CATEGORIZED]-(post:Post)
|
||||
RETURN DISTINCT post
|
||||
LIMIT 10
|
||||
"""
|
||||
)
|
||||
|
||||
tags: [Tag]! @relation(name: "TAGGED", direction: "OUT")
|
||||
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
|
||||
|
||||
comments: [Comment]! @relation(name: "COMMENTS", direction: "IN")
|
||||
commentsCount: Int! @cypher(statement: "MATCH (this)<-[:COMMENTS]-(r:Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(r)")
|
||||
|
||||
shoutedBy: [User]! @relation(name: "SHOUTED", direction: "IN")
|
||||
shoutedCount: Int! @cypher(statement: "MATCH (this)<-[:SHOUTED]-(r:User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)")
|
||||
|
||||
# Has the currently logged in user shouted that post?
|
||||
shoutedByCurrentUser: Boolean! @cypher(
|
||||
statement: """
|
||||
MATCH (this)<-[:SHOUTED]-(u:User {id: $cypherParams.currentUserId})
|
||||
RETURN COUNT(u) >= 1
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
type Comment {
|
||||
id: ID!
|
||||
activityId: String
|
||||
postId: ID
|
||||
author: User @relation(name: "WROTE", direction: "IN")
|
||||
content: String!
|
||||
contentExcerpt: String
|
||||
post: Post @relation(name: "COMMENTS", direction: "OUT")
|
||||
createdAt: String
|
||||
updatedAt: String
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
disabledBy: User @relation(name: "DISABLED", direction: "IN")
|
||||
}
|
||||
|
||||
type Report {
|
||||
id: ID!
|
||||
submitter: User @relation(name: "REPORTED", direction: "IN")
|
||||
description: String
|
||||
type: String! @cypher(statement: "MATCH (resource)<-[:REPORTED]-(this) RETURN labels(resource)[0]")
|
||||
createdAt: String
|
||||
comment: Comment @relation(name: "REPORTED", direction: "OUT")
|
||||
post: Post @relation(name: "REPORTED", direction: "OUT")
|
||||
user: User @relation(name: "REPORTED", direction: "OUT")
|
||||
}
|
||||
|
||||
type Category {
|
||||
id: ID!
|
||||
name: String!
|
||||
slug: String
|
||||
icon: String!
|
||||
posts: [Post]! @relation(name: "CATEGORIZED", direction: "IN")
|
||||
postCount: Int! @cypher(statement: "MATCH (this)<-[:CATEGORIZED]-(r:Post) RETURN COUNT(r)")
|
||||
}
|
||||
|
||||
type Badge {
|
||||
id: ID!
|
||||
key: String!
|
||||
type: BadgeTypeEnum!
|
||||
status: BadgeStatusEnum!
|
||||
icon: String!
|
||||
|
||||
rewarded: [User]! @relation(name: "REWARDED", direction: "OUT")
|
||||
}
|
||||
|
||||
enum BadgeTypeEnum {
|
||||
role
|
||||
crowdfunding
|
||||
}
|
||||
enum BadgeStatusEnum {
|
||||
permanent
|
||||
temporary
|
||||
}
|
||||
enum ShoutTypeEnum {
|
||||
Post
|
||||
Organization
|
||||
Project
|
||||
}
|
||||
enum FollowTypeEnum {
|
||||
User
|
||||
Organization
|
||||
Project
|
||||
}
|
||||
|
||||
type Reward {
|
||||
id: ID!
|
||||
user: User @relation(name: "REWARDED", direction: "IN")
|
||||
rewarderId: ID
|
||||
createdAt: String
|
||||
badge: Badge @relation(name: "REWARDED", direction: "OUT")
|
||||
}
|
||||
|
||||
type Organization {
|
||||
id: ID!
|
||||
createdBy: User @relation(name: "CREATED_ORGA", direction: "IN")
|
||||
ownedBy: [User] @relation(name: "OWNING_ORGA", direction: "IN")
|
||||
name: String!
|
||||
slug: String
|
||||
description: String!
|
||||
descriptionExcerpt: String
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
|
||||
tags: [Tag]! @relation(name: "TAGGED", direction: "OUT")
|
||||
categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")
|
||||
}
|
||||
|
||||
type Tag {
|
||||
id: ID!
|
||||
name: String!
|
||||
taggedPosts: [Post]! @relation(name: "TAGGED", direction: "IN")
|
||||
taggedOrganizations: [Organization]! @relation(name: "TAGGED", direction: "IN")
|
||||
taggedCount: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p) RETURN COUNT(DISTINCT p)")
|
||||
taggedCountUnique: Int! @cypher(statement: "MATCH (this)<-[:TAGGED]-(p)<-[:WROTE]-(u:User) RETURN COUNT(DISTINCT u)")
|
||||
deleted: Boolean
|
||||
disabled: Boolean
|
||||
}
|
||||
|
||||
type SharedInboxEndpoint {
|
||||
id: ID!
|
||||
uri: String
|
||||
}
|
||||
|
||||
type SocialMedia {
|
||||
id: ID!
|
||||
url: String
|
||||
ownedBy: [User]! @relation(name: "OWNED", direction: "IN")
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
type Badge {
|
||||
id: ID!
|
||||
key: String!
|
||||
type: BadgeType!
|
||||
status: BadgeStatus!
|
||||
icon: String!
|
||||
@ -10,4 +9,23 @@ type Badge {
|
||||
updatedAt: String
|
||||
|
||||
rewarded: [User]! @relation(name: "REWARDED", direction: "OUT")
|
||||
}
|
||||
}
|
||||
|
||||
enum BadgeStatus {
|
||||
permanent
|
||||
temporary
|
||||
}
|
||||
|
||||
enum BadgeType {
|
||||
role
|
||||
crowdfunding
|
||||
}
|
||||
|
||||
type Query {
|
||||
Badge: [Badge]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
reward(badgeKey: ID!, userId: ID!): User
|
||||
unreward(badgeKey: ID!, userId: ID!): User
|
||||
}
|
||||
|
||||
@ -1,28 +1,15 @@
|
||||
import uuid from 'uuid/v4'
|
||||
|
||||
export default function(params) {
|
||||
const {
|
||||
id = uuid(),
|
||||
key = '',
|
||||
type = 'crowdfunding',
|
||||
status = 'permanent',
|
||||
icon = '/img/badges/indiegogo_en_panda.svg',
|
||||
} = params
|
||||
|
||||
export default function create() {
|
||||
return {
|
||||
mutation: `
|
||||
mutation(
|
||||
$id: ID
|
||||
$key: String!
|
||||
$type: BadgeType!
|
||||
$status: BadgeStatus!
|
||||
$icon: String!
|
||||
) {
|
||||
CreateBadge(id: $id, key: $key, type: $type, status: $status, icon: $icon) {
|
||||
id
|
||||
}
|
||||
factory: async ({ args, neodeInstance }) => {
|
||||
const defaults = {
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
}
|
||||
`,
|
||||
variables: { id, key, type, status, icon },
|
||||
args = {
|
||||
...defaults,
|
||||
...args,
|
||||
}
|
||||
return neodeInstance.create('Badge', args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,6 +73,7 @@ export default function Factory(options = {}) {
|
||||
const { factory, mutation, variables } = this.factories[node](args)
|
||||
if (factory) {
|
||||
this.lastResponse = await factory({ args, neodeInstance })
|
||||
return this.lastResponse
|
||||
} else {
|
||||
this.lastResponse = await this.graphQLClient.request(mutation, variables)
|
||||
}
|
||||
|
||||
@ -3,7 +3,7 @@ import uuid from 'uuid/v4'
|
||||
import encryptPassword from '../../helpers/encryptPassword'
|
||||
import slugify from 'slug'
|
||||
|
||||
export default function create(params) {
|
||||
export default function create() {
|
||||
return {
|
||||
factory: async ({ args, neodeInstance }) => {
|
||||
const defaults = {
|
||||
@ -21,8 +21,7 @@ export default function create(params) {
|
||||
...args,
|
||||
}
|
||||
args = await encryptPassword(args)
|
||||
const user = await neodeInstance.create('User', args)
|
||||
return user.toJson()
|
||||
return neodeInstance.create('User', args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,52 +5,42 @@ import Factory from './factories'
|
||||
;(async function() {
|
||||
try {
|
||||
const f = Factory()
|
||||
await Promise.all([
|
||||
const [racoon, rabbit, wolf, bear, turtle, rhino] = await Promise.all([
|
||||
f.create('Badge', {
|
||||
id: 'b1',
|
||||
key: 'indiegogo_en_racoon',
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
id: 'indiegogo_en_racoon',
|
||||
icon: '/img/badges/indiegogo_en_racoon.svg',
|
||||
}),
|
||||
f.create('Badge', {
|
||||
id: 'b2',
|
||||
key: 'indiegogo_en_rabbit',
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
id: 'indiegogo_en_rabbit',
|
||||
icon: '/img/badges/indiegogo_en_rabbit.svg',
|
||||
}),
|
||||
f.create('Badge', {
|
||||
id: 'b3',
|
||||
key: 'indiegogo_en_wolf',
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
id: 'indiegogo_en_wolf',
|
||||
icon: '/img/badges/indiegogo_en_wolf.svg',
|
||||
}),
|
||||
f.create('Badge', {
|
||||
id: 'b4',
|
||||
key: 'indiegogo_en_bear',
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
id: 'indiegogo_en_bear',
|
||||
icon: '/img/badges/indiegogo_en_bear.svg',
|
||||
}),
|
||||
f.create('Badge', {
|
||||
id: 'b5',
|
||||
key: 'indiegogo_en_turtle',
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
id: 'indiegogo_en_turtle',
|
||||
icon: '/img/badges/indiegogo_en_turtle.svg',
|
||||
}),
|
||||
f.create('Badge', {
|
||||
id: 'b6',
|
||||
key: 'indiegogo_en_rhino',
|
||||
type: 'crowdfunding',
|
||||
status: 'permanent',
|
||||
id: 'indiegogo_en_rhino',
|
||||
icon: '/img/badges/indiegogo_en_rhino.svg',
|
||||
}),
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
const [
|
||||
peterLustig,
|
||||
bobDerBaumeister,
|
||||
jennyRostock,
|
||||
tick, // eslint-disable-line no-unused-vars
|
||||
trick, // eslint-disable-line no-unused-vars
|
||||
track, // eslint-disable-line no-unused-vars
|
||||
dagobert,
|
||||
] = await Promise.all([
|
||||
f.create('User', {
|
||||
id: 'u1',
|
||||
name: 'Peter Lustig',
|
||||
@ -123,30 +113,16 @@ import Factory from './factories'
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
f.relate('User', 'Badges', {
|
||||
from: 'b6',
|
||||
to: 'u1',
|
||||
}),
|
||||
f.relate('User', 'Badges', {
|
||||
from: 'b5',
|
||||
to: 'u2',
|
||||
}),
|
||||
f.relate('User', 'Badges', {
|
||||
from: 'b4',
|
||||
to: 'u3',
|
||||
}),
|
||||
f.relate('User', 'Badges', {
|
||||
from: 'b3',
|
||||
to: 'u4',
|
||||
}),
|
||||
f.relate('User', 'Badges', {
|
||||
from: 'b2',
|
||||
to: 'u5',
|
||||
}),
|
||||
f.relate('User', 'Badges', {
|
||||
from: 'b1',
|
||||
to: 'u6',
|
||||
}),
|
||||
peterLustig.relateTo(racoon, 'rewarded'),
|
||||
peterLustig.relateTo(rhino, 'rewarded'),
|
||||
peterLustig.relateTo(wolf, 'rewarded'),
|
||||
bobDerBaumeister.relateTo(racoon, 'rewarded'),
|
||||
bobDerBaumeister.relateTo(turtle, 'rewarded'),
|
||||
jennyRostock.relateTo(bear, 'rewarded'),
|
||||
dagobert.relateTo(rabbit, 'rewarded'),
|
||||
])
|
||||
|
||||
await Promise.all([
|
||||
f.relate('User', 'Friends', {
|
||||
from: 'u1',
|
||||
to: 'u2',
|
||||
|
||||
@ -23,24 +23,27 @@ Cypress.Commands.add('factory', () => {
|
||||
Cypress.Commands.add(
|
||||
'create',
|
||||
{ prevSubject: true },
|
||||
(factory, node, properties) => {
|
||||
return factory.create(node, properties)
|
||||
async (factory, node, properties) => {
|
||||
await factory.create(node, properties)
|
||||
return factory
|
||||
}
|
||||
)
|
||||
|
||||
Cypress.Commands.add(
|
||||
'relate',
|
||||
{ prevSubject: true },
|
||||
(factory, node, relationship, properties) => {
|
||||
return factory.relate(node, relationship, properties)
|
||||
async (factory, node, relationship, properties) => {
|
||||
await factory.relate(node, relationship, properties)
|
||||
return factory
|
||||
}
|
||||
)
|
||||
|
||||
Cypress.Commands.add(
|
||||
'mutate',
|
||||
{ prevSubject: true },
|
||||
(factory, mutation, variables) => {
|
||||
return factory.mutate(mutation, variables)
|
||||
async (factory, mutation, variables) => {
|
||||
await factory.mutate(mutation, variables)
|
||||
return factory
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
[?] type: String, // in nitro this is a defined enum - seems good for now
|
||||
[X] required: true
|
||||
},
|
||||
[X] key: {
|
||||
[X] id: {
|
||||
[X] type: String,
|
||||
[X] required: true
|
||||
},
|
||||
@ -43,7 +43,7 @@
|
||||
CALL apoc.load.json("file:${IMPORT_CHUNK_PATH_CQL_FILE}") YIELD value as badge
|
||||
MERGE(b:Badge {id: badge._id["$oid"]})
|
||||
ON CREATE SET
|
||||
b.key = badge.key,
|
||||
b.id = badge.key,
|
||||
b.type = badge.type,
|
||||
b.icon = replace(badge.image.path, 'https://api-alpha.human-connection.org', ''),
|
||||
b.status = badge.status,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div :class="[badges.length === 2 && 'hc-badges-dual']" class="hc-badges">
|
||||
<div v-for="badge in badges" :key="badge.key" class="hc-badge-container">
|
||||
<div v-for="badge in badges" :key="badge.id" class="hc-badge-container">
|
||||
<img :title="badge.key" :src="badge.icon | proxyApiUrl" class="hc-badge" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -47,7 +47,8 @@ export default {
|
||||
},
|
||||
watch: {
|
||||
Post(post) {
|
||||
this.comments = post[0].comments || []
|
||||
const [first] = post
|
||||
this.comments = (first && first.comments) || []
|
||||
},
|
||||
},
|
||||
apollo: {
|
||||
|
||||
@ -25,7 +25,6 @@ export default app => {
|
||||
}
|
||||
badges {
|
||||
id
|
||||
key
|
||||
icon
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,7 +29,6 @@ export default i18n => {
|
||||
}
|
||||
badges {
|
||||
id
|
||||
key
|
||||
icon
|
||||
}
|
||||
}
|
||||
|
||||
@ -30,7 +30,6 @@ export default i18n => {
|
||||
}
|
||||
badges {
|
||||
id
|
||||
key
|
||||
icon
|
||||
}
|
||||
}
|
||||
@ -61,7 +60,6 @@ export default i18n => {
|
||||
}
|
||||
badges {
|
||||
id
|
||||
key
|
||||
icon
|
||||
}
|
||||
}
|
||||
|
||||
@ -19,7 +19,6 @@ export default i18n => {
|
||||
createdAt
|
||||
badges {
|
||||
id
|
||||
key
|
||||
icon
|
||||
}
|
||||
badgesCount
|
||||
@ -39,7 +38,6 @@ export default i18n => {
|
||||
commentsCount
|
||||
badges {
|
||||
id
|
||||
key
|
||||
icon
|
||||
}
|
||||
location {
|
||||
@ -61,7 +59,6 @@ export default i18n => {
|
||||
commentsCount
|
||||
badges {
|
||||
id
|
||||
key
|
||||
icon
|
||||
}
|
||||
location {
|
||||
|
||||
@ -157,7 +157,6 @@ export default {
|
||||
}
|
||||
badges {
|
||||
id
|
||||
key
|
||||
icon
|
||||
}
|
||||
}
|
||||
|
||||
@ -111,7 +111,6 @@ export default {
|
||||
}
|
||||
badges {
|
||||
id
|
||||
key
|
||||
icon
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user