-
Notifications
You must be signed in to change notification settings - Fork 22
Add deb repository and remote pages #347
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
base: main
Are you sure you want to change the base?
Changes from all commits
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,9 @@ | ||
| import { msg } from '@lingui/core/macro'; | ||
| import { Paths, formatPath } from 'src/paths'; | ||
| import { Action } from './action'; | ||
|
|
||
| export const debRemoteCreateAction = Action({ | ||
| title: msg`Add remote`, | ||
| onClick: (item, { navigate }) => | ||
| navigate(formatPath(Paths.deb.remote.edit, { name: '_' })), | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| import { msg, t } from '@lingui/core/macro'; | ||
| import { DebRemoteAPI } from 'src/api'; | ||
| import { DeleteRemoteModal } from 'src/components'; | ||
| import { | ||
| handleHttpError, | ||
| parsePulpIDFromURL, | ||
| taskAlert, | ||
| waitForTaskUrl, | ||
| } from 'src/utilities'; | ||
| import { Action } from './action'; | ||
|
|
||
| export const debRemoteDeleteAction = Action({ | ||
| title: msg`Delete`, | ||
| modal: ({ addAlert, listQuery, setState, state }) => | ||
| state.deleteModalOpen ? ( | ||
| <DeleteRemoteModal | ||
| closeAction={() => setState({ deleteModalOpen: null })} | ||
| deleteAction={() => | ||
| deleteRemote(state.deleteModalOpen, { addAlert, setState, listQuery }) | ||
| } | ||
| name={state.deleteModalOpen.name} | ||
| /> | ||
| ) : null, | ||
| onClick: ( | ||
| { name, id, pulp_href }: { name: string; id?: string; pulp_href?: string }, | ||
| { setState }, | ||
| ) => | ||
| setState({ | ||
| deleteModalOpen: { pulpId: id || parsePulpIDFromURL(pulp_href), name }, | ||
| }), | ||
| }); | ||
|
|
||
| function deleteRemote({ name, pulpId }, { addAlert, setState, listQuery }) { | ||
| return DebRemoteAPI.delete(pulpId) | ||
| .then(({ data }) => { | ||
| addAlert(taskAlert(data.task, t`Removal started for remote ${name}`)); | ||
| setState({ deleteModalOpen: null }); | ||
| return waitForTaskUrl(data.task); | ||
| }) | ||
| .then(() => listQuery()) | ||
| .catch( | ||
| handleHttpError(t`Failed to remove remote ${name}`, () => null, addAlert), | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { msg } from '@lingui/core/macro'; | ||
| import { Paths, formatPath } from 'src/paths'; | ||
| import { Action } from './action'; | ||
|
|
||
| export const debRemoteEditAction = Action({ | ||
| title: msg`Edit`, | ||
| onClick: ({ name }, { navigate }) => | ||
| navigate(formatPath(Paths.deb.remote.edit, { name })), | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { msg } from '@lingui/core/macro'; | ||
| import { Paths, formatPath } from 'src/paths'; | ||
| import { Action } from './action'; | ||
|
|
||
| export const debRepositoryCreateAction = Action({ | ||
| title: msg`Add repository`, | ||
| onClick: (item, { navigate }) => | ||
| navigate(formatPath(Paths.deb.repository.edit, { name: '_' })), | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| import { msg, t } from '@lingui/core/macro'; | ||
| import { DebDistributionAPI, DebRepositoryAPI } from 'src/api'; | ||
| import { DeleteRepositoryModal } from 'src/components'; | ||
| import { | ||
| handleHttpError, | ||
| parsePulpIDFromURL, | ||
| taskAlert, | ||
| waitForTaskUrl, | ||
| } from 'src/utilities'; | ||
| import { Action } from './action'; | ||
|
|
||
| export const debRepositoryDeleteAction = Action({ | ||
| title: msg`Delete`, | ||
| modal: ({ addAlert, listQuery, setState, state }) => | ||
| state.deleteModalOpen ? ( | ||
| <DeleteRepositoryModal | ||
| closeAction={() => setState({ deleteModalOpen: null })} | ||
| deleteAction={() => | ||
| deleteRepository(state.deleteModalOpen, { | ||
| addAlert, | ||
| listQuery, | ||
| setState, | ||
| }) | ||
| } | ||
| name={state.deleteModalOpen.name} | ||
| /> | ||
| ) : null, | ||
| onClick: ( | ||
| { name, id, pulp_href }: { name: string; id?: string; pulp_href?: string }, | ||
| { setState }, | ||
| ) => | ||
| setState({ | ||
| deleteModalOpen: { | ||
| pulpId: id || parsePulpIDFromURL(pulp_href), | ||
| name, | ||
| pulp_href, | ||
| }, | ||
| }), | ||
| }); | ||
|
|
||
| async function deleteRepository( | ||
| { name, pulp_href, pulpId }, | ||
| { addAlert, setState, listQuery }, | ||
| ) { | ||
| // TODO: handle more pages | ||
| const distributionsToDelete = await DebDistributionAPI.list({ | ||
| repository: pulp_href, | ||
| page: 1, | ||
| page_size: 100, | ||
| }) | ||
| .then(({ data: { results } }) => results || []) | ||
| .catch((e) => { | ||
| handleHttpError( | ||
| t`Failed to list distributions, removing only the repository.`, | ||
| () => null, | ||
| addAlert, | ||
| )(e); | ||
| return []; | ||
| }); | ||
|
|
||
| const deleteRepo = DebRepositoryAPI.delete(pulpId) | ||
| .then(({ data }) => { | ||
| addAlert(taskAlert(data.task, t`Removal started for repository ${name}`)); | ||
| return waitForTaskUrl(data.task); | ||
| }) | ||
| .catch( | ||
| handleHttpError( | ||
| t`Failed to remove repository ${name}`, | ||
| () => setState({ deleteModalOpen: null }), | ||
| addAlert, | ||
| ), | ||
| ); | ||
|
|
||
| const deleteDistribution = ({ name, pulp_href }) => { | ||
| const distribution_id = parsePulpIDFromURL(pulp_href); | ||
| return DebDistributionAPI.delete(distribution_id) | ||
| .then(({ data }) => { | ||
| addAlert( | ||
| taskAlert(data.task, t`Removal started for distribution ${name}`), | ||
| ); | ||
| return waitForTaskUrl(data.task); | ||
| }) | ||
| .catch( | ||
| handleHttpError( | ||
| t`Failed to remove distribution ${name}`, | ||
| () => null, | ||
| addAlert, | ||
| ), | ||
| ); | ||
| }; | ||
|
|
||
| return Promise.all([ | ||
| deleteRepo, | ||
| ...distributionsToDelete.map(deleteDistribution), | ||
| ]).then(() => { | ||
| setState({ deleteModalOpen: null }); | ||
| listQuery(); | ||
| }); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import { msg } from '@lingui/core/macro'; | ||
| import { Paths, formatPath } from 'src/paths'; | ||
| import { Action } from './action'; | ||
|
|
||
| export const debRepositoryEditAction = Action({ | ||
| title: msg`Edit`, | ||
| onClick: ({ name }, { navigate }) => | ||
| navigate(formatPath(Paths.deb.repository.edit, { name })), | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| import { msg, t } from '@lingui/core/macro'; | ||
|
Contributor
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. MUST (mirror default): Good to see this flagged on the PR already - I'd agree it's worth resolving to match what SHOULD (SyncModal duplication): Also flagged by you already - agreed this is worth doing, but I'd suggest pulling |
||
| import { Button, FormGroup, Modal, Switch } from '@patternfly/react-core'; | ||
| import { useEffect, useState } from 'react'; | ||
| import { DebRepositoryAPI } from 'src/api'; | ||
| import { HelpButton, Spinner } from 'src/components'; | ||
| import { handleHttpError, parsePulpIDFromURL, taskAlert } from 'src/utilities'; | ||
| import { Action } from './action'; | ||
|
|
||
| // as in ansible-repository-sync and file-repository-sync | ||
| const SyncModal = ({ | ||
| closeAction, | ||
| syncAction, | ||
| name, | ||
| }: { | ||
| closeAction: () => null; | ||
| syncAction: (syncParams) => Promise<void>; | ||
| name: string; | ||
| }) => { | ||
| const [pending, setPending] = useState(false); | ||
| const [syncParams, setSyncParams] = useState({ | ||
| mirror: true, | ||
| optimize: true, | ||
| }); | ||
|
|
||
| useEffect(() => { | ||
| setPending(false); | ||
| setSyncParams({ mirror: true, optimize: true }); | ||
| }, [name]); | ||
|
|
||
| if (!name) { | ||
| return null; | ||
| } | ||
|
|
||
| return ( | ||
| <Modal | ||
| actions={[ | ||
| <div data-cy='sync-button' key='sync'> | ||
| <Button | ||
| key='sync' | ||
| onClick={() => { | ||
| setPending(true); | ||
| syncAction(syncParams) | ||
| .then(closeAction) | ||
| .finally(() => setPending(false)); | ||
| }} | ||
| variant='primary' | ||
| isDisabled={pending} | ||
| > | ||
| {t`Sync`} | ||
| {pending && <Spinner size='sm' />} | ||
| </Button> | ||
| </div>, | ||
| <Button key='close' onClick={closeAction} variant='link'> | ||
| {t`Close`} | ||
| </Button>, | ||
| ]} | ||
| isOpen | ||
| onClose={closeAction} | ||
| title={t`Sync repository "${name}"`} | ||
| variant='medium' | ||
| > | ||
| <FormGroup | ||
| label={t`Mirror`} | ||
| labelIcon={ | ||
| <HelpButton | ||
| content={t`If selected, all content that is not present in the remote repository will be removed from the local repository; otherwise, sync will add missing content.`} | ||
| /> | ||
| } | ||
| > | ||
| <Switch | ||
| isChecked={syncParams.mirror} | ||
| onChange={(_event, mirror) => | ||
| setSyncParams({ ...syncParams, mirror }) | ||
| } | ||
| label={t`Content not present in remote repository will be removed from the local repository`} | ||
| labelOff={t`Sync will only add missing content`} | ||
| /> | ||
| </FormGroup> | ||
| <br /> | ||
| <FormGroup | ||
| label={t`Optimize`} | ||
| labelIcon={ | ||
| <HelpButton | ||
| content={t`Only perform the sync if changes are reported by the remote server. To force a sync to happen, deselect this option.`} | ||
| /> | ||
| } | ||
| > | ||
| <Switch | ||
| isChecked={syncParams.optimize} | ||
| onChange={(_event, optimize) => | ||
| setSyncParams({ ...syncParams, optimize }) | ||
| } | ||
| label={t`Only perform the sync if changes are reported by the remote server.`} | ||
| labelOff={t`Force a sync to happen.`} | ||
| /> | ||
| </FormGroup> | ||
| <br /> | ||
| </Modal> | ||
| ); | ||
| }; | ||
|
|
||
| export const debRepositorySyncAction = Action({ | ||
| title: msg`Sync`, | ||
| modal: ({ addAlert, query, setState, state }) => | ||
| state.syncModalOpen ? ( | ||
| <SyncModal | ||
| closeAction={() => setState({ syncModalOpen: null })} | ||
| syncAction={(syncParams) => | ||
| syncRepository(state.syncModalOpen, { addAlert, query }, syncParams) | ||
| } | ||
| name={state.syncModalOpen.name} | ||
| /> | ||
| ) : null, | ||
| onClick: ({ name, pulp_href }, { setState }) => | ||
| setState({ | ||
| syncModalOpen: { name, pulp_href }, | ||
| }), | ||
| visible: (_item, { hasPermission }) => | ||
| hasPermission('deb.change_aptrepository'), | ||
| disabled: ({ remote, last_sync_task }) => { | ||
| if (!remote) { | ||
| return t`There are no remotes associated with this repository.`; | ||
| } | ||
|
|
||
| if ( | ||
| last_sync_task && | ||
| ['running', 'waiting'].includes(last_sync_task.state) | ||
| ) { | ||
| return t`Sync task is already queued.`; | ||
| } | ||
| }, | ||
| }); | ||
|
|
||
| function syncRepository({ name, pulp_href }, { addAlert, query }, syncParams) { | ||
| const pulpId = parsePulpIDFromURL(pulp_href); | ||
| return DebRepositoryAPI.sync(pulpId, syncParams || { mirror: true }) | ||
| .then(({ data }) => { | ||
| addAlert(taskAlert(data.task, t`Sync started for repository "${name}".`)); | ||
|
|
||
| query(); | ||
| }) | ||
| .catch( | ||
| handleHttpError( | ||
| t`Failed to sync repository "${name}"`, | ||
| () => null, | ||
| addAlert, | ||
| ), | ||
| ); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| import { PulpAPI } from './pulp'; | ||
|
|
||
| const base = new PulpAPI(); | ||
|
|
||
| export const DebDistributionAPI = { | ||
| create: (data) => base.http.post(`distributions/deb/apt/`, data), | ||
|
|
||
| delete: (id) => base.http.delete(`distributions/deb/apt/${id}/`), | ||
|
|
||
| list: (params?) => base.list(`distributions/deb/apt/`, params), | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MUST: Bare
// TODOisn't enough here - if the repository being deleted has more than 100 distributions, the excess are silently orphaned. I'm aware this same pattern exists in bothansibleandfilerepository actions, but I don't want to carry it over intodebas well.Please either link this
TODOto a tracked Github issue, or implement pagination using the offset value returned by the API.