Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor GitLoader #1796

Merged
merged 1 commit into from
Jul 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 2 additions & 9 deletions packages/loaders/git/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,10 @@
},
"dependencies": {
"@graphql-tools/utils": "6.0.14",
"@graphql-tools/graphql-tag-pluck": "6.0.14",
"simple-git": "2.13.1"
},
"buildOptions": {
"external": [
"simple-git",
"simple-git/promise"
]
"@graphql-tools/graphql-tag-pluck": "6.0.14"
},
"publishConfig": {
"access": "public",
"directory": "dist"
}
}
}
17 changes: 5 additions & 12 deletions packages/loaders/git/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,9 @@ export type GitLoaderOptions = SingleFileOptions & {
/**
* Additional options to pass to `graphql-tag-pluck`
*/
pluckConfig: GraphQLTagPluckOptions;
pluckConfig?: GraphQLTagPluckOptions;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Making this optional since graphql-tag-pluck doesn't actually require an options object to be passed in.

};

const createInvalidExtensionError = (path: string) => new Error(`Invalid file extension: ${path}`);

/**
* This loader loads a file from git.
*
Expand Down Expand Up @@ -72,7 +70,10 @@ export class GitLoader implements UniversalLoader {

const rawSDL = await gqlPluckFromCodeString(pointer, content, options.pluckConfig);

return ensureSource({ rawSDL, pointer, path });
return {
location: pointer,
rawSDL,
};
}

loadSync(pointer: string, options: GitLoaderOptions) {
Expand All @@ -86,17 +87,9 @@ export class GitLoader implements UniversalLoader {

const rawSDL = gqlPluckFromCodeStringSync(pointer, content, options.pluckConfig);

return ensureSource({ rawSDL, pointer, path });
}
}

function ensureSource({ rawSDL, pointer, path }: { rawSDL: string; pointer: string; path: string }) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Dropping ensureSource and createInvalidExtensionError because gqlPluckFromCodeString guarantees a string is returned and will already throw if an extension is invalid.

if (rawSDL) {
return {
location: pointer,
rawSDL,
};
}

throw createInvalidExtensionError(path);
}
21 changes: 13 additions & 8 deletions packages/loaders/git/src/load-git.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
import simplegit from 'simple-git/promise';
import simplegitSync from 'simple-git';
import { exec, execSync } from 'child_process';

type Input = { ref: string; path: string };

const createLoadError = (error: any) => new Error('Unable to load schema from git: ' + error);
const createLoadError = (error: any) => new Error('Unable to load file from git: ' + error);
const createCommand = ({ ref, path }: Input) => {
return [`${ref}:${path}`];
return `git show ${ref}:${path}`;
};

/**
* @internal
*/
export async function loadFromGit(input: Input): Promise<string | never> {
try {
const git = simplegit();
return await git.show(createCommand(input));
return await new Promise((resolve, reject) => {
exec(createCommand(input), { encoding: 'utf-8' }, (error, stdout) => {
if (error) {
reject(error);
} else {
resolve(stdout);
}
});
});
} catch (error) {
throw createLoadError(error);
}
Expand All @@ -25,8 +31,7 @@ export async function loadFromGit(input: Input): Promise<string | never> {
*/
export function loadFromGitSync(input: Input): string | never {
try {
const git = simplegitSync();
return git.show(createCommand(input));
return execSync(createCommand(input), { encoding: 'utf-8' });
} catch (error) {
throw createLoadError(error);
}
Expand Down
78 changes: 78 additions & 0 deletions packages/loaders/git/tests/loader.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { execSync } from 'child_process';

import { Source } from '@graphql-tools/utils';

import { GitLoader } from '../src';
import { runTests } from '../../../testing/utils';

describe('GitLoader', () => {
const loader = new GitLoader();
const lastCommit = execSync('git rev-parse HEAD', { encoding: 'utf-8' }).replace(/\n/g, '');
const getPointer = (fileName: string) => {
return `git:${lastCommit}:packages/loaders/git/tests/test-files/${fileName}`;
};

describe('loaderId', () => {
it('should return a loader id', () => {
expect(loader.loaderId()).toBeDefined();
});
});

describe('canLoad', () => {
runTests({
async: loader.canLoad.bind(loader),
sync: loader.canLoadSync.bind(loader),
})(canLoad => {
it('should return true for a valid pointer', async () => {
expect(canLoad(getPointer('some-file.graphql'))).resolves.toBe(true);
});

it('should return false if pointer does not begin with "git:"', async () => {
expect(canLoad(getPointer('some-file.graphql').substring(4))).resolves.toBe(false);
});

it('should return false if pointer is not a string', async () => {
expect(canLoad(42 as any)).resolves.toBe(false);
});
});
});

describe('load', () => {
runTests({
async: loader.load.bind(loader),
sync: loader.loadSync.bind(loader),
})(load => {
it('should load document from a .graphql file', async () => {
const result: Source = await load(getPointer('type-defs.graphql'), {});
expect(result.document).toBeDefined();
});

it('should load introspection data from a .json file', async () => {
const result: Source = await load(getPointer('introspection.json'), {});
expect(result.schema).toBeDefined();
});

it('should load type definitions from a .json file', async () => {
const result: Source = await load(getPointer('type-defs.json'), {});
expect(result.document).toBeDefined();
});

it('should load type definitions from a pluckable file', async () => {
const result: Source = await load(getPointer('pluckable.ts'), {});
expect(result.rawSDL).toBeDefined();
});

it('should throw when pointer is malformed', async () => {
await expect(load(getPointer('foo:graphql'), {})).rejects.toThrowError(
'Schema pointer should match "git:branchName:path/to/file"'
);
});

it('should throw when the file does not exist', async () => {
await expect(load(getPointer('wrong-filename.graphql'), {})).rejects.toThrowError(
'Unable to load file from git'
);
});
});
});
});
3 changes: 3 additions & 0 deletions packages/loaders/git/tests/test-files/bad-extension.garphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
type Query {
hello: String
}