Skip to content

Commit

Permalink
Merge pull request #307 from gwynndp/contract-query
Browse files Browse the repository at this point in the history
Add Contract endpoint and queries
  • Loading branch information
Kpoke committed Nov 13, 2023
2 parents de848bd + 43b4a69 commit ccb03c5
Show file tree
Hide file tree
Showing 12 changed files with 390 additions and 14 deletions.
2 changes: 2 additions & 0 deletions server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import responseTime from 'response-time';
import organizationsRouterV2 from 'routers/organizationsRouterV2';
import boundsRouter from './routers/boundsRouter';
import capturesRouter from './routers/capturesRouter';
import contractsRouter from './routers/contractsRouter';
import countriesRouter from './routers/countriesRouter';
import gisRouter from './routers/gisRouter';
import growerAccountsRouter from './routers/growerAccountsRouter';
Expand Down Expand Up @@ -80,6 +81,7 @@ app.use('/v2/growers', growerAccountsRouter);
app.use('/v2/trees', treesRouterV2);
app.use('/bounds', boundsRouter);
app.use('/gis', gisRouter);
app.use('/contract', contractsRouter);
// Global error handler
app.use(errorHandler);

Expand Down
193 changes: 193 additions & 0 deletions server/infra/database/ContractRepository.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import Contract from 'interfaces/Contract';
import ContractFilter from 'interfaces/ContractFilter';
import FilterOptions from 'interfaces/FilterOptions';
import HttpError from 'utils/HttpError';
import BaseRepository from './BaseRepository';
import Session from './Session';

