## Mocking Microservice REST Requests

There are certain situations in which microservice REST requests from other teams are a work in
progress, and have not yet been fully implemented. In these types of situations, it may be 
desireable to mock out our expectations of what the payload response will be for use in testing
the addition of the data to the graph and unblocking development of frontend features.

How can we handle this in a way that will reduce the overhead in eventually removing mock payloads
from our code when the microservice REST request is properly implemented? The 
[`OwsDataSource`](/src/connectors/ows-data-source/ows-data-source.js) class provides functions
which allow you to register mock payloads for a `GET` request, and will automatically return the
specified payload if the request matches a registered mock.

### Implementing a Mock REST Payload

#### Example Context

Let's say there's going to be a new endpoint on 
[`ows-users`](https://github.com/theorchard/ows-users) which will return user profile data from a
route `/users/identity/:identity/application/:application/profiles` with the general shape:

```json
{
    "items": [
        {
            "profile_type": "ArtistProfile",
            "profile_name": "artistozuna_profile1",
            "profile_id": 1346777
        },
        {
            "profile_type": "LabelProfile",
            "profile_name": "frenchkiss-vc",
            "profile_id": 6918
        }
    ]
}
```

In the [`ows-users`](/src/connectors/ows-users/ows-users.js) connector, we might want to define a
function `getUserProfiles` which retrieves this data from the work-in-progress endpoint:

```javascript
class OwsUsers extends OwsDataSource {
    
    async getUserProfiles() {
        // return user profiles
    }
    
}
```

#### Desired Outcome

We know that eventually we will be able to use `super.get` to retrieve this data, so our final code
when this service is implemented might look something like:

```javascript
import { formatProfile } from './formatters/profile';

class OwsUsers extends OwsDataSource {
    
    async getUserProfiles() {
        const { application, identity } = this.context;
        const { items } =
            (await this.get(`users/identity${identity}/application/${application}/profiles`)) || 
                { items: [] };
        return items.map(formatProfile);
    }
    
}
```

How can we leverage the REST mocking built into `OwsDataSource` to return a mock payload for this
endpoint? Excellent question, I'm glad you asked!

#### Using `OwsDataSource` Mocks

`OwsDataSource` provides a `mock` method which allows you to register a mock payload as a response
to a particular URL and set of query parameters. This should be done in the object constructor:

```javascript
class OwsUsers extends OwsDataSource {
    
    constructor() {
        super();
        this.baseURL = OWS_USERS_URL;
        this.mock({
            url: 'users/identity/5c548bd462501638ea3ed444/application/orchard-go/profiles',
            payload: {
                items: [
                    {
                        profile_type: "ArtistProfile",
                        profile_name: "artistozuna_profile1",
                        profile_id: 1346777
                    },
                    {
                        profile_type: "LabelProfile",
                        profile_name: "frenchkiss-vc",
                        profile_id: 6918
                    }
                ]
            }
        });
    }
    
    async getUserProfiles() {
        const { application, identity } = this.context;
        const { items } =
            (await this.get(`users/identity${identity}/application/${application}/profiles`)) || 
                { items: [] };
        return items.map(formatProfile);
    }
    
}
```

Now, when the `get` method is called with the `url` specified in the mock, the mock payload will be
returned instead of executing a real API call to `ows-users`. Neat!

You can also specify a path by which to find a JSON file which could contain this data instead, so 
you might alternatively do something like:

```javascript
class OwsUsers extends OwsDataSource {
    
    constructor() {
        super();
        this.baseURL = OWS_USERS_URL;
        this.mock({
            url: 'users/identity/5c548bd462501638ea3ed444/application/orchard-go/profiles',
            payload: `${__dirname}/__mocks__/profile.json`
        });
    }
    
    async getUserProfiles() {
        const { application, identity } = this.context;
        const { items } =
            (await this.get(`users/identity${identity}/application/${application}/profiles`)) || 
                { items: [] };
        return items.map(formatProfile);
    }
    
}
```

Note that by convention, mock JSON responses should live in a `__mocks__` directory inside the
connector folder.

#### Considerations

**Why wouldn't we just mock the `getUserProfiles` method itself?**

Good question! We know that we'll eventually receive a REST payload back from the profiles
endpoint, and we generally know its shape. We will still, however, want to implement a formatter
which will return the shape of profiles for the object graph. So if we were to simply return a mock
object:

```javascript
class OwsUsers extends OwsDataSource {
    
    async getUserProfiles() {
        return [
            {
                profileType: "ArtistProfile",
                profileName: "artistozuna_profile1",
                profileId: 1346777
            },
            {
                profileType: "LabelProfile",
                profileName: "frenchkiss-vc",
                profileId: 6918
            }
        ]
    }
    
}
```

we haven't yet implemented the formatter logic to convert from the REST response the GraphQL schema
format. Additionally, we'll probably also want to put this request inside of a `DataLoader` so that
we're not executing multiple requests to `ows-users` if profiles are referenced twice in the object
graph:

```javascript
class OwsUsers extends OwsDataSource {
    
    constructor() {
        super();
        this.baseURL = OWS_USERS_URL;
        
        this.userProfileLoader = new DataLoader(
            async profileParams =>
                Promise.all(profileParams.map(async ({ identity, application }) => {
                    const result = await this.get(
                        `users/identity/${identity}/application/${application}/profiles`
                    );
                    return result || null;
                })),
            {
                cacheKeyFn: ({ identity, application }) => `${identity}${application}`
            }
        );

        this.mock({
            url: 'users/identity/5c548bd462501638ea3ed444/application/orchard-go/profiles',
            payload: `${__dirname}/__mocks__/profile.json`
        });
    }
    
    async getUserProfiles() {
        const { application, identity } = this.context;
        const { items } =
            (await this.get(`users/identity${identity}/application/${application}/profiles`)) || 
                { items: [] };
        return items.map(formatProfile);
    }
    
}
```

If we simply mock the return value from `getUserProfiles`, we're not thinking about the formatters
or using `DataLoader` up front, so that's more work we'd have to do down the line once this
endpoint is implemented. It's also much easier to remove the call to `this.mock` and then all the
data will flow to the appropriate endpoints once implemented.
