Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/ps/assign user roles backend #311

Open
wants to merge 16 commits into
base: feature/permissioning-system
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
985 changes: 385 additions & 600 deletions app/package-lock.json

Large diffs are not rendered by default.

106 changes: 86 additions & 20 deletions app/src/components/NotificationDropdown.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@
<li v-for="notification in paginatedNotifications" :key="notification.id">
<a
@click="updateNotification(notification)"
:href="isInvitation(notification) ? `/${notification.resource}` : `#`"
:href="getResource(notification)[0] === `teams` ? `/${notification.resource}` : `#`"
>
<div class="notification__body">
<span :class="{ 'font-bold': !notification.isRead }">
{{ notification.message }}
</span>
</div>
<!--<div class="notification__footer">{{ notification.author }} {{ notification.createdAt }}</div>-->
</a>
</li>
<!-- Pagination Controls -->
Expand Down Expand Up @@ -57,25 +56,19 @@
import { BellIcon } from '@heroicons/vue/24/outline'
import { ChevronLeftIcon } from '@heroicons/vue/24/outline'
import { ChevronRightIcon } from '@heroicons/vue/24/outline'
import { useUserDataStore } from '@/stores/user'
import { log, parseError } from '@/utils'

const currentPage = ref(1)
const itemsPerPage = ref(4)
const totalPages = ref(0)

const updateEndPoint = ref('')
const {
//isFetching: isNotificationsFetching,
//error: notificationError,
data: notifications,
execute: executeFetchNotifications
} = useCustomFetch<NotificationResponse>('notification').json()
const endpointUrl = ref('')

const {
//isFetching: isUpdateNotificationsFetching,
//error: isUpdateNotificationError,
execute: executeUpdateNotifications
//data: _notifications
} = useCustomFetch<NotificationResponse>(updateEndPoint, {
const { data: notifications, execute: executeFetchNotifications } =
useCustomFetch<NotificationResponse>('notification').get().json()

const { execute: executeUpdateNotifications } = useCustomFetch<NotificationResponse>(endpointUrl, {
immediate: false
})
.put()
Expand All @@ -91,22 +84,95 @@
return idx > -1
})

const isInvitation = (notification: Notification) => {
const getResource = (notification: Notification) => {
if (notification.resource) {
const resourceArr = notification.resource.split('/')
if (resourceArr[0] === 'teams') return true
return resourceArr
} else return []

Check warning on line 91 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L91

Added line #L91 was not covered by tests
}

const { execute: executeFetchMemberContract, data: memberContract } = useCustomFetch(endpointUrl, {
immediate: false,
beforeFetch: async ({ options, url, cancel }) => {
options.headers = {

Check warning on line 97 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L96-L97

Added lines #L96 - L97 were not covered by tests
memberaddress: `${useUserDataStore().address}`,
'Content-Type': 'application/json',
...options.headers
}
return { options, url, cancel }

Check warning on line 102 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L102

Added line #L102 was not covered by tests
}
})
.get()
.json()

return false
}
const signature = ref('')

const { execute: executeAddMemberSignature } = useCustomFetch(endpointUrl, {
immediate: false
})
.put()
.json()

const updateNotification = async (notification: Notification) => {
updateEndPoint.value = `notification/${notification.id}`
const resource = getResource(notification)

Check warning on line 117 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L117

Added line #L117 was not covered by tests
if (resource[0] === `role-assignment`) {
endpointUrl.value = `teams/${resource[1]}/member/contract`

Check warning on line 119 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L119

Added line #L119 was not covered by tests
//get contract
await executeFetchMemberContract()
const contract = JSON.parse(JSON.parse(memberContract.value.contract))

Check warning on line 122 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L121-L122

Added lines #L121 - L122 were not covered by tests
//sign contract
signature.value = await signContract(contract)

Check warning on line 124 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L124

Added line #L124 was not covered by tests
//save signature
endpointUrl.value = `teams/${resource[1]}/member/signature/${signature.value}`
await executeAddMemberSignature()

Check warning on line 127 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L126-L127

Added lines #L126 - L127 were not covered by tests
}
endpointUrl.value = `notification/${notification.id}`

Check warning on line 129 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L129

Added line #L129 was not covered by tests

await executeUpdateNotifications()
await executeFetchNotifications()
}

const signContract = async (contract: undefined | Object) => {
if (!contract) return
const params = [

Check warning on line 137 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L137

Added line #L137 was not covered by tests
useUserDataStore().address,
{
types: {
EIP712Domain: [
{ name: 'name', type: 'string' },
{ name: 'version', type: 'string' }
],
Entitlement: [
{ name: 'name', type: 'string' },
{ name: 'resource', type: 'string' },
{ name: 'accessLevel', type: 'string' }
],
Role: [
{ name: 'name', type: 'string' },
{ name: 'entitlement', type: 'Entitlement' }
],
Contract: [
{ name: 'assignedTo', type: 'address' },
{ name: 'assignedBy', type: 'address' },
{ name: 'role', type: 'Role' }
]
},
primaryType: 'Contract',
domain: {
name: 'CNC Contract',
version: '1'
},
message: contract
}
]
try {

Check warning on line 168 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L168

Added line #L168 was not covered by tests
//@ts-ignore
return await window.ethereum.request({ method: 'eth_signTypedData_v4', params: params })

Check warning on line 170 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L170

Added line #L170 was not covered by tests
} catch (error) {
log.error(parseError(error))

Check warning on line 172 in app/src/components/NotificationDropdown.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/NotificationDropdown.vue#L172

Added line #L172 was not covered by tests
}
}