export default class ContractRepository extends BaseRepository<Contract> {
constructor(session: Session) {
super('contract', session);
this.tableName = 'contracts.contract';
}

filterWhereBuilder(object, builder) {
const result = builder;
const {
whereNulls = [],
whereNotNulls = [],
whereIns = [],
...parameters
} = object;

if (parameters.tokenized === 'true') {
whereNotNulls.push('wallet.token.id');
} else if (parameters.tokenized === 'false') {
whereNulls.push('wallet.token.id');
}
delete parameters.tokenized;

result.whereNot(`${this.tableName}.status`, 'deleted');

whereNotNulls.forEach((whereNot) => {
// to map table names to fields for query
switch (true) {
case whereNot === 'tag_id':
result.whereNotNull('treetracker.capture_tag.tag_id');
break;
default:
result.whereNotNull(whereNot);
}
});

whereNulls.forEach((whereNull) => {
// to map table names to fields for query
switch (true) {
case whereNull === 'tag_id':
result.whereNull('treetracker.capture_tag.tag_id');
break;
default:
result.whereNull(whereNull);
}
});

whereIns.forEach((whereIn) => {
result.whereIn(whereIn.field, whereIn.values);
});

const filterObject = { ...parameters };

if (filterObject.startDate) {
result.where(
`${this.tableName}.captured_at`,
'>=',
filterObject.startDate,
);
delete filterObject.startDate;
}
if (filterObject.endDate) {
result.where(`${this.tableName}.captured_at`, '<=', filterObject.endDate);
delete filterObject.endDate;
}

if (filterObject.id) {
result.where(`${this.tableName}.id`, '=', filterObject.id);
delete filterObject.id;
}

if (filterObject.organization_id) {
result.where(`${this.tableName}.growing_organization_id`, 'in', [
...filterObject.organization_id,
]);
delete filterObject.organization_id;
}

result.where(filterObject);
}

async getByFilter(filterCriteria: ContractFilter, options: FilterOptions) {
const knex = this.session.getDB();
const { sort, ...filter } = filterCriteria;

let promise = knex
.select(
knex.raw(
`
${this.tableName}.id,
${this.tableName}.status,
${this.tableName}.notes,
${this.tableName}.created_at,
${this.tableName}.updated_at,
${this.tableName}.signed_at,
${this.tableName}.closed_at,
${this.tableName}.listed,
row_to_json(agreement.*) AS agreement,
row_to_json(grower_account.*) AS worker,
row_to_json(stakeholder.*) AS stakeholder
FROM ${this.tableName}
LEFT JOIN contracts.agreement AS agreement
ON agreement.id = ${this.tableName}.agreement_id
LEFT JOIN stakeholder.stakeholder AS stakeholder
ON stakeholder.id = agreement.growing_organization_id
LEFT JOIN treetracker.grower_account AS grower_account
ON grower_account.id = ${this.tableName}.worker_id
`,
),
)
.where((builder) => this.filterWhereBuilder(filter, builder));

promise = promise.orderBy(
`${this.tableName}.${sort?.order_by}` || `${this.tableName}.id`,
sort?.order || 'desc',
);

const { limit, offset } = options;
if (limit) {
promise = promise.limit(limit);
}
if (offset) {
promise = promise.offset(offset);
}

const captures = await promise;

return captures;
}

async getCount(filterCriteria: ContractFilter) {
const knex = this.session.getDB();
const { ...filter } = filterCriteria;

const result = await knex
.select(
knex.raw(
`COUNT(*) AS count
FROM ${this.tableName}
LEFT JOIN contracts.agreement AS agreement
ON agreement.id = ${this.tableName}.agreement_id
LEFT JOIN stakeholder.stakeholder AS stakeholder
ON stakeholder.id = agreement.growing_organization_id
LEFT JOIN treetracker.grower_account AS grower_account
ON grower_account.id = ${this.tableName}.worker_id
`,
),
)
.where((builder) => this.filterWhereBuilder(filter, builder));

return result[0].count;
}

async getById(id: string) {
const object = await this.session
.getDB()
.select(
this.session.getDB().raw(`
${this.tableName}.id,
${this.tableName}.status,
${this.tableName}.notes,
${this.tableName}.created_at,
${this.tableName}.updated_at,
${this.tableName}.signed_at,
${this.tableName}.closed_at,
${this.tableName}.listed,
row_to_json(agreement.*) AS agreement,
row_to_json(grower_account.*) AS worker,
row_to_json(stakeholder.*) AS stakeholder
FROM ${this.tableName}
LEFT JOIN contracts.agreement AS agreement
ON agreement.id = ${this.tableName}.agreement_id
LEFT JOIN stakeholder.stakeholder AS stakeholder
ON stakeholder.id = agreement.growing_organization_id
LEFT JOIN treetracker.grower_account AS grower_account
ON grower_account.id = ${this.tableName}.worker_id
`),
)
.where(`${this.tableName}.id`, id)
.first();

if (!object) {
throw new HttpError(404, `Can not find ${this.tableName} by id:${id}`);
}
return object;
}
}
6 changes: 5 additions & 1 deletion server/infra/database/GrowerAccountRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ export default class GrowerAccountRepository extends BaseRepository<GrowerAccoun
}

if (filterObject.organization_id) {
result.where(`${this.tableName}.organization_id`, '=',`${filterObject.organization_id}`);
result.where(
`${this.tableName}.organization_id`,
'=',
`${filterObject.organization_id}`,
);
delete filterObject.organization_id;
}

Expand Down
6 changes: 4 additions & 2 deletions server/infra/database/TreeRepository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,14 @@ export default class TreeRepository extends BaseRepository<Tree> {
.first();

if (!object) {
throw new HttpError(404, `Can not find ${this.tableName} by uuid:${uuid}`);
throw new HttpError(
404,
`Can not find ${this.tableName} by uuid:${uuid}`,
);
}
return object;
}


