Skip to content

Commit

Permalink
fix: allow multiple feeds with same url to co-exist (microsoft#6219)
Browse files Browse the repository at this point in the history
* Fix microsoft#6205: allow multiple feeds with same url to co-exist
Also: remove JS feed for now
Also: add community feed to display all bf-component packages

* remove deprecated code

* Design tweaks for microsoft#6072

* Update feed management modal

Co-authored-by: Andy Brown <[email protected]>
Co-authored-by: Ben Yackley <[email protected]>
Co-authored-by: Chris Whitten <[email protected]>
  • Loading branch information
4 people authored Mar 10, 2021
1 parent dad03ae commit 1e0f4fb
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 73 deletions.
53 changes: 18 additions & 35 deletions extensions/packageManager/src/components/FeedModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export const FeedModal: React.FC<WorkingModalProps> = (props) => {
setEditRow(false);
if (selection.getSelectedCount() > 0) {
setSelectedItem(selection.getSelection()[0] as PackageSourceFeed);
setEditRow(true);
} else {
setSelectedItem(undefined);
}
Expand All @@ -69,7 +70,10 @@ export const FeedModal: React.FC<WorkingModalProps> = (props) => {

useEffect(() => {
setItems(props.feeds);
}, [props.feeds]);
selection.toggleAllSelected();
setSelectedItem(undefined);
setEditRow(false);
}, [props.feeds, props.hidden]);

const columns = [
{
Expand Down Expand Up @@ -116,37 +120,21 @@ export const FeedModal: React.FC<WorkingModalProps> = (props) => {
},
{
key: 'column3',
minWidth: 80,
minWidth: 40,
maxWidth: 40,
isResizable: false,
name: '',
onRender: (item: PackageSourceFeed) => {
if (selectedItem && item.key === selectedItem.key && !editRow)
if (selectedItem && item.key === selectedItem.key)
return (
<Fragment>
<IconButton
disabled={!selectedItem || selectedItem.readonly}
iconProps={{ iconName: 'Edit' }}
onClick={() => setEditRow(true)}
/>
<IconButton
disabled={!selectedItem || selectedItem.readonly}
iconProps={{ iconName: 'Delete' }}
onClick={removeSelected}
/>
</Fragment>
);

if (selectedItem && item.key === selectedItem.key && editRow)
return (
<IconButton
disabled={!selectedItem || !selectedItem.text || !selectedItem.url || selectedItem.readonly}
iconProps={{ iconName: 'Checkmark' }}
onClick={() => {
setEditRow(false);
props.onUpdateFeed(selectedItem.key, selectedItem);
}}
/>
);
},
},
];
Expand All @@ -158,14 +146,19 @@ export const FeedModal: React.FC<WorkingModalProps> = (props) => {
[field]: val,
};
setSelectedItem(newSelection);
setItems(items.map((i) => (i.key === newSelection.key ? newSelection : i)));
};
};

const savePendingEdits = () => {
props.onUpdateFeed(items);
};

const addItem = () => {
const newItem = {
key: uuid(),
text: formatMessage('New Feed'),
url: 'http://',
text: '',
url: '',
} as PackageSourceFeed;

const newItems = items.concat([newItem]);
Expand Down Expand Up @@ -193,25 +186,15 @@ export const FeedModal: React.FC<WorkingModalProps> = (props) => {
formatMessage('Are you sure you want to remove this feed source?')
)
) {
setItems(items.filter((i) => i.key !== selectedItem.key));
setSelectedItem(undefined);
telemetryClient.track('PackageFeedDeleted', {});
props.onUpdateFeed(selectedItem.key, null);
}
};

const closeDialog = () => {
if (editRow) {
confirm(
formatMessage('Discard changes?'),
formatMessage('You have unsaved changes. Are you sure you want to close this window?')
).then((confirmed) => {
if (confirmed) {
props.closeDialog();
}
});
} else {
props.closeDialog();
}
savePendingEdits();
props.closeDialog();
};