const paginatedNotifications = computed(() => {
if (!notifications.value?.data) return []
const start = (currentPage.value - 1) * itemsPerPage.value
Expand Down
10 changes: 9 additions & 1 deletion app/src/components/__tests__/MemberCard.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('MemberCard', () => {
}
;(useUserDataStore as unknown as mockReturn).mockReturnValue(userDataStore)
})
const props = { member, teamId, ownerAddress }
const props = { isAddingRole: false, member, teamId, ownerAddress }
const wrapper = mount(MemberCard, {
props,
global: {
Expand All @@ -61,6 +61,7 @@ describe('MemberCard', () => {
it('shows delete button if user is the owner and not the member', async () => {
const wrapper = mount(MemberCard, {
props: {
isAddingRole: false,
member: { name: 'John Doe', address: '0x123' },
teamId: 1,
ownerAddress: '0x4b6Bf5cD91446408290725879F5666dcd9785F62'
Expand All @@ -74,6 +75,7 @@ describe('MemberCard', () => {
it('shows show-address button', async () => {
const wrapper = mount(MemberCard, {
props: {
isAddingRole: false,
member: { name: 'John Doe', address: '0x123' },
teamId: 1,
ownerAddress: '0x4b6Bf5cD91446408290725879F5666dcd9785F62'
Expand All @@ -85,6 +87,7 @@ describe('MemberCard', () => {
it('shows copy address button', async () => {
const wrapper = mount(MemberCard, {
props: {
isAddingRole: false,
member: { name: 'John Doe', address: '0x123' },
teamId: 1,
ownerAddress: '0x4b6Bf5cD91446408290725879F5666dcd9785F62'
Expand All @@ -96,6 +99,7 @@ describe('MemberCard', () => {
it('shows "Copied!" when address is copied', async () => {
const wrapper = mount(MemberCard, {
props: {
isAddingRole: false,
member: { name: 'John Doe', address: '0x123' },
teamId: 1,
ownerAddress: '0x4b6Bf5cD91446408290725879F5666dcd9785F62'
Expand All @@ -109,6 +113,7 @@ describe('MemberCard', () => {
it('does not show copy button if copy not supported', async () => {
const wrapper = mount(MemberCard, {
props: {
isAddingRole: false,
member: { name: 'John Doe', address: '0x123' },
teamId: 1,
ownerAddress: '0x456'
Expand All @@ -123,6 +128,7 @@ describe('MemberCard', () => {
it('does not show delete button if user is not the owner', async () => {
const wrapper = mount(MemberCard, {
props: {
isAddingRole: false,
member: { name: 'John Doe', address: '0x123' },
teamId: 1,
ownerAddress: '0x456'
Expand All @@ -136,6 +142,7 @@ describe('MemberCard', () => {
it('opens new window when show address button is clicked', async () => {
const wrapper = mount(MemberCard, {
props: {
isAddingRole: false,
member: { name: 'John Doe', address: '0x123' },
teamId: 1,
ownerAddress: '0x4b6Bf5cD91446408290725879F5666dcd9785F62'
Expand All @@ -151,6 +158,7 @@ describe('MemberCard', () => {
it('copies address when copy address button is clicked', async () => {
const wrapper = mount(MemberCard, {
props: {
isAddingRole: false,
member: { name: 'John Doe', address: '0x123' },
teamId: 1,
ownerAddress: '0x4b6Bf5cD91446408290725879F5666dcd9785F62'
Expand Down
28 changes: 19 additions & 9 deletions app/src/components/__tests__/NotificationDropdown.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,29 @@ describe('NotificationDropdown.vue', () => {

beforeEach(() => {
// Mock the fetch function to return the mock notifications
vi.mock('@/composables/useCustomFetch', () => ({
useCustomFetch: () => ({
json: () => ({
data: ref({ data: mockNotifications }),
execute: vi.fn()
}),
put: () => ({
vi.mock('@/composables/useCustomFetch', async (importOriginal) => {
const actual: Object = await importOriginal()
return {
...actual,
useCustomFetch: () => ({
json: () => ({
data: ref({ data: mockNotifications }),
execute: vi.fn()
}),
put: () => ({
json: () => ({
execute: vi.fn()
})
}),
get: () => ({
json: () => ({
data: ref({ data: mockNotifications }),
execute: vi.fn()
})
})
})
})
}))
}
})

wrapper = mount(NotificationDropdown, {
props: {}
Expand Down
11 changes: 8 additions & 3 deletions app/src/components/forms/roles/AddEntitlementForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,13 @@
import { useVuelidate } from '@vuelidate/core'
import FormErrorMessage from '../../ui/FormErrorMessage.vue'

const isInit = ref(true)

Check warning on line 32 in app/src/components/forms/roles/AddEntitlementForm.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/forms/roles/AddEntitlementForm.vue#L32

Added line #L32 was not covered by tests

const entitlement = defineModel({
default: {
entitlementTypeId: 0,
value: '',
isInit: true //so entitlement value isn't cleared on load
value: ''
//isInit: true //so entitlement value isn't cleared on load
}
})

Expand All @@ -48,7 +50,10 @@
watch(
() => entitlement.value.entitlementTypeId,
(newType, oldType) => {
if (!entitlement.value.isInit && newType !== oldType && oldType !== 0) {
// if (!entitlement.value.isInit && newType !== oldType && oldType !== 0) {
// entitlement.value.value = ''
// }
if (!isInit.value && newType !== oldType && oldType !== 0) {
entitlement.value.value = ''
}
}
Expand Down
25 changes: 13 additions & 12 deletions app/src/components/forms/roles/AddRoleCategoryForm.vue
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
Role - {{ input.name ? input.name : index + 1 }}
</div>
<div class="collapse-content">
<AddRoleForm v-model="roleCategory.roles[index]" />
<AddRoleForm v-if="roleCategory.roles" v-model="roleCategory.roles[index]" />
</div>
</div>
</section>
Expand All @@ -65,7 +65,7 @@
class="w-6 h-6 cursor-pointer"
@click="
() => {
if (roleCategory.roles.length > 1) {
if (roleCategory.roles && roleCategory.roles.length > 1) {
roleCategory.roles.pop()
}
}
Expand All @@ -77,7 +77,7 @@
class="w-6 h-6 cursor-pointer"
@click="
() => {
roleCategory.roles.push({
roleCategory?.roles?.push({
name: '',
description: '',
entitlements: [
Expand Down Expand Up @@ -109,6 +109,7 @@
</div>
<div class="collapse-content">
<AddEntitlementForm
v-if="roleCategory.entitlements"

Check warning on line 112 in app/src/components/forms/roles/AddRoleCategoryForm.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/forms/roles/AddRoleCategoryForm.vue#L112

Added line #L112 was not covered by tests
v-model="roleCategory.entitlements[index]"
:available-types="getAvailableTypes(index)"
/>
Expand All @@ -122,7 +123,7 @@
class="w-6 h-6 cursor-pointer"
@click="
() => {
if (roleCategory.entitlements.length > 1) {
if (roleCategory.entitlements && roleCategory.entitlements.length > 1) {
roleCategory.entitlements.pop()
}
}
Expand All @@ -134,7 +135,7 @@
class="w-6 h-6 cursor-pointer"
@click="
() => {
roleCategory.entitlements.push({
roleCategory?.entitlements?.push({
entitlementTypeId: 0,
value: ''
})
Expand Down Expand Up @@ -164,8 +165,9 @@
import { useCustomFetch } from '@/composables/useCustomFetch'
import { useVuelidate } from '@vuelidate/core'
import { required } from '@vuelidate/validators'
import type { RoleCategory } from '@/types'

const roleCategory = defineModel({
const roleCategory = defineModel<RoleCategory>({

Check warning on line 170 in app/src/components/forms/roles/AddRoleCategoryForm.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/forms/roles/AddRoleCategoryForm.vue#L170

Added line #L170 was not covered by tests
default: {
name: '',
description: '',
Expand Down Expand Up @@ -226,25 +228,24 @@
.get()
.json()

const {
execute: executeUpdateRoleCategory
} = useCustomFetch(roleCategoryEndPoint, {
const { execute: executeUpdateRoleCategory } = useCustomFetch(roleCategoryEndPoint, {

Check warning on line 231 in app/src/components/forms/roles/AddRoleCategoryForm.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/forms/roles/AddRoleCategoryForm.vue#L231

Added line #L231 was not covered by tests
immediate: false
})
.put(roleCategory)
.json()

const getAvailableTypes = (index: number) => {
return computed(() => {
const selectedTypes = roleCategory.value.entitlements.map(
const selectedTypes = roleCategory.value.entitlements?.map(

Check warning on line 239 in app/src/components/forms/roles/AddRoleCategoryForm.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/forms/roles/AddRoleCategoryForm.vue#L239

Added line #L239 was not covered by tests
(entitlement) => entitlement.entitlementTypeId
)

return _entTypes.value?.entTypes.filter(
(type: { id: number; name: string }) =>
type.id === -1 ||
!selectedTypes.includes(type.id) ||
roleCategory.value.entitlements[index].entitlementTypeId === type.id
!selectedTypes?.includes(type.id) ||
(roleCategory.value.entitlements &&
roleCategory.value.entitlements[index].entitlementTypeId === type.id)

Check warning on line 248 in app/src/components/forms/roles/AddRoleCategoryForm.vue

View check run for this annotation

Codecov / codecov/patch

app/src/components/forms/roles/AddRoleCategoryForm.vue#L246-L248

Added lines #L246 - L248 were not covered by tests
)
})
}
Expand Down
Loading
Loading