Skip to content

Commit

Permalink
[ENG-6744] FE: Contributors can "configure" others' addons (#2438)
Browse files Browse the repository at this point in the history
* fixed configure button displayed when user won't be able to configure addon, fixed user and resource reference reuest swarm

* reverted removal of accountOwner

* added isOwned to some failing tests

* fixed tests

* fixed connect button not appearing if not admin

* removed pencil for admin while viewing addon configured by other user

* fixed addon edit tests
  • Loading branch information
opaduchak authored Dec 30, 2024
1 parent 8592e9d commit 70a71e8
Show file tree
Hide file tree
Showing 9 changed files with 115 additions and 38 deletions.
20 changes: 11 additions & 9 deletions app/guid-node/addons/index/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,17 @@
>
<span>{{configuredAddon.displayName}}</span>
<span local-class='float-right'>
<Button
data-test-edit-connected-location={{configuredAddon.displayName}}
data-analytics-name='Edit connected location'
local-class='edit-connected-button'
aria-label={{t 'addons.list.edit-location' displayName=configuredAddon.displayName}}
{{on 'click' (fn manager.configureProvider manager.selectedProvider configuredAddon)}}
>
<FaIcon @icon='pencil-alt' />
</Button>
{{#if configuredAddon.currentUserIsOwner}}
<Button
data-test-edit-connected-location={{configuredAddon.displayName}}
data-analytics-name='Edit connected location'
local-class='edit-connected-button'
aria-label={{t 'addons.list.edit-location' displayName=configuredAddon.displayName}}
{{on 'click' (fn manager.configureProvider manager.selectedProvider configuredAddon)}}
>
<FaIcon @icon='pencil-alt' />
</Button>
{{/if}}
<Button
data-test-remove-connected-location={{configuredAddon.displayName}}
data-analytics-name='Remove connected location'
Expand Down
6 changes: 4 additions & 2 deletions app/models/configured-addon.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Model, { AsyncBelongsTo, attr, belongsTo } from '@ember-data/model';

import UserReferenceModel from 'ember-osf-web/models/user-reference';
import { ConnectedStorageOperationNames, OperationKwargs } from './addon-operation-invocation';
import UserReferenceModel from './user-reference';
import AuthorizedAccountModel, { ConnectedCapabilities } from './authorized-account';

export interface ConfiguredAddonEditableAttrs {
Expand All @@ -21,10 +21,12 @@ export default class ConfiguredAddonModel extends Model {
@attr('array') connectedOperationNames!: ConnectedStorageOperationNames[];
@attr('fixstring') rootFolder!: string;


@belongsTo('user-reference', { inverse: null })
accountOwner!: AsyncBelongsTo<UserReferenceModel> & UserReferenceModel;

@attr('boolean')
currentUserIsOwner!: boolean;

async getFolderItems(this: AuthorizedAccountModel, _kwargs?: OperationKwargs) : Promise<any> {
// To be implemented in child classes
return;
Expand Down
26 changes: 25 additions & 1 deletion app/packages/addons-service/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,17 +95,38 @@ export default class Provider {
return Boolean(this.configuredAddons?.length);
}

get isOwned() {
if (this.node?.userHasAdminPermission) {
return true;
}
if (!this.configuredAddons || this.configuredAddons.length === 0) {
return true;
}
if (!this.userReference) {
return false;
}
return this.configuredAddons?.any(
addon => addon.currentUserIsOwner,
);
}

constructor(
provider: any,
currentUser: CurrentUserService,
node?: NodeModel,
allConfiguredAddons?: EmberArray<AllConfiguredAddonTypes>,
resourceReference?: ResourceReferenceModel,
userReference?: UserReferenceModel,
) {
setOwner(this, getOwner(provider));
this.node = node;
this.currentUser = currentUser;
this.provider = provider;
this.configuredAddons = allConfiguredAddons?.filter(addon => addon.externalServiceId === this.provider.id);
this.serviceNode = resourceReference;
if (userReference) {
this.userReference = userReference;
}

if (provider instanceof ExternalStorageServiceModel) {
this.providerMap = this.providerTypeMapper.externalStorageService;
Expand Down Expand Up @@ -144,6 +165,9 @@ export default class Provider {
@task
@waitFor
async getUserReference() {
if (this.userReference){
return;
}
const { user } = this.currentUser;
const userReferences = await this.store.query('user-reference', {
filter: {user_uri: user?.links.iri?.toString()},
Expand All @@ -155,7 +179,7 @@ export default class Provider {
@task
@waitFor
async getResourceReference() {
if (this.node) {
if (this.node && !this.serviceNode) {
const resourceRefs = await this.store.query('resource-reference', {
filter: {resource_uri: this.node.links.iri?.toString()},
});
Expand Down
4 changes: 4 additions & 0 deletions lib/osf-components/addon/components/addon-card/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,8 @@ export default class AddonsCardComponent extends Component<Args> {
get addonIsConfigured() {
return this.args.addon.isConfigured;
}

get addonIsOwned() {
return this.args.addon.isOwned;
}
}
41 changes: 21 additions & 20 deletions lib/osf-components/addon/components/addon-card/template.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -15,25 +15,26 @@
>
{{@addon.provider.displayName}}
</div>

<div local-class='buttons-wrapper'>
{{#if this.addonIsConfigured}}
<Button
data-test-addon-card-configure
data-analytics-name='Configure'
{{on 'click' (fn @manager.listProviderConfigurations @addon)}}
>
{{t 'osf-components.addon-card.configure'}}
</Button>
{{else}}
<Button
data-test-addon-card-connect
data-analytics-name='Connect'
{{on 'click' (fn @manager.beginAccountSetup @addon)}}
>
{{t 'osf-components.addon-card.connect'}}
</Button>
{{/if}}
</div>
{{#if this.addonIsOwned}}
<div local-class='buttons-wrapper'>
{{#if this.addonIsConfigured}}
<Button
data-test-addon-card-configure
data-analytics-name='Configure'
{{on 'click' (fn @manager.listProviderConfigurations @addon)}}
>
{{t 'osf-components.addon-card.configure'}}
</Button>
{{else}}
<Button
data-test-addon-card-connect
data-analytics-name='Connect'
{{on 'click' (fn @manager.beginAccountSetup @addon)}}
>
{{t 'osf-components.addon-card.connect'}}
</Button>
{{/if}}
</div>
{{/if}}
</div>

Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import ConfiguredStorageAddonModel from 'ember-osf-web/models/configured-storage
import { AccountCreationArgs} from 'ember-osf-web/models/authorized-account';
import AuthorizedStorageAccountModel from 'ember-osf-web/models/authorized-storage-account';
import ConfiguredCitationAddonModel from 'ember-osf-web/models/configured-citation-addon';
import UserReferenceModel from 'ember-osf-web/models/user-reference';

interface FilterSpecificObject {
modelName: string;
Expand Down Expand Up @@ -58,6 +59,7 @@ export default class AddonsServiceManagerComponent extends Component<Args> {

node = this.args.node;
@tracked addonServiceNode?: ResourceReferenceModel;
@tracked userReference?: UserReferenceModel;

possibleFilterTypes = Object.values(FilterTypes);
mapper: Record<FilterTypes, FilterSpecificObject> = {
Expand Down Expand Up @@ -285,7 +287,10 @@ export default class AddonsServiceManagerComponent extends Component<Args> {
@task
@waitFor
async initialize() {
await taskFor(this.getServiceNode).perform();
await Promise.all([
taskFor(this.getUserReference).perform(),
taskFor(this.getServiceNode).perform(),
]);
await taskFor(this.getStorageAddonProviders).perform();
}

Expand Down Expand Up @@ -385,7 +390,18 @@ export default class AddonsServiceManagerComponent extends Component<Args> {
}
return heading;
}

@task
@waitFor
async getUserReference() {
if (this.userReference){
return;
}
const { user } = this.currentUser;
const userReferences = await this.store.query('user-reference', {
filter: {user_uri: user?.links.iri?.toString()},
});
this.userReference = userReferences.firstObject;
}
// Service API Methods

@task
Expand All @@ -394,7 +410,9 @@ export default class AddonsServiceManagerComponent extends Component<Args> {
const serviceProviderModels = (await this.store.findAll(providerType)).toArray();
const serviceProviders = [] as Provider[];
for (const provider of serviceProviderModels) {
serviceProviders.addObject(new Provider(provider, this.currentUser, this.node, configuredAddons));
serviceProviders.addObject(new Provider(
provider, this.currentUser, this.node, configuredAddons, this.addonServiceNode, this.userReference,
));
}
return serviceProviders;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,14 @@ export default class UserAddonManagerComponent extends Component<Args> {
const serviceStorageProviders = await taskFor(this.getExternalProviders)
.perform(activeFilterObject.modelName) as ExternalStorageServiceModel[];
const list = serviceStorageProviders.sort(this.providerSorter)
.map(provider => new Provider(provider, this.currentUser));
.map(provider => new Provider(
provider,
this.currentUser,
undefined,
undefined,
undefined,
this.userReference,
));
activeFilterObject.list = list;
}

Expand All @@ -255,7 +262,14 @@ export default class UserAddonManagerComponent extends Component<Args> {
const cloudComputingProviders = await taskFor(this.getExternalProviders)
.perform(activeFilterObject.modelName) as ExternalComputingServiceModel[];
activeFilterObject.list = cloudComputingProviders.sort(this.providerSorter)
.map(provider => new Provider(provider, this.currentUser));
.map(provider => new Provider(
provider,
this.currentUser,
undefined,
undefined,
undefined,
this.userReference,
));
}

@task
Expand All @@ -265,7 +279,14 @@ export default class UserAddonManagerComponent extends Component<Args> {
const serviceCloudComputingProviders = await taskFor(this.getExternalProviders)
.perform(activeFilterObject.modelName) as ExternalCitationServiceModel[];
activeFilterObject.list = serviceCloudComputingProviders.sort(this.providerSorter)
.map(provider => new Provider(provider, this.currentUser));
.map(provider => new Provider(
provider,
this.currentUser,
undefined,
undefined,
undefined,
this.userReference,
));
}

@task
Expand Down
3 changes: 3 additions & 0 deletions tests/acceptance/guid-node/addons-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ module('Acceptance | guid-node/addons', hooks => {
externalStorageService: box,
accountOwner: userRef,
authorizedResource: nodeRef,
currentUserIsOwner: true,
});
const s3AccountsDisplayNamesAndRootFolders = [{
displayName: 'My Box Account',
Expand All @@ -139,6 +140,7 @@ module('Acceptance | guid-node/addons', hooks => {
externalStorageService: s3,
accountOwner: userRef,
authorizedResource: nodeRef,
currentUserIsOwner: true,
});
server.create('configured-storage-addon', {
displayName: s3AccountsDisplayNamesAndRootFolders[1].displayName,
Expand All @@ -147,6 +149,7 @@ module('Acceptance | guid-node/addons', hooks => {
externalStorageService: s3,
accountOwner: userRef,
authorizedResource: nodeRef,
currentUserIsOwner: true,
});

const url = `/${node.id}/addons`;
Expand Down
2 changes: 2 additions & 0 deletions tests/integration/components/addon-card/component-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ module('Integration | Component | addon-card', hooks => {
},
disableProjectAddon: sinon.stub(),
nodeAddon: { configured: false },
isOwned: true,
};
this.manager ={
node: { id: 'testnode' },
Expand Down Expand Up @@ -78,6 +79,7 @@ module('Integration | Component | addon-card', hooks => {
disableProjectAddon: { perform: sinon.stub() },
nodeAddon: { configured: true },
isConfigured: true,
isOwned: true,
};
this.addon = addonObj;
this.manager = {
Expand Down

0 comments on commit 70a71e8

Please sign in to comment.