-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
feat(repository): initial AtomicCrudRepository implementation #1974
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add an end-to-end test to verify that when a connector does not support atomic findOrCreate
, the HTTP responses have status code set to 501 Not Implemented
. A pointer to help you find the place where to implement this functionality:
https://github.com/strongloop/loopback-next/blob/1bcdb5b69221501b3952bd70c0c78409b600d1ab/packages/rest/src/providers/reject.provider.ts#L14-L16
filter: Filter<T>, | ||
entity: DataObject<T>, | ||
options?: Options, | ||
): Promise<[T, boolean]>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👎 for using an anonymous tuple. Let's define a new interface with named properties please.
export interface FindOrCreateResult<T extends Entity> {
entity: T;
wasCreated: boolean;
}
I am not sure what would be the best name for the boolean property. I find the short name "created" misleading, because "created" is usually set to a timestamp (creation time). Few alternatives that come to my mind:
- wasCreated
- isNew
- wasFound
- found
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On the second thought, I think I like found
most.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I was thinking about defining a type/interface too, but thought it might be a one off thing. I think found
is reasonable 👍.
this.modelClass.findOrCreate(filter as legacy.Filter, entity, options), | ||
); | ||
return [this.toEntity(result[0]), result[1]]; | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use reverse logic + early return to keep this method simpler & easier to reason about.
async findOrCreate(
filter: Filter<T>,
entity: DataObject<T>,
options?: AnyObject | undefined,
): Promise<[T, boolean]> {
const canRunAtomically = typeof this.dataSource.connector.findOrCreate === 'function';
if (!canRunAtomically) {
throw new Error('Method not implemented.');
// FIXME add machine-readable `err.code`
}
const result = await ensurePromise(
this.modelClass.findOrCreate(filter as legacy.Filter, entity, options),
);
return [this.toEntity(result[0]), result[1]];
}
toVisit: String[]; | ||
} | ||
|
||
it('converts PropertyDefinition with array type', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see how does this test relates for findOrCreate
functionality, could you please help me to understand?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied the tests from DefaultCrudRepository
unit test file because I was worried code coverage would drop if I didn't have them and since we extend from it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, as far as I understand code coverage, it counts lines of source code. As long as there is a test executing the source code behind DefaultCrudRepository
in the file legacy-juggler-bridge.ts
, it does not matter whether this test creates an instance of DefaultCrudRepository
directly or an instance of a class inheriting from this Repository implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah yeah that is correct, I misunderstood how it worked. Thanks for the explanation, I will remove them and have assertions that you proposed in #1974 (comment).
); | ||
}); | ||
|
||
it('shares the backing PersistedModel across repo instances', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't this scenario already covered by other tests? Why are we duplicating them here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I copied the tests from DefaultCrudRepository unit test file because I was worried code coverage would drop if I didn't have them and since we extend from it.
}); | ||
}); | ||
|
||
context('Non atomic CRUD operations', () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These operations are already covered by DefaultCrudRepository
tests, please don't duplicate them again.
If we want to verify that AtomicCrudRepository
uses the same CRUD implementation as DefaultCrudRepository
, then just check for function equality.
Here is what I have in my mind, I hope it will work as I expect:
const repo = new DefaultAtomicCrudRepository(Note, ds);
expect(repo.create).to.equal(DefaultCrudRepository.prototype.create);
}); | ||
|
||
context('Atomic CRUD operations', () => { | ||
// TODO: how can we test a connector that doesn't have findOrCreate? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how can we test a connector that doesn't have findOrCreate?
See the existing test suite in juggler for inspiration:
https://github.com/strongloop/loopback-datasource-juggler/blob/f0a6bd146b7ef2f987fd974ffdb5906cf6a584db/test/memory.test.js#L904-L923
ds.connector.findOrCreate = false;
I think the following approach is a little bit more clean, but IDK if it works:
ds.connector.findOrCreate = undefined;
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome, thanks for the link @bajtos!
1b7548c
to
d3cce33
Compare
Closing as rejected in favour of #2005 (see #1956 (comment)). |
Connect to #1956. Implement an atomic
findOrCreate
function with a new interface for atomic operations as suggested in #1956 with unit tests (most copied from DefaultCrudRepository). Needs loopbackio/loopback-datasource-juggler#1654 to land first.Checklist
npm test
passes on your machinepackages/cli
were updatedexamples/*
were updated