-
-
Notifications
You must be signed in to change notification settings - Fork 302
Issue 5449 show license audit and special permissions checks #5563
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
Changes from all commits
7f440fc
e2601ef
6dfe37b
ce62b6f
0a77563
c4f5167
93cc4ad
c44d667
0de5ef7
2aafe21
3345082
778b79c
414a918
5d929ea
0f726f6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| import { computed, ref } from 'vue'; | ||
|
|
||
| const MOCK_DEFAULTS = { | ||
| isLoading: ref(true), | ||
| isFinished: ref(false), | ||
| data: computed(() => null), | ||
| fetchData: jest.fn(() => Promise.resolve()), | ||
| }; | ||
|
|
||
| export function useLatestCommunityLibrarySubmissionMock(overrides = {}) { | ||
| return { | ||
| ...MOCK_DEFAULTS, | ||
| ...overrides, | ||
| }; | ||
| } | ||
|
|
||
| export const useLatestCommunityLibrarySubmission = jest.fn(() => | ||
| useLatestCommunityLibrarySubmissionMock(), | ||
| ); |
AlexVelezLl marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| import { computed, ref } from 'vue'; | ||
|
|
||
| const MOCK_DEFAULTS = { | ||
| isLoading: computed(() => false), | ||
| isFinished: computed(() => true), | ||
| invalidLicenses: computed(() => []), | ||
| specialPermissions: computed(() => []), | ||
| includedLicenses: computed(() => []), | ||
| isAuditing: ref(false), | ||
| hasAuditData: computed(() => false), | ||
| auditTaskId: ref(null), | ||
| error: ref(null), | ||
| checkAndTriggerAudit: jest.fn(), | ||
| triggerAudit: jest.fn(), | ||
| fetchPublishedData: jest.fn(), | ||
| }; | ||
|
|
||
| export function useLicenseAuditMock(overrides = {}) { | ||
| return { | ||
| ...MOCK_DEFAULTS, | ||
| ...overrides, | ||
| }; | ||
| } | ||
|
|
||
| export const useLicenseAudit = jest.fn(() => useLicenseAuditMock()); |
AlexVelezLl marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| import { computed, ref } from 'vue'; | ||
|
|
||
| const MOCK_DEFAULTS = { | ||
| isLoading: ref(true), | ||
| isFinished: ref(false), | ||
| data: computed(() => null), | ||
| fetchData: jest.fn(() => Promise.resolve()), | ||
| }; | ||
|
|
||
| export function usePublishedDataMock(overrides = {}) { | ||
| return { | ||
| ...MOCK_DEFAULTS, | ||
| ...overrides, | ||
| }; | ||
| } | ||
|
|
||
| export const usePublishedData = jest.fn(() => usePublishedDataMock()); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,127 @@ | ||
| import { computed, ref, unref, watch } from 'vue'; | ||
| import { Channel } from 'shared/data/resources'; | ||
|
|
||
| export function useLicenseAudit(channelRef, channelVersionRef) { | ||
| const isAuditing = ref(false); | ||
| const auditTaskId = ref(null); | ||
| const auditError = ref(null); | ||
| const publishedData = ref(null); | ||
|
|
||
| watch( | ||
| () => unref(channelRef)?.published_data, | ||
| newPublishedData => { | ||
| if (newPublishedData) { | ||
| publishedData.value = newPublishedData; | ||
| if (isAuditing.value) { | ||
| isAuditing.value = false; | ||
| auditError.value = null; | ||
| } | ||
| } | ||
| }, | ||
| { immediate: true, deep: true }, | ||
| ); | ||
|
|
||
| const currentVersionData = computed(() => { | ||
| const version = unref(channelVersionRef); | ||
| if (!publishedData.value || version == null) { | ||
| return undefined; | ||
| } | ||
| return publishedData.value[version]; | ||
| }); | ||
|
|
||
| const hasAuditData = computed(() => { | ||
| const versionData = currentVersionData.value; | ||
| if (!versionData) { | ||
| return false; | ||
| } | ||
|
|
||
| return ( | ||
| 'community_library_invalid_licenses' in versionData && | ||
| 'community_library_special_permissions' in versionData | ||
| ); | ||
| }); | ||
|
|
||
| const invalidLicenses = computed(() => { | ||
| const versionData = currentVersionData.value; | ||
| return versionData?.community_library_invalid_licenses || []; | ||
| }); | ||
|
|
||
| const specialPermissions = computed(() => { | ||
| const versionData = currentVersionData.value; | ||
| return versionData?.community_library_special_permissions || []; | ||
| }); | ||
|
|
||
| const includedLicenses = computed(() => { | ||
| const versionData = currentVersionData.value; | ||
| return versionData?.included_licenses || []; | ||
| }); | ||
|
|
||
| const isAuditComplete = computed(() => { | ||
| return publishedData.value !== null && hasAuditData.value; | ||
| }); | ||
|
|
||
| async function triggerAudit() { | ||
| if (isAuditing.value) return; | ||
|
|
||
| try { | ||
| isAuditing.value = true; | ||
| auditError.value = null; | ||
|
|
||
| const channelId = unref(channelRef)?.id; | ||
| if (!channelId) { | ||
| throw new Error('Channel ID is required to trigger audit'); | ||
| } | ||
|
|
||
| const response = await Channel.auditLicenses(channelId); | ||
| auditTaskId.value = response.task_id; | ||
| } catch (error) { | ||
| isAuditing.value = false; | ||
| auditError.value = error; | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| async function fetchPublishedData() { | ||
| const channelId = unref(channelRef)?.id; | ||
| if (!channelId) return; | ||
|
|
||
| try { | ||
| const data = await Channel.getPublishedData(channelId); | ||
| publishedData.value = data; | ||
| } catch (error) { | ||
| auditError.value = error; | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| async function checkAndTriggerAudit() { | ||
| if (!publishedData.value) { | ||
| await fetchPublishedData(); | ||
| } | ||
|
|
||
| if (hasAuditData.value || isAuditing.value) { | ||
| return; | ||
| } | ||
|
|
||
| await triggerAudit(); | ||
| } | ||
|
|
||
| return { | ||
| isLoading: computed(() => { | ||
| if (isAuditComplete.value || auditError.value) return false; | ||
| return isAuditing.value; | ||
| }), | ||
| isFinished: computed(() => isAuditComplete.value), | ||
| isAuditing, | ||
| invalidLicenses, | ||
| specialPermissions, | ||
| includedLicenses, | ||
| hasAuditData, | ||
| auditTaskId, | ||
| error: auditError, | ||
|
|
||
| checkAndTriggerAudit, | ||
| triggerAudit, | ||
| fetchPublishedData, | ||
| }; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| import { computed, ref, unref, watch } from 'vue'; | ||
| import { AuditedSpecialPermissionsLicense } from 'shared/data/resources'; | ||
|
|
||
| const ITEMS_PER_PAGE = 3; | ||
|
|
||
| /** | ||
| * Composable that fetches and paginates audited special-permissions licenses | ||
| * for a given set of permission IDs. | ||
| * | ||
| * @param {Array<string|number>|import('vue').Ref<Array<string|number>>} permissionIds | ||
| * A list (or ref to a list) of special-permissions license IDs to fetch. | ||
| * | ||
| * @returns {{ | ||
| * permissions: import('vue').Ref<Array<Object>>, | ||
| * currentPagePermissions: import('vue').ComputedRef<Array<Object>>, | ||
| * isLoading: import('vue').Ref<boolean>, | ||
| * error: import('vue').Ref<Error|null>, | ||
| * currentPage: import('vue').Ref<number>, | ||
| * totalPages: import('vue').ComputedRef<number>, | ||
| * nextPage: () => void, | ||
| * previousPage: () => void, | ||
| * }} | ||
| * Reactive state for the fetched, flattened permissions and pagination | ||
| * helpers used by `SpecialPermissionsList.vue`. | ||
| */ | ||
| export function useSpecialPermissions(permissionIds) { | ||
AlexVelezLl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| const permissions = ref([]); | ||
| const isLoading = ref(false); | ||
| const error = ref(null); | ||
| const currentPage = ref(1); | ||
|
|
||
| const totalPages = computed(() => { | ||
| return Math.ceil(permissions.value.length / ITEMS_PER_PAGE); | ||
| }); | ||
|
|
||
| const currentPagePermissions = computed(() => { | ||
| const start = (currentPage.value - 1) * ITEMS_PER_PAGE; | ||
| const end = start + ITEMS_PER_PAGE; | ||
| return permissions.value.slice(start, end); | ||
| }); | ||
|
|
||
| async function fetchPermissions(ids) { | ||
| if (!ids || ids.length === 0) { | ||
| permissions.value = []; | ||
| return; | ||
| } | ||
|
|
||
| isLoading.value = true; | ||
| error.value = null; | ||
|
|
||
| try { | ||
| const response = await AuditedSpecialPermissionsLicense.fetchCollection({ | ||
| by_ids: ids.join(','), | ||
| distributable: false, | ||
| }); | ||
|
|
||
| permissions.value = response.map(permission => ({ | ||
| id: permission.id, | ||
| description: permission.description, | ||
| distributable: permission.distributable, | ||
| })); | ||
| } catch (err) { | ||
| error.value = err; | ||
| permissions.value = []; | ||
| } finally { | ||
| isLoading.value = false; | ||
| } | ||
| } | ||
|
|
||
| function nextPage() { | ||
| if (currentPage.value < totalPages.value) { | ||
| currentPage.value += 1; | ||
| } | ||
| } | ||
|
|
||
| function previousPage() { | ||
| if (currentPage.value > 1) { | ||
| currentPage.value -= 1; | ||
| } | ||
| } | ||
|
|
||
| const resolvedPermissionIds = computed(() => { | ||
| const ids = unref(permissionIds); | ||
| if (!ids || ids.length === 0) { | ||
| return []; | ||
| } | ||
| return ids; | ||
| }); | ||
|
Comment on lines
+82
to
+88
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's not add support for a non-array
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed, Thanks! |
||
|
|
||
| watch( | ||
| resolvedPermissionIds, | ||
| ids => { | ||
| fetchPermissions(ids); | ||
| }, | ||
| { immediate: true }, | ||
| ); | ||
|
|
||
| return { | ||
| permissions, | ||
| currentPagePermissions, | ||
| isLoading, | ||
| error, | ||
| currentPage, | ||
| totalPages, | ||
| nextPage, | ||
| previousPage, | ||
| }; | ||
| } | ||
Uh oh!
There was an error while loading. Please reload this page.