-
Notifications
You must be signed in to change notification settings - Fork 256
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
Merged
AlexVelezLl
merged 15 commits into
learningequality:unstable
from
taoerman:issue-5449-Show-license-audit-and-special-permissions-checks
Dec 3, 2025
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7f440fc
Show license audit and special permissions checks in the Submit to Co…
taoerman e2601ef
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] 6dfe37b
fix linting
taoerman ce62b6f
fix bug
taoerman 0a77563
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] c4f5167
fix linting
taoerman 93cc4ad
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] c44d667
fix code
taoerman 0de5ef7
fix conflict
taoerman 2aafe21
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] 3345082
fix linting
taoerman 778b79c
[pre-commit.ci lite] apply automatic fixes
pre-commit-ci-lite[bot] 414a918
Merge branch 'unstable' into issue-5449-Show-license-audit-and-specia…
taoerman 5d929ea
fix code
taoerman 0f726f6
fix code
taoerman File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or 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 hidden or 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
19 changes: 19 additions & 0 deletions
19
...tToCommunityLibrarySidePanel/composables/__mocks__/useLatestCommunityLibrarySubmission.js
This file contains hidden or 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,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(), | ||
| ); |
25 changes: 25 additions & 0 deletions
25
...nts/sidePanels/SubmitToCommunityLibrarySidePanel/composables/__mocks__/useLicenseAudit.js
AlexVelezLl marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or 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,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()); |
17 changes: 17 additions & 0 deletions
17
...ts/sidePanels/SubmitToCommunityLibrarySidePanel/composables/__mocks__/usePublishedData.js
AlexVelezLl marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or 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,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()); |
127 changes: 127 additions & 0 deletions
127
...it/components/sidePanels/SubmitToCommunityLibrarySidePanel/composables/useLicenseAudit.js
This file contains hidden or 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,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, | ||
| }; | ||
| } |
108 changes: 108 additions & 0 deletions
108
...ponents/sidePanels/SubmitToCommunityLibrarySidePanel/composables/useSpecialPermissions.js
This file contains hidden or 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,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; | ||
| }); | ||
|
|
||
| watch( | ||
| resolvedPermissionIds, | ||
| ids => { | ||
| fetchPermissions(ids); | ||
| }, | ||
| { immediate: true }, | ||
| ); | ||
|
|
||
| return { | ||
| permissions, | ||
| currentPagePermissions, | ||
| isLoading, | ||
| error, | ||
| currentPage, | ||
| totalPages, | ||
| nextPage, | ||
| previousPage, | ||
| }; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.