Skip to content

Commit

Permalink
Merge pull request #337 from manifoldco/gui/8832-fix-provisioning-status
Browse files Browse the repository at this point in the history
Rework how the resources list work with operations
  • Loading branch information
Guillaume St-Pierre authored Aug 6, 2019
2 parents a2ba082 + 8dcdcca commit 30f1eac
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 72 deletions.
8 changes: 7 additions & 1 deletion docs/docs/components/manifold-resource-list.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ example: |
label="my-resource-3"
logo="https://cdn.manifold.co/providers/logdna/logos/ftzzxwdr0c8wx6gh0ntf83fq4w.png"
resource-id="1234"
resource-status="unavailable"
resource-status="resize"
></manifold-resource-card-view>
<manifold-resource-card-view
label="my-resource-4"
logo="https://cdn.manifold.co/providers/logdna/logos/ftzzxwdr0c8wx6gh0ntf83fq4w.png"
resource-id="1234"
resource-status="deprovision"
></manifold-resource-card-view>
</div>
</manifold-resource-list>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,11 @@
--status-color: var(--manifold-color-info);
}

&[data-status='degraded'] {
&[data-status='resize'] {
--status-color: var(--manifold-color-warn);
}

&[data-status='offline'] {
&[data-status='deprovision'], &[data-status='offline'] {
--status-color: var(--manifold-color-error);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,6 @@ describe('<manifold-resource-card>', () => {
});
const el = await page.find('manifold-resource-card-view >>> [itemprop="status"]');

expect(el.innerText).toBe('Provision');
expect(el.innerText).toBe('Provisioning');
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,18 @@ interface EventDetail {

const AVAILABLE = 'available';
const PROVISIONING = 'provision';
const DEGRADED = 'degraded';
const RESIZING = 'resize';
const DEPROVISION = 'deprovision';
const OFFLINE = 'offline';

const statusToText = {
[AVAILABLE]: 'Available',
[PROVISIONING]: 'Provisioning',
[RESIZING]: 'Resizing',
[DEPROVISION]: 'Deprovisioning',
[OFFLINE]: 'Offline',
};

@Component({
tag: 'manifold-resource-card-view',
styleUrl: 'manifold-resource-card-view.css',
Expand Down Expand Up @@ -56,18 +65,31 @@ export class ManifoldResourceCardView {
return this.resourceLinkFormat.replace(/:resource/gi, this.label);
}

get status(): string {
get status() {
if (
this.resourceStatus &&
(this.resourceStatus === AVAILABLE ||
this.resourceStatus === PROVISIONING ||
this.resourceStatus === DEGRADED)
[AVAILABLE, PROVISIONING, RESIZING, DEPROVISION].includes(this.resourceStatus)
) {
return this.resourceStatus;
}
return OFFLINE;
}

get statusText() {
switch (this.resourceStatus) {
case AVAILABLE:
return statusToText[AVAILABLE];
case PROVISIONING:
return statusToText[PROVISIONING];
case RESIZING:
return statusToText[RESIZING];
case DEPROVISION:
return statusToText[DEPROVISION];
default:
return statusToText[OFFLINE];
}
}

async fetchResourceId(resourceLabel: string) {
if (!this.restFetch) {
return;
Expand Down Expand Up @@ -122,7 +144,7 @@ export class ManifoldResourceCardView {
<div class="status-box">
<div class="status" data-status={this.status}>
<div class="inner" itemprop="status">
{this.status[0].toUpperCase() + this.status.slice(1)}
{this.statusText}
</div>
</div>
</div>
Expand Down
160 changes: 98 additions & 62 deletions src/components/manifold-resource-list/manifold-resource-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ interface RealResourceBody extends Marketplace.ResourceBody {
user_id?: string;
}

const PROVISION = 'provision';
const RESIZE = 'resize';
const DEPROVISION = 'deprovision';
const TRANSFER = 'transfer';

@Component({
tag: 'manifold-resource-list',
styleUrl: 'manifold-resource-list.css',
Expand Down Expand Up @@ -64,26 +69,19 @@ export class ManifoldResourceList {
}

static opStateToStatus(operation: Provisioning.Operation): string {
switch (operation.body.type) {
case 'provision':
return (operation.body as Provisioning.provision).state === 'done'
? 'available'
: operation.body.type;
case 'resize':
return (operation.body as Provisioning.resize).state === 'done'
? 'available'
: operation.body.type;
case 'transfer':
return (operation.body as Provisioning.transfer).state === 'done'
? 'available'
: operation.body.type;
case 'deprovision':
return (operation.body as Provisioning.deprovision).state === 'done'
? 'unavailable'
: operation.body.type;
default:
return 'unavailable';
if (![PROVISION, RESIZE, TRANSFER, DEPROVISION].includes(operation.body.type)) {
return 'unavailable';
}
const opBody:
| Provisioning.provision
| Provisioning.resize
| Provisioning.transfer
| Provisioning.deprovision = operation.body;

if (opBody.state === 'done') {
return 'available';
}
return operation.body.type;
}

static userResources(resources: RealResource[]) {
Expand All @@ -98,17 +96,18 @@ export class ManifoldResourceList {
return;
}

const resourcesResp = await this.restFetch<Marketplace.Resource[]>({
service: 'marketplace',
endpoint: `/resources/?me`,
// TODO: All this will be removed when graphql comes in
const operationsResp = await this.restFetch<Provisioning.Operation[]>({
service: 'provisioning',
endpoint: `/operations/?is_deleted=false`,
});

if (resourcesResp instanceof Error) {
console.error(resourcesResp);
if (operationsResp instanceof Error) {
console.error(operationsResp);
return;
}

if (Array.isArray(resourcesResp)) {
if (Array.isArray(operationsResp)) {
const productsResp = await this.restFetch<Catalog.Product[]>({
service: 'catalog',
endpoint: `/products`,
Expand All @@ -118,51 +117,88 @@ export class ManifoldResourceList {
return;
}

const operationsResp = await this.restFetch<Provisioning.Operation[]>({
service: 'provisioning',
endpoint: `/operations/?is_deleted=false`,
const resourcesResp = await this.restFetch<Marketplace.Resource[]>({
service: 'marketplace',
endpoint: `/resources/?me`,
});

if (operationsResp instanceof Error) {
console.error(operationsResp);
if (resourcesResp instanceof Error) {
console.error(resourcesResp);
return;
}

this.resources = ManifoldResourceList.userResources(
[...resourcesResp].sort((a, b) => a.body.label.localeCompare(b.body.label))
).map(
(resource: Marketplace.Resource): FoundResource => {
const userResource = ManifoldResourceList.userResources(resourcesResp);

const resources: FoundResource[] = [];
// First, do a run through of the operations to add any resource not covered by one or in the process of being modified by one
operationsResp
.filter((op: Provisioning.Operation) =>
[PROVISION, RESIZE, TRANSFER, DEPROVISION].includes(op.body.type)
)
.forEach((operation: Provisioning.Operation) => {
const opBody:
| Provisioning.provision
| Provisioning.resize
| Provisioning.transfer
| Provisioning.deprovision = operation.body;

// Don't run this code is the operation is done, fallback to the simpler resource code.
if (opBody.state === 'done') {
return;
}

const resource = userResource.find((res: RealResource) => opBody.resource_id === res.id);

if (resource) {
const product = productsResp.find(
(prod: Catalog.Product): boolean => prod.id === resource.body.product_id
);

resources.push({
id: resource.id,
label: resource.body.label,
name: resource.body.name,
logo: product && product.body.logo_url,
status: ManifoldResourceList.opStateToStatus(operation),
});
return;
}
if (operation.body.type !== PROVISION) {
// Only provision operation without a resource should be processed
return;
}
const product = productsResp.find(
(prod: Catalog.Product): boolean => prod.id === resource.body.product_id
(prod: Catalog.Product): boolean =>
prod.id === (opBody as Provisioning.provision).product_id
);
const operation = operationsResp
.filter((op: Provisioning.Operation) =>
['provision', 'resize', 'transfer', 'deprovision'].includes(op.body.type)
)
.find((op: Provisioning.Operation) => {
switch (op.body.type) {
case 'provision':
return (op.body as Provisioning.provision).resource_id === resource.id;
case 'resize':
return (op.body as Provisioning.resize).resource_id === resource.id;
case 'transfer':
return (op.body as Provisioning.transfer).resource_id === resource.id;
case 'deprovision':
return (op.body as Provisioning.deprovision).resource_id === resource.id;
default:
return false;
}
});

return {
id: resource.id,
label: resource.body.label,
name: resource.body.name,
resources.push({
id: opBody.resource_id || '',
// Only the provision operation has this info
label: (opBody as Provisioning.provision).label || '',
name: (opBody as Provisioning.provision).name || '',
logo: product && product.body.logo_url,
status: operation ? ManifoldResourceList.opStateToStatus(operation) : 'available',
};
status: ManifoldResourceList.opStateToStatus(operation),
});
});

// Then run through all the real resource and add them as available if they weren't added by an operation
userResource.forEach((resource: RealResource) => {
if (resources.find((res: FoundResource) => res.id === resource.id)) {
return;
}
);

const product = productsResp.find(
(prod: Catalog.Product): boolean => prod.id === resource.body.product_id
);
resources.push({
id: resource.id,
label: resource.body.label,
name: resource.body.name,
logo: product && product.body.logo_url,
status: 'available',
});
});

this.resources = resources.sort((a, b) => a.label.localeCompare(b.label));
} else {
this.resources = [];
}
Expand Down

1 comment on commit 30f1eac

@vercel
Copy link

@vercel vercel bot commented on 30f1eac Aug 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.