async getByOrganization(
organization_id: number,
options: FilterOptions,
Expand Down
14 changes: 14 additions & 0 deletions server/interfaces/Contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import DbModel from './DbModel';

export default interface Contract extends DbModel {
id: number;
agreement_id: number;
worker_id: number;
status: string;
notes: string;
created_at: string;
updated_at: string;
signed_at: string;
closed_at: string;
listed: true;
}
16 changes: 16 additions & 0 deletions server/interfaces/ContractFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import DbModel from './DbModel';

interface ContractFilter extends DbModel {
id?: string | undefined;
agreement_id?: string | undefined;
worker_id?: string | undefined;
status?: string | undefined;
notes?: string | undefined;
created_at?: string | undefined;
updated_at?: string | undefined;
signed_at?: string | undefined;
closed_at?: string | undefined;
listed?: true | false;
}

export default ContractFilter;
29 changes: 29 additions & 0 deletions server/models/Contract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import ContractRepository from 'infra/database/ContractRepository';
import { delegateRepository } from 'infra/database/delegateRepository';
import Contract from 'interfaces/Contract';
import ContractFilter from 'interfaces/ContractFilter';
import FilterOptions from 'interfaces/FilterOptions';

function getByFilter(
contractRepository: ContractRepository,
): (filter: ContractFilter, options: FilterOptions) => Promise<Contract[]> {
return async function (filter: ContractFilter, options: FilterOptions) {
const contracts = await contractRepository.getByFilter(filter, options);
return contracts;
};
}

function getCount(
contractRepository: ContractRepository,
): (filter: ContractFilter) => Promise<Contract[]> {
return async function (filter: ContractFilter) {
const count = await contractRepository.getCount(filter);
return count;
};
}

export default {
getByFilter,
getCount,
getById: delegateRepository<ContractRepository, Contract>('getById'),
};
2 changes: 1 addition & 1 deletion server/models/Organization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Organization from 'interfaces/Organization';
import { delegateRepository } from '../infra/database/delegateRepository';
import OrganizationRepository from '../infra/database/OrganizationRepository';

type Filter = Partial<{ planter_id: number; organization_id: number}>;
type Filter = Partial<{ planter_id: number; organization_id: number }>;

function getByFilter(
organizationRepository: OrganizationRepository,
Expand Down
16 changes: 11 additions & 5 deletions server/models/OrganizationV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import FilterOptions from 'interfaces/FilterOptions';
import Organization from 'interfaces/Organization';
import { delegateRepository } from '../infra/database/delegateRepository';

type Filter = Partial<{ planter_id: number; organization_id: number, grower_id:string }>;
type Filter = Partial<{
planter_id: number;
organization_id: number;
grower_id: string;
}>;

function getByFilter(
organizationRepository: OrganizationRepositoryV2,
Expand All @@ -18,7 +22,7 @@ function getByFilter(
);
return trees;
}
if (filter.grower_id){
if (filter.grower_id) {
log.warn('using grower filter...');
const trees = await organizationRepository.getByGrower(
filter.grower_id,
Expand All @@ -41,14 +45,16 @@ function getOrganizationLinks(organization) {
}

export default {
getById: delegateRepository<OrganizationRepositoryV2, Organization>('getById'),
getById: delegateRepository<OrganizationRepositoryV2, Organization>(
'getById',
),
getByMapName: delegateRepository<OrganizationRepositoryV2, Organization>(
'getByMapName',
),
getByFilter,
getOrganizationLinks,
getFeaturedOrganizations: delegateRepository<
OrganizationRepositoryV2,
OrganizationRepositoryV2,
Organization
>('getFeaturedOrganizations'),
};
};
6 changes: 4 additions & 2 deletions server/models/TreeV2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ function getFeaturedTreeDepricated(treeRepository: TreeRepositoryV2) {
export default {
getById: delegateRepository<TreeRepositoryV2, Tree>('getById'),
getByFilter,
getFeaturedTree: delegateRepository<TreeRepositoryV2, Tree>('getFeaturedTree'),
getFeaturedTree: delegateRepository<TreeRepositoryV2, Tree>(
'getFeaturedTree',
),
countByFilter,
};
};
Loading

0 comments on commit ccb03c5

Please sign in to comment.