Skip to content

Commit

Permalink
Merge pull request #301 from manifoldco/gui/profile-endpoint-in-ui
Browse files Browse the repository at this point in the history
Use the profile graphQL endpoint if ownerId is not set
  • Loading branch information
Guillaume St-Pierre authored Jul 29, 2019
2 parents 5dc8676 + 5158765 commit b9ef628
Show file tree
Hide file tree
Showing 10 changed files with 170 additions and 29 deletions.
20 changes: 14 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Added `oauth-url` prop to `manifold-auth-token` component.

### Fixed
- Fixed name for `manifold-service-view` to be `manifold-service-card-view` to match documentation.
- Added missing support for theme variable `--manifold-tag-free-text-color`.

### Deprecated
- Deprecated `resource-label` attibute on `manifold-data-product-logo`. Use `manifold-data-resource-logo` component instead.
- Deprecated `resource-label` attribute on `manifold-data-product-logo`. Use `manifold-data-resource-logo` component instead.

### Changed
- Added graphqlFetch to `manifold-connection`.
- Converted `manifold-data-product-logo` to use GraphQL.
- Changed the provision button so it fetches the owner ID automatically if not set.

## [v0.5.0]
### Added
- Added a SSO data button and the resource wrapper for ssoing into a resource's product dashboard.
- Added a new CTA slot in the product card for displaying unique cta content.
- Added `oauth-url` prop to `manifold-auth-token` component.

### Fixed
- Fixed the provision button requiring the label to be set, preventing or automatic label generation from working.
- Fixed name for `manifold-service-view` to be `manifold-service-card-view` to match documentation.
- Added missing support for theme variable `--manifold-tag-free-text-color`.

### Changed
- Added graphqlFetch to `manifold-connection`.
- Converted `manifold-data-product-logo` to use GraphQL.
- Changed the `manifold-auth-token` component to now use the shadowcat oauth system rather than only use the given token. This enables platforms to now use real authentication.

## [0.4.3]
### Fixed
Expand Down
20 changes: 17 additions & 3 deletions docs/docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,32 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]
### Added
- Added `oauth-url` prop to `manifold-auth-token` component.

### Fixed
- Fixed name for `manifold-service-view` to be `manifold-service-card-view` to match documentation.
- Added missing support for theme variable `--manifold-tag-free-text-color`.

### Deprecated
- Deprecated `resource-label` attribute on `manifold-data-product-logo`. Use `manifold-data-resource-logo` component instead.

### Changed
- Added graphqlFetch to `manifold-connection`.
- Converted `manifold-data-product-logo` to use GraphQL.
- Changed the provision button so it fetches the owner ID automatically if not set.

## [v0.5.0]
### Added
- Added a SSO data button and the resource wrapper for ssoing into a resource's product dashboard.
- Added a new CTA slot in the product card for displaying unique cta content.

### Deprecated
- Deprecated `resource-label` attibute on `manifold-data-product-logo`. Use `manifold-data-resource-logo` component instead.

### Fixed
- Fixed the provision button requiring the label to be set, preventing or automatic label generation from working.

### Changed
- Changed the `manifold-auth-token` component to now use the shadowcat oauth system rather than only use the given token. This enables platforms to now use real authentication.

