Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions apps/api/src/subjects/__tests__/subjects.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,11 +133,19 @@ describe('SubjectsService', () => {
subjectModel.findFirst.mockResolvedValueOnce({ id: '123' });
await subjectsService.deleteById('123', { force: true });
expect(subjectModel.delete).not.toHaveBeenCalled();
expect(prismaClient.session.deleteMany).toHaveBeenCalled();
expect(prismaClient.instrumentRecord.deleteMany).toHaveBeenCalled();
expect(prismaClient.subject.deleteMany).toHaveBeenCalled();
expect(prismaClient.$transaction).toHaveBeenCalledOnce();
});
it('should pass operations to $transaction in order: instrumentRecord, session, subject', async () => {
subjectModel.findFirst.mockResolvedValueOnce({ id: '123' });
const instrumentRecordOp = 'instrumentRecord-op';
const sessionOp = 'session-op';
const subjectOp = 'subject-op';
prismaClient.instrumentRecord.deleteMany.mockReturnValueOnce(instrumentRecordOp);
prismaClient.session.deleteMany.mockReturnValueOnce(sessionOp);
prismaClient.subject.deleteMany.mockReturnValueOnce(subjectOp);
await subjectsService.deleteById('123', { force: true });
expect(prismaClient.$transaction).toHaveBeenCalledWith([instrumentRecordOp, sessionOp, subjectOp]);
});
it('should throw NotFoundException when subject does not exist', async () => {
subjectModel.findFirst.mockResolvedValueOnce(null);
await expect(subjectsService.deleteById('123')).rejects.toBeInstanceOf(NotFoundException);
Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/subjects/subjects.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,14 @@ export class SubjectsService {
return { success: true };
}
await this.prismaClient.$transaction([
this.prismaClient.session.deleteMany({
this.prismaClient.instrumentRecord.deleteMany({
where: {
subject: {
id: subject.id
}
}
}),
this.prismaClient.instrumentRecord.deleteMany({
this.prismaClient.session.deleteMany({
where: {
Comment thread
david-roper marked this conversation as resolved.
subject: {
id: subject.id
Expand Down
43 changes: 26 additions & 17 deletions cli/odc-cli
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ from argparse import (
ArgumentTypeError,
RawTextHelpFormatter,
)
from datetime import datetime
from datetime import datetime, timezone
from typing import Any, Callable, TypedDict
from urllib.error import HTTPError, URLError
from urllib.parse import parse_qsl, urlencode, urlparse, urlunparse, quote
Expand Down Expand Up @@ -242,15 +242,15 @@ class HttpClient:

@classmethod
def _request(cls, method: str, url: str, data: Any = None) -> HttpResponse:
headers = cls._get_default_headers()
body = json.dumps(data).encode('utf-8') if data is not None else None
headers = cls._get_default_headers(has_body=body is not None)
request = Request(url, data=body, headers=headers, method=method)
return cls._send(request)

@classmethod
def _send(cls, req: Request) -> HttpResponse:
try:
with urlopen(req, timeout=2) as response:
with urlopen(req, timeout=30) as response:
return cls._build_response(response)
except HTTPError as err:
return cls._build_response(err)
Expand Down Expand Up @@ -283,8 +283,10 @@ class HttpClient:
return HttpResponse(data=data, status=response.getcode())

@staticmethod
def _get_default_headers() -> dict[str, str]:
headers = {'Content-Type': 'application/json'}
def _get_default_headers(has_body: bool = False) -> dict[str, str]:
headers: dict[str, str] = {}
if has_body:
headers['Content-Type'] = 'application/json'
if config.access_token:
headers['Authorization'] = f'Bearer {config.access_token}'
return headers
Expand Down Expand Up @@ -366,7 +368,7 @@ class InstrumentRecordsCommands:
url = build_url_with_params(
f'{config.base_url}/v1/instrument-records',
{
'minDate': min_date,
'minDate': min_date.isoformat() if min_date is not None else None,
'instrumentId': instrument_id,
'subjectId': subject_id,
},
Expand Down Expand Up @@ -405,14 +407,21 @@ class SubjectCommands:
@require_token
@staticmethod
def find(min_date: datetime | None, subject_id: str | None) -> None:
url = build_url_with_params(
f'{config.base_url}/v1/subjects',
{
'minDate': min_date,
'subjectId': subject_id,
},
)
response = HttpClient.get(url)
if subject_id is not None:
url = f'{config.base_url}/v1/subjects/{quote(subject_id, safe="")}'
response = HttpClient.get(url)
else:
url = build_url_with_params(
f'{config.base_url}/v1/subjects',
{}
)
response = HttpClient.get(url)
if min_date is not None and isinstance(response.data, list):
min_date_utc = min_date.replace(tzinfo=timezone.utc) if min_date.tzinfo is None else min_date
response.data = [
s for s in response.data
if datetime.fromisoformat(s['createdAt'].replace('Z', '+00:00')) >= min_date_utc
]
Comment thread
david-roper marked this conversation as resolved.
print(response)


Expand Down Expand Up @@ -563,9 +572,9 @@ class CLI:
find_parser.add_argument(
'--min-date',
type=ArgumentTypes.valid_datetime,
help='filter records created after this date (format: yyyy-mm-dd or yyyy-mm-dd hh:mm:ss)',
help='filter subjects created after this date (format: yyyy-mm-dd or yyyy-mm-dd hh:mm:ss). Cannot be used with --subject-id',
)
find_parser.add_argument('--subject-id', help='filter by subject id')
find_parser.add_argument('--subject-id', help='find a single subject by exact id. Cannot be used with --min-date')
find_parser.set_defaults(fn=SubjectCommands.find)

def _create_instruments_parser(self):
Expand Down Expand Up @@ -606,7 +615,7 @@ class CLI:
app_group.add_argument(
'--dummy-subject-count',
type=int,
help='.umber of dummy subjects to create for the demo',
help='number of dummy subjects to create for the demo',
)
app_group.add_argument(
'--records-per-subject',
Expand Down
Loading