return (
Expand Down
7 changes: 6 additions & 1 deletion extensions/packageManager/src/components/ImportDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ const ImportDialog: React.FC<ImportDialogProps> = (props) => {
return (
<Fragment>
<form onSubmit={submit}>
<p>
{formatMessage(
'Install a specific package by pasting the name and version number below. The text must be an exact match in order to find the correct package. You can also add and browse feeds to find packages to add.'
)}
</p>
<TextField
required
defaultValue={props.name || ''}
Expand All @@ -54,8 +59,8 @@ const ImportDialog: React.FC<ImportDialogProps> = (props) => {
onChange={updateVersion}
/>
<DialogFooter>
<PrimaryButton disabled={isDisable()} text={formatMessage('Add')} type="submit" onClick={submit} />
<DefaultButton onClick={props.closeDialog}>{formatMessage('Cancel')}</DefaultButton>
<PrimaryButton disabled={isDisable()} text={formatMessage('Import')} type="submit" onClick={submit} />
</DialogFooter>
</form>
</Fragment>
Expand Down
55 changes: 28 additions & 27 deletions extensions/packageManager/src/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,14 @@ export default async (composer: IExtensionRegistration): Promise<void> => {
// if no sources are in the config file, set the default list to our 1st party feed.
if (!packageSources) {
packageSources = [
{
key: 'npm',
text: 'npm',
url: 'https://registry.npmjs.org/-/v1/search?text=keywords:bf-component&size=100&from=0',
searchUrl: 'https://registry.npmjs.org/-/v1/search?text={{keyword}}+keywords:bf-component&size=100&from=0',
readonly: true,
},
// TODO: Re-enable the NPM feed when we have a JS runtime
// {
// key: 'npm',
// text: 'npm',
// url: 'https://registry.npmjs.org/-/v1/search?text=keywords:bf-component&size=100&from=0',
// searchUrl: 'https://registry.npmjs.org/-/v1/search?text={{keyword}}+keywords:bf-component&size=100&from=0',
// readonly: true,
// },
{
key: 'nuget',
text: 'nuget',
Expand All @@ -82,28 +83,25 @@ export default async (composer: IExtensionRegistration): Promise<void> => {
},
type: PackageSourceType.NuGet,
},
{
key: 'nuget-community',
text: 'community packages',
url: 'https://api.nuget.org/v3/index.json',
defaultQuery: {
prerelease: true,
semVerLevel: '2.0.0',
query: 'tags:bf-component',
},
type: PackageSourceType.NuGet,
},
];
composer.store.write('feeds', packageSources);
}

res.json(packageSources);
},
updateFeeds: async function (req, res) {
const { key, updatedItem } = req.body;

let feeds = composer.store.read('feeds') as IPackageSource[];

if (!updatedItem) {
// update component state
feeds = feeds.filter((f) => f.key !== key);
} else if (feeds.filter((f) => f.key === key).length) {
// item found
feeds = feeds.map((f) => (f.key === key ? updatedItem : f));
} else {
// new item to be appended
feeds = feeds.concat([updatedItem]);
}

const { feeds } = req.body;
composer.store.write('feeds', feeds);
res.json(feeds);
},
Expand Down Expand Up @@ -144,14 +142,13 @@ export default async (composer: IExtensionRegistration): Promise<void> => {
getFeed: async function (req, res) {
// We receive an array of urls for the package sources to retrieve.
// Why an array? In the future it is feasible we would want to mix several feeds together...
const packageSourceUrls: string[] = [req.query.url];
let packageSources: IPackageSource[] = composer.store.read('feeds') as IPackageSource[];

// Get package sources that match a url in the feed query received.
packageSources = packageSources.filter((f) => f.url != null && packageSourceUrls.includes(f.url));
const packageSources: IPackageSource[] = composer.store.read('feeds') as IPackageSource[];

const packageSource = packageSources.find((source) => source.key === req.query.key);
const combined: IPackageDefinition[] = [];
for (const packageSource of packageSources) {

if (packageSource) {
try {
const feed: IFeed = await new FeedFactory(composer).build(packageSource);
const packageQuery: IPackageQuery = {
Expand All @@ -174,6 +171,10 @@ export default async (composer: IExtensionRegistration): Promise<void> => {
message: `Could not load feed from URL ${packageSource.url}. Please check the feed URL and format. Error message: ${err.message}`,
});
}
} else {
return res.status(500).json({
message: `Could not find feed with key ${req.query.key}`,
});
}

const recentlyUsed = (composer.store.read('recentlyUsed') as any[]) || [];
Expand Down
18 changes: 8 additions & 10 deletions extensions/packageManager/src/pages/Library.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -92,9 +92,11 @@ const Library: React.FC = () => {
descriptionLink: formatMessage('Learn more'),
viewDocumentation: formatMessage('View documentation'),
installButton: formatMessage('Install'),
installToolbarButton: formatMessage('Add a package'),

updateButton: formatMessage('Update to'),
installed: formatMessage('installed'),
importDialogTitle: formatMessage('Install a Package'),
importDialogTitle: formatMessage('Add a package'),
installProgress: formatMessage('Installing package...'),
uninstallProgress: formatMessage('Removing package...'),
recentlyUsedCategory: formatMessage('Recently Used'),
Expand Down Expand Up @@ -145,15 +147,12 @@ const Library: React.FC = () => {
};

const getLibraryAPI = () => {
const feedUrl = `${API_ROOT}/feed?url=` + encodeURIComponent(feeds.find((f) => f.key == feed).url);
const feedUrl = `${API_ROOT}/feed?key=${feed}`;
return httpClient.get(feedUrl);
};

const getSearchResults = () => {
const feedUrl = feeds.find((f) => f.key == feed).searchUrl
? `${API_ROOT}/feed?url=` +
encodeURIComponent(feeds.find((f) => f.key == feed).searchUrl.replace(/\{\{keyword\}\}/g, searchTerm))
: `${API_ROOT}/feed?url=` + encodeURIComponent(feeds.find((f) => f.key == feed).url);
const feedUrl = `${API_ROOT}/feed?key=${feed}&term=${encodeURIComponent(searchTerm)}`;
return httpClient.get(feedUrl);
};

Expand Down Expand Up @@ -327,7 +326,7 @@ const Library: React.FC = () => {
const toolbarItems: IToolbarItem[] = [
{
type: 'action',
text: strings.installButton,
text: strings.installToolbarButton,
buttonProps: {
iconProps: {
iconName: 'Add',
Expand Down Expand Up @@ -542,10 +541,9 @@ const Library: React.FC = () => {
navigateTo(`/bot/${currentProjectId}/botProjectsSettings/#runtimeSettings`);
};

const updateFeed = async (key: string, updatedItem: PackageSourceFeed) => {
const updateFeed = async (feeds: PackageSourceFeed[]) => {
const response = await httpClient.post(`${API_ROOT}/feeds`, {
key: key,
updatedItem: updatedItem,
feeds,
});

// update the list of feeds in the component state
Expand Down

0 comments on commit 1e0f4fb

Please sign in to comment.