## [0.4.3]
### Fixed
- Fixed the rename and deprovision button not behaving properly when used in their resource warpers
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/data/manifold-data-provision-button.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ recommend relying on listening from events from the [plan
selector](#manifold-plan-selector) component. You could do that like so:

```js
const userId = ''; // Note: must be set
const userId = ''; // Note: Can be omitted, will be fetch automatically.
const resourceLabel = ''; // Can be obtained from your own input

function updateButton({ detail: { features, planLabel, productLabel, regionName } }) {
Expand Down
14 changes: 12 additions & 2 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export namespace Components {
'selectedResource'?: Gateway.Resource;
}
interface ManifoldAuthToken {
'oauthUrl'?: string;
/**
* _(hidden)_ Passed by `<manifold-connection>`
*/
Expand Down Expand Up @@ -63,7 +64,7 @@ export namespace Components {
/**
* _(optional)_ Specify `env="stage"` for staging
*/
'env': 'stage' | 'prod';
'env': 'local' | 'stage' | 'prod';
}
interface ManifoldCostDisplay {
'baseCost'?: number;
Expand Down Expand Up @@ -184,6 +185,10 @@ export namespace Components {
* _(hidden)_ Passed by `<manifold-connection>`
*/
'connection'?: Connection;
/**
* _(hidden)_ Passed by `<manifold-connection>`
*/
'graphqlFetch'?: <T>(body: GraphqlRequestBody) => GraphqlResponseBody<T>;
'ownerId'?: string;
/**
* Plan to provision (slug)
Expand Down Expand Up @@ -1110,6 +1115,7 @@ declare namespace LocalJSX {
'selectedResource'?: Gateway.Resource;
}
interface ManifoldAuthToken extends JSXBase.HTMLAttributes<HTMLManifoldAuthTokenElement> {
'oauthUrl'?: string;
'onManifoldOauthTokenChange'?: (event: CustomEvent<any>) => void;
/**
* _(hidden)_ Passed by `<manifold-connection>`
Expand Down Expand Up @@ -1141,7 +1147,7 @@ declare namespace LocalJSX {
/**
* _(optional)_ Specify `env="stage"` for staging
*/
'env'?: 'stage' | 'prod';
'env'?: 'local' | 'stage' | 'prod';
}
interface ManifoldCostDisplay extends JSXBase.HTMLAttributes<HTMLManifoldCostDisplayElement> {
'baseCost'?: number;
Expand Down Expand Up @@ -1268,6 +1274,10 @@ declare namespace LocalJSX {
* _(hidden)_ Passed by `<manifold-connection>`
*/
'connection'?: Connection;
/**
* _(hidden)_ Passed by `<manifold-connection>`
*/
'graphqlFetch'?: <T>(body: GraphqlRequestBody) => GraphqlResponseBody<T>;
'onManifold-provisionButton-click'?: (event: CustomEvent<any>) => void;
'onManifold-provisionButton-error'?: (event: CustomEvent<any>) => void;
'onManifold-provisionButton-invalid'?: (event: CustomEvent<any>) => void;
Expand Down
7 changes: 1 addition & 6 deletions src/components/manifold-auth-token/manifold-auth-token.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,7 @@ export class ManifoldAuthToken {

render() {
return (
<div>
<manifold-oauth
onReceiveManifoldToken={this.setInternalToken}
oauthUrl={this.oauthUrl}
></manifold-oauth>
</div>
<manifold-oauth onReceiveManifoldToken={this.setInternalToken} oauthUrl={this.oauthUrl} />
);
}
}
Expand Down
6 changes: 2 additions & 4 deletions src/components/manifold-connection/manifold-connection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function getDefaultToken() {
@Component({ tag: 'manifold-connection' })
export class ManiTunnel {
/** _(optional)_ Specify `env="stage"` for staging */
@Prop() env: 'stage' | 'prod' = 'prod';
@Prop() env: 'local' | 'stage' | 'prod' = 'prod';

@State() authToken?: string = getDefaultToken();

Expand All @@ -40,8 +40,6 @@ export class ManiTunnel {
}

render() {
const endpoint = this.env === 'stage' ? 'https://api.stage.manifold.co/graphql' : undefined;

return (
<Tunnel.Provider
state={{
Expand All @@ -51,7 +49,7 @@ export class ManiTunnel {
graphqlFetch: createGraphqlFetch({
getAuthToken: this.getAuthToken,
setAuthToken: this.setAuthToken,
endpoint,
endpoint: connections[this.env].graphql,
}),
}}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,27 @@ import { connections } from '../../utils/connections';
import { ManifoldDataProvisionButton } from './manifold-data-provision-button';

describe('<manifold-data-provision-button>', () => {
it('fetches product and plan id on load', () => {
it('fetches product, plan and profile id on load', () => {
const productLabel = 'test-product';
const planLabel = 'test-plan';

const provisionButton = new ManifoldDataProvisionButton();
provisionButton.fetchProductPlanId = jest.fn();
provisionButton.fetchProfileId = jest.fn();
provisionButton.productLabel = productLabel;
provisionButton.planLabel = planLabel;
provisionButton.componentWillLoad();
expect(provisionButton.fetchProductPlanId).toHaveBeenCalledWith(productLabel, planLabel);
expect(provisionButton.fetchProfileId).toHaveBeenCalled();
});

it('does not fetch the profile id if set on load', () => {
const provisionButton = new ManifoldDataProvisionButton();
provisionButton.fetchProfileId = jest.fn();
provisionButton.ownerId = '1234';
provisionButton.componentWillLoad();

expect(provisionButton.fetchProfileId).not.toHaveBeenCalled();
});

it('fetches product and plan id on change', () => {
Expand Down Expand Up @@ -172,6 +183,69 @@ describe('<manifold-data-provision-button>', () => {
});
});

describe('when created with no owner ID', () => {
afterEach(() => {
fetchMock.restore();
});

const profile = { profile: { id: '1234' } };

beforeEach(() => {
fetchMock
.mock(`${connections.prod.catalog}/products/?label=test`, [Product])
.mock(`${connections.prod.catalog}/plans/?product_id=${Product.id}&label=test`, [
ExpandedPlan,
]);
});

it('will fetch the owner id on load', async () => {
const page = await newSpecPage({
components: [ManifoldDataProvisionButton],
html: `
<manifold-data-provision-button
product-label="test"
plan-label="test"
>Provision</manifold-data-provision-button>
`,
});

const root = page.rootInstance as ManifoldDataProvisionButton;
// TODO: Find a smarter way to call this. This is always undefined, even if the component is wrapper in the manifold-connection
// @ts-ignore
root.graphqlFetch = jest.fn(() => ({ data: profile }));

root.componentWillLoad();
await page.waitForChanges();

expect(root.graphqlFetch).toHaveBeenCalled();
expect(root.ownerId).toEqual(profile.profile.id);
});

it('will do nothing if the owner id is set', async () => {
const page = await newSpecPage({
components: [ManifoldDataProvisionButton],
html: `
<manifold-data-provision-button
product-label="test"
plan-label="test"
owner-id="5678"
>Provision</manifold-data-provision-button>
`,
});

const root = page.rootInstance as ManifoldDataProvisionButton;
// TODO: Find a smarter way to call this. This is always undefined, even if the component is wrapper in the manifold-connection
// @ts-ignore
root.graphqlFetch = jest.fn(() => ({ data: profile }));

root.componentWillLoad();
await page.waitForChanges();

expect(root.graphqlFetch).not.toHaveBeenCalled();
expect(root.ownerId).toEqual('5678');
});
});

describe('When sending a request to provision', () => {
afterEach(() => {
fetchMock.restore();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { h, Component, Prop, Element, State, Watch, Event, EventEmitter } from '@stencil/core';
import { gql } from '@manifoldco/gql-zero';

import { Gateway } from '../../types/gateway';
import Tunnel from '../../data/connection';
import { globalRegion } from '../../data/region';
import { withAuth } from '../../utils/auth';
import { Connection, connections } from '../../utils/connections';
import { Catalog } from '../../types/catalog';
import { GraphqlRequestBody, GraphqlResponseBody } from '../../utils/graphqlFetch';

/* eslint-disable no-console */

Expand All @@ -21,13 +23,31 @@ interface ErrorMessage {
resourceLabel?: string;
}

interface Profile {
id: string;
}

interface ProfileMessage {
profile: Profile;
}

const query = gql`
query ProfileId {
profile {
id
}
}
`;

@Component({ tag: 'manifold-data-provision-button' })
export class ManifoldDataProvisionButton {
@Element() el: HTMLElement;
/** _(hidden)_ Passed by `<manifold-connection>` */
@Prop() connection?: Connection = connections.prod;
/** _(hidden)_ Passed by `<manifold-connection>` */
@Prop() authToken?: string;
/** _(hidden)_ Passed by `<manifold-connection>` */
@Prop() graphqlFetch?: <T>(body: GraphqlRequestBody) => GraphqlResponseBody<T>;
/** Product to provision (slug) */
@Prop() productLabel?: string;
/** Plan to provision (slug) */
Expand Down Expand Up @@ -61,6 +81,9 @@ export class ManifoldDataProvisionButton {
if (this.productLabel) {
this.fetchProductPlanId(this.productLabel, this.planLabel);
}
if (!this.ownerId) {
this.fetchProfileId();
}
}

async provision() {
Expand Down Expand Up @@ -172,6 +195,19 @@ export class ManifoldDataProvisionButton {
this.planId = plans[0].id;
}

async fetchProfileId() {
if (!this.graphqlFetch || this.ownerId) {
return;
}

const { data } = await this.graphqlFetch<ProfileMessage>({ query });
console.log(data);

if (data) {
this.ownerId = data.profile.id;
}
}

validate(input: string) {
return /^[a-z][a-z0-9]*/.test(input);
}
Expand All @@ -189,4 +225,4 @@ export class ManifoldDataProvisionButton {
}
}

Tunnel.injectProps(ManifoldDataProvisionButton, ['connection', 'authToken']);
Tunnel.injectProps(ManifoldDataProvisionButton, ['connection', 'authToken', 'graphqlFetch']);
4 changes: 4 additions & 0 deletions src/utils/connections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export interface Connection {
marketplace: string;
provisioning: string;
connector: string;
graphql: string;
}

export type Env = 'local' | 'stage' | 'prod';
Expand All @@ -18,6 +19,7 @@ export const connections: Connections = {
marketplace: 'http://api.marketplace.arigato.tools/v1',
provisioning: 'http://api.provisioning.arigato.tools/v1',
connector: 'http://api.connector.arigato.tools/v1',
graphql: 'http://graphql.arigato.tools/graphql',
},
stage: {
billing: 'https://api.billing.stage.manifold.co/v1',
Expand All @@ -26,6 +28,7 @@ export const connections: Connections = {
marketplace: 'https://api.marketplace.stage.manifold.co/v1',
provisioning: 'https://api.provisioning.stage.manifold/v1',
connector: 'https://api.connector.stage.manifold/v1',
graphql: 'https://api.stage.manifold.co/graphql',
},
prod: {
billing: 'https://api.billing.manifold.co/v1',
Expand All @@ -34,5 +37,6 @@ export const connections: Connections = {
marketplace: 'https://api.marketplace.manifold.co/v1',
provisioning: 'https://api.provisioning.manifold.co/v1',
connector: 'https://api.connector.manifold.co/v1',
graphql: 'https://api.manifold.co/graphql',
},
};
Loading

1 comment on commit b9ef628

@vercel
Copy link

@vercel vercel bot commented on b9ef628 Jul 29, 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.