mirror of
https://github.com/Ocelot-Social-Community/Ocelot-Social.git
synced 2025-12-13 07:46:06 +00:00
Merge pull request #4136 from Ocelot-Social-Community/4124-switch-user-role
feat: 🍰 Switch User Role As Admin
This commit is contained in:
commit
35626e6b5f
24
CHANGELOG.md
24
CHANGELOG.md
@ -4,9 +4,29 @@ All notable changes to this project will be documented in this file. Dates are d
|
||||
|
||||
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
|
||||
|
||||
#### [v0.6.5](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.4...v0.6.5)
|
||||
#### [v0.6.6](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.5...v0.6.6)
|
||||
|
||||
- updated CHANGELOG.md [`9d9075f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9d9075f2117b2eb4b607e7d59ab18c7e655c6ea7)
|
||||
- feat: Image Cropping Is Optional [`#4199`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4199)
|
||||
- publish workflow [`#4195`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/4195)
|
||||
- change user roles is working, test fails [`c528269`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/c528269cb2972e6ea937d31ba22d0e11168141f2)
|
||||
- file upload: refactored [`650e83f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/650e83f4c250389477933a2e7d21d8245b0ce882)
|
||||
- change user role: tests are working [`14dfe2a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/14dfe2ae2cd4a24c06c9229893b33586dfceae4f)
|
||||
|
||||
#### [0.6.5](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/v0.6.4...0.6.5)
|
||||
|
||||
> 8 February 2021
|
||||
|
||||
- - adjusted changelog to ocelot-social repo [`9603882`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9603882edebf8967e05abfa94e4e1ebf452d4e24)
|
||||
- - first steps towards docker image deployment & github autotagging [`5503216`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5503216ad4a0230ac533042e4a69806590fc2a5a)
|
||||
- - deploy structure image [`a60400b`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/a60400b4fe6f59bbb80e1073db4def3ba205e1a7)
|
||||
|
||||
#### [v0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.4...v0.6.4)
|
||||
|
||||
> 9 February 2021
|
||||
|
||||
- chore(release): 0.6.4 [`8b7570d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8b7570dc35d0ea431f673a711ac051f1e1320acb)
|
||||
- change user roles is working, test fails [`8c3310a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8c3310abaf87c0e5597fec4f93fb37d27122c9e7)
|
||||
- change user role: tests are working [`f10da4b`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/f10da4b09388fe1e2b85abd53f6ffc67c785d4c1)
|
||||
|
||||
#### [0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.3...0.6.4)
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social-backend",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"description": "GraphQL Backend for ocelot.social",
|
||||
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
|
||||
"author": "ocelot.social Community",
|
||||
|
||||
@ -166,6 +166,7 @@ export default shield(
|
||||
unpinPost: isAdmin,
|
||||
UpdateDonations: isAdmin,
|
||||
GenerateInviteCode: isAuthenticated,
|
||||
switchUserRole: isAdmin,
|
||||
},
|
||||
User: {
|
||||
email: or(isMyOwn, isAdmin),
|
||||
|
||||
@ -244,6 +244,31 @@ export default {
|
||||
session.close()
|
||||
}
|
||||
},
|
||||
switchUserRole: async (object, args, context, resolveInfo) => {
|
||||
const { role, id } = args
|
||||
|
||||
if (context.user.id === id) throw new Error('you-cannot-change-your-own-role')
|
||||
const session = context.driver.session()
|
||||
const writeTxResultPromise = session.writeTransaction(async (transaction) => {
|
||||
const switchUserRoleResponse = await transaction.run(
|
||||
`
|
||||
MATCH (user:User {id: $id})
|
||||
SET user.role = $role
|
||||
SET user.updatedAt = toString(datetime())
|
||||
RETURN user {.*}
|
||||
`,
|
||||
{ id, role },
|
||||
)
|
||||
const [user] = switchUserRoleResponse.records.map((record) => record.get('user'))
|
||||
return user
|
||||
})
|
||||
try {
|
||||
const user = await writeTxResultPromise
|
||||
return user
|
||||
} finally {
|
||||
session.close()
|
||||
}
|
||||
},
|
||||
},
|
||||
User: {
|
||||
email: async (parent, params, context, resolveInfo) => {
|
||||
|
||||
@ -45,6 +45,18 @@ const deleteUserMutation = gql`
|
||||
}
|
||||
`
|
||||
|
||||
const switchUserRoleMutation = gql`
|
||||
mutation($role: UserGroup!, $id: ID!) {
|
||||
switchUserRole(role: $role, id: $id) {
|
||||
name
|
||||
role
|
||||
id
|
||||
updatedAt
|
||||
email
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
beforeAll(() => {
|
||||
const { server } = createServer({
|
||||
context: () => {
|
||||
@ -458,3 +470,71 @@ describe('Delete a User as admin', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('switch user role', () => {
|
||||
beforeEach(async () => {
|
||||
user = await Factory.build('user', {
|
||||
id: 'user',
|
||||
role: 'user',
|
||||
})
|
||||
admin = await Factory.build('user', {
|
||||
role: 'admin',
|
||||
id: 'admin',
|
||||
})
|
||||
})
|
||||
|
||||
describe('as simple user', () => {
|
||||
it('cannot change the role', async () => {
|
||||
authenticatedUser = await user.toJson()
|
||||
variables = {
|
||||
id: 'user',
|
||||
role: 'admin',
|
||||
}
|
||||
await expect(mutate({ mutation: switchUserRoleMutation, variables })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
expect.objectContaining({
|
||||
message: 'Not Authorised!',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe('as admin', () => {
|
||||
it('changes the role of other user', async () => {
|
||||
authenticatedUser = await admin.toJson()
|
||||
variables = {
|
||||
id: 'user',
|
||||
role: 'moderator',
|
||||
}
|
||||
await expect(mutate({ mutation: switchUserRoleMutation, variables })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
data: {
|
||||
switchUserRole: expect.objectContaining({
|
||||
role: 'moderator',
|
||||
}),
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('cannot change own role', async () => {
|
||||
authenticatedUser = await admin.toJson()
|
||||
variables = {
|
||||
id: 'admin',
|
||||
role: 'moderator',
|
||||
}
|
||||
await expect(mutate({ mutation: switchUserRoleMutation, variables })).resolves.toEqual(
|
||||
expect.objectContaining({
|
||||
errors: [
|
||||
expect.objectContaining({
|
||||
message: 'you-cannot-change-your-own-role',
|
||||
}),
|
||||
],
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -215,4 +215,6 @@ type Mutation {
|
||||
unmuteUser(id: ID!): User
|
||||
blockUser(id: ID!): User
|
||||
unblockUser(id: ID!): User
|
||||
|
||||
switchUserRole(role: UserGroup!, id: ID!): User
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"description": "Fullstack and API tests with cypress and cucumber for ocelot.social",
|
||||
"author": "ocelot.social Community",
|
||||
"license": "MIT",
|
||||
|
||||
28
webapp/graphql/admin/Roles.js
Normal file
28
webapp/graphql/admin/Roles.js
Normal file
@ -0,0 +1,28 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export const FetchAllRoles = () => {
|
||||
return gql`
|
||||
query {
|
||||
__type(name: "UserGroup") {
|
||||
name
|
||||
enumValues {
|
||||
name
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
|
||||
export const updateUserRole = (role, id) => {
|
||||
return gql`
|
||||
mutation($role: UserGroup!, $id: ID!) {
|
||||
switchUserRole(role: $role, id: $id) {
|
||||
name
|
||||
role
|
||||
id
|
||||
updatedAt
|
||||
email
|
||||
}
|
||||
}
|
||||
`
|
||||
}
|
||||
@ -63,6 +63,7 @@
|
||||
"placeholder": "E-Mail, Name oder Beschreibung"
|
||||
},
|
||||
"name": "Benutzer",
|
||||
"roleChanged": "Rolle erfolgreich geändert!",
|
||||
"table": {
|
||||
"columns": {
|
||||
"createdAt": "Erstellt am",
|
||||
|
||||
@ -63,6 +63,7 @@
|
||||
"placeholder": "e-mail, name or description"
|
||||
},
|
||||
"name": "Users",
|
||||
"roleChanged": "Role changed successfully!",
|
||||
"table": {
|
||||
"columns": {
|
||||
"createdAt": "Created at",
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ocelot-social-webapp",
|
||||
"version": "0.6.5",
|
||||
"version": "0.6.6",
|
||||
"description": "ocelot.social Frontend",
|
||||
"repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social",
|
||||
"author": "ocelot.social Community",
|
||||
|
||||
@ -1,27 +1,54 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { config, mount } from '@vue/test-utils'
|
||||
import Vuex from 'vuex'
|
||||
import Users from './users.vue'
|
||||
|
||||
const localVue = global.localVue
|
||||
config.stubs['nuxt-link'] = '<span><slot /></span>'
|
||||
|
||||
describe('Users', () => {
|
||||
let wrapper
|
||||
let Wrapper
|
||||
let mocks
|
||||
let getters
|
||||
|
||||
beforeEach(() => {
|
||||
mocks = {
|
||||
$t: jest.fn(),
|
||||
$apollo: {
|
||||
loading: false,
|
||||
},
|
||||
}
|
||||
})
|
||||
const mocks = {
|
||||
$t: jest.fn(),
|
||||
$apollo: {
|
||||
loading: false,
|
||||
mutate: jest
|
||||
.fn()
|
||||
.mockRejectedValue({ message: 'Ouch!' })
|
||||
.mockResolvedValue({
|
||||
data: {
|
||||
switchUserRole: {
|
||||
id: 'user',
|
||||
email: 'user@example.org',
|
||||
name: 'User',
|
||||
role: 'moderator',
|
||||
slug: 'user',
|
||||
},
|
||||
},
|
||||
}),
|
||||
},
|
||||
$toast: {
|
||||
error: jest.fn(),
|
||||
success: jest.fn(),
|
||||
},
|
||||
}
|
||||
|
||||
describe('mount', () => {
|
||||
getters = {
|
||||
'auth/isAdmin': () => true,
|
||||
'auth/user': () => {
|
||||
return { id: 'admin' }
|
||||
},
|
||||
}
|
||||
|
||||
Wrapper = () => {
|
||||
const store = new Vuex.Store({ getters })
|
||||
return mount(Users, {
|
||||
mocks,
|
||||
localVue,
|
||||
store,
|
||||
})
|
||||
}
|
||||
|
||||
@ -69,5 +96,54 @@ describe('Users', () => {
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('change roles', () => {
|
||||
beforeAll(() => {
|
||||
wrapper = Wrapper()
|
||||
wrapper.setData({
|
||||
User: [
|
||||
{
|
||||
id: 'admin',
|
||||
email: 'admin@example.org',
|
||||
name: 'Admin',
|
||||
role: 'admin',
|
||||
slug: 'admin',
|
||||
},
|
||||
{
|
||||
id: 'user',
|
||||
email: 'user@example.org',
|
||||
name: 'User',
|
||||
role: 'user',
|
||||
slug: 'user',
|
||||
},
|
||||
],
|
||||
userRoles: ['user', 'moderator', 'admin'],
|
||||
})
|
||||
})
|
||||
|
||||
it('cannot change own role', () => {
|
||||
const adminRow = wrapper.findAll('tr').at(1)
|
||||
expect(adminRow.find('select').exists()).toBe(false)
|
||||
})
|
||||
|
||||
it('changes the role of another user', () => {
|
||||
const userRow = wrapper.findAll('tr').at(2)
|
||||
userRow.findAll('option').at(1).setSelected()
|
||||
expect(mocks.$apollo.mutate).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
variables: {
|
||||
id: 'user',
|
||||
role: 'moderator',
|
||||
},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('toasts a success message after role has changed', () => {
|
||||
const userRow = wrapper.findAll('tr').at(2)
|
||||
userRow.findAll('option').at(1).setSelected()
|
||||
expect(mocks.$toast.success).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -48,6 +48,21 @@
|
||||
<template #createdAt="scope">
|
||||
{{ scope.row.createdAt | dateTime }}
|
||||
</template>
|
||||
|
||||
<template slot="role" slot-scope="scope">
|
||||
<template v-if="userRoles">
|
||||
<select
|
||||
v-if="scope.row.id !== currentUser.id"
|
||||
:value="`${scope.row.role}`"
|
||||
v-on:change="changeUserRole(scope.row.id, $event)"
|
||||
>
|
||||
<option v-for="value in userRoles" :key="value">
|
||||
{{ value }}
|
||||
</option>
|
||||
</select>
|
||||
<ds-text v-else>{{ scope.row.role }}</ds-text>
|
||||
</template>
|
||||
</template>
|
||||
</ds-table>
|
||||
<pagination-buttons :hasNext="hasNext" :hasPrevious="hasPrevious" @next="next" @back="back" />
|
||||
</base-card>
|
||||
@ -58,10 +73,12 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapGetters } from 'vuex'
|
||||
import gql from 'graphql-tag'
|
||||
import { isEmail } from 'validator'
|
||||
import normalizeEmail from '~/components/utils/NormalizeEmail'
|
||||
import PaginationButtons from '~/components/_new/generic/PaginationButtons/PaginationButtons'
|
||||
import { FetchAllRoles, updateUserRole } from '~/graphql/admin/Roles'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -77,6 +94,7 @@ export default {
|
||||
hasNext: false,
|
||||
email: null,
|
||||
filter: null,
|
||||
userRoles: [],
|
||||
form: {
|
||||
formData: {
|
||||
query: '',
|
||||
@ -88,6 +106,9 @@ export default {
|
||||
hasPrevious() {
|
||||
return this.offset > 0
|
||||
},
|
||||
...mapGetters({
|
||||
currentUser: 'auth/user',
|
||||
}),
|
||||
fields() {
|
||||
return {
|
||||
index: this.$t('admin.users.table.columns.number'),
|
||||
@ -153,6 +174,14 @@ export default {
|
||||
return User.map((u, i) => Object.assign({}, u, { index: this.offset + i }))
|
||||
},
|
||||
},
|
||||
userRoles: {
|
||||
query() {
|
||||
return FetchAllRoles()
|
||||
},
|
||||
update({ __type }) {
|
||||
return __type.enumValues.map((item) => item.name)
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
back() {
|
||||
@ -174,6 +203,20 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
changeUserRole(id, event) {
|
||||
const newRole = event.target.value
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: updateUserRole(),
|
||||
variables: { role: newRole, id },
|
||||
})
|
||||
.then(({ data }) => {
|
||||
this.$toast.success(this.$t('admin.users.roleChanged'))
|
||||
})
|
||||
.catch((error) => {
|
||||
this.$toast.error(error.message)
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user