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

Fix animal uses api to act on single resource #3396

Draft
wants to merge 8 commits into
base: integration
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2024 LiteFarm.org
* This file is part of LiteFarm.
*
* LiteFarm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LiteFarm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, see <https://www.gnu.org/licenses/>.
*/
import AnimalTypeUseRelationship from '../models/animalTypeUseRelationshipModel.js';

const animalUseController = {
getAnimalTypeUseRelationships() {
return async (_req, res) => {
try {
const useRelationships = await AnimalTypeUseRelationship.query();
return res.status(200).send(useRelationships);
} catch (error) {
console.error(error);
return res.status(500).json({
error,
});
}
};
},
};

export default animalUseController;
2 changes: 1 addition & 1 deletion packages/api/src/controllers/animalUseController.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const animalUseController = {
getAnimalUses() {
return async (_req, res) => {
try {
const uses = await AnimalUse.getAnimalUsesForTypes();
const uses = await AnimalUse.query();
return res.status(200).send(uses);
} catch (error) {
console.error(error);
Expand Down
43 changes: 43 additions & 0 deletions packages/api/src/models/animalTypeUseRelationshipModel.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2024 LiteFarm.org
* This file is part of LiteFarm.
*
* LiteFarm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LiteFarm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, see <https://www.gnu.org/licenses/>.
*/

import Model from './baseFormatModel.js';

class AnimalTypeUseRelationshipModel extends Model {
static get tableName() {
return 'animal_type_use_relationship';
}

static get idColumn() {
return ['default_type_id', 'animal_use_id'];
}

// Optional JSON schema. This is not the database schema! Nothing is generated
// based on this. This is only used for validation. Whenever a model instance
// is created it is checked against this schema. http://json-schema.org/.
static get jsonSchema() {
return {
type: 'object',
required: ['default_type_id', 'animal_use_id'],
properties: {
default_type_id: { type: 'integer' },
animal_use_id: { type: 'integer' },
},
additionalProperties: false,
};
}
}

export default AnimalTypeUseRelationshipModel;
27 changes: 0 additions & 27 deletions packages/api/src/models/animalUseModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
* GNU General Public License for more details, see <https://www.gnu.org/licenses/>.
*/

import knex from '../util/knex.js';
import baseModel from './baseModel.js';

class AnimalUse extends baseModel {
Expand All @@ -39,32 +38,6 @@ class AnimalUse extends baseModel {
additionalProperties: false,
};
}

static async getAnimalUsesForTypes() {
const typeUseRelationships = await knex('animal_type_use_relationship')
.select({
default_type_id: 'default_type_id',
useId: 'animal_use.id',
useKey: 'animal_use.key',
})
.join('animal_use', 'animal_type_use_relationship.animal_use_id', '=', 'animal_use.id');

const usesPerType = typeUseRelationships.reduce((map, { default_type_id, useId, useKey }) => {
map[default_type_id] = map[default_type_id] || [];
map[default_type_id].push({ id: useId, key: useKey });

return map;
}, {});

const response = Object.entries(usesPerType).map(([defaultTypeId, uses]) => {
return { default_type_id: +defaultTypeId, uses };
});

const allUses = await AnimalUse.query();
response.push({ default_type_id: null, uses: allUses });

return response;
}
}

export default AnimalUse;
28 changes: 28 additions & 0 deletions packages/api/src/routes/animalTypeUseRelationshipRoute.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2024 LiteFarm.org
* This file is part of LiteFarm.
*
* LiteFarm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LiteFarm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, see <https://www.gnu.org/licenses/>.
*/

import express from 'express';

const router = express.Router();
import checkScope from '../middleware/acl/checkScope.js';
import animalTypeUseRelationshipController from '../controllers/animalTypeUseRelationshipController.js';

router.get(
'/',
checkScope(['get:animal_uses']),
animalTypeUseRelationshipController.getAnimalTypeUseRelationships(),
);

export default router;
2 changes: 2 additions & 0 deletions packages/api/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ import animalOriginRoute from './routes/animalOriginRoute.js';
import animalGroupRoute from './routes/animalGroupRoute.js';
import animalRemovalReasonRoute from './routes/animalRemovalReasonRoute.js';
import animalUseRoute from './routes/animalUseRoute.js';
import animalTypeUseRelationshipRoute from './routes/animalTypeUseRelationshipRoute.js';
import cropRoutes from './routes/cropRoute.js';
import cropVarietyRoutes from './routes/cropVarietyRoute.js';
import fieldRoutes from './routes/fieldRoute.js';
Expand Down Expand Up @@ -293,6 +294,7 @@ app
.use('/animal_groups', animalGroupRoute)
.use('/animal_removal_reasons', animalRemovalReasonRoute)
.use('/animal_uses', animalUseRoute)
.use('/animal_type_use_relationships', animalTypeUseRelationshipRoute)
.use('/location', locationRoute)
.use('/userLog', userLogRoute)
.use('/crop', cropRoutes)
Expand Down
141 changes: 141 additions & 0 deletions packages/api/tests/animal_type_use_relationship.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
/*
* Copyright 2024 LiteFarm.org
* This file is part of LiteFarm.
*
* LiteFarm is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* LiteFarm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, see <https://www.gnu.org/licenses/>.
*/

import chai from 'chai';

import chaiHttp from 'chai-http';
chai.use(chaiHttp);

import server from '../src/server.js';
import knex from '../src/util/knex.js';
import { tableCleanup } from './testEnvironment.js';

jest.mock('jsdom');
jest.mock('../src/middleware/acl/checkJwt.js', () =>
jest.fn((req, _res, next) => {
req.auth = {};
req.auth.user_id = req.get('user_id');
next();
}),
);
import mocks from './mock.factories.js';

describe('Animal Type Use Relationship Tests', () => {
let farm;
let newOwner;

const getRequest = async ({ user_id = newOwner.user_id, farm_id = farm.farm_id }) => {
const response = await chai
.request(server)
.get('/animal_type_use_relationships')
.set('user_id', user_id)
.set('farm_id', farm_id);

return response;
};

function fakeUserFarm(role = 1) {
return { ...mocks.fakeUserFarm(), role_id: role };
}

async function returnUserFarms(role) {
const [mainFarm] = await mocks.farmFactory();
const [user] = await mocks.usersFactory();

await mocks.userFarmFactory(
{
promisedUser: [user],
promisedFarm: [mainFarm],
},
fakeUserFarm(role),
);
return { mainFarm, user };
}

async function makeAnimalUse(properties) {
const [animalUse] = await mocks.animal_useFactory({
properties,
});
return animalUse;
}

async function makeAnimalTypeUseRelationship(defaultType, use) {
const [animalTypeUseRelationship] = await mocks.animal_type_use_relationshipFactory({
promisedDefaultAnimalType: [defaultType],
promisedAnimalUse: [use],
});
return animalTypeUseRelationship;
}

beforeEach(async () => {
[farm] = await mocks.farmFactory();
[newOwner] = await mocks.usersFactory();
});

afterEach(async (done) => {
await tableCleanup(knex);
done();
});

afterAll(async (done) => {
await knex.destroy();
done();
});

describe('Get animal type use relationship tests', () => {
test('All users should get animal type use relationships', async () => {
const roles = [1, 2, 3, 5];
const [use1, use2, use3, use4, use5, use6, use7, use8] = await Promise.all(
[1, 2, 3, 4, 5, 6, 7, 8].map(async () => await makeAnimalUse()),
);

const [defaultType1] = await mocks.default_animal_typeFactory();
const [defaultType2] = await mocks.default_animal_typeFactory();

const testCase = [
{ defaultType: defaultType1, uses: [use1, use2, use3, use6, use7, use8] },
{ defaultType: defaultType2, uses: [use1, use5, use6, use7, use8] },
];

for (let { defaultType, uses } of testCase) {
if (defaultType) {
for (let use of uses) {
await makeAnimalTypeUseRelationship(defaultType, use);
}
}
}

for (const role of roles) {
const { mainFarm, user } = await returnUserFarms(role);

const res = await getRequest({
user_id: user.user_id,
farm_id: mainFarm.farm_id,
});

expect(res.status).toBe(200);
expect(res.body.length).toBe(11);
const filteredDefaultType1 = res.body.filter(
(useRelationship) => useRelationship.default_type_id === defaultType1.id,
);
expect(filteredDefaultType1.length).toBe(6);
const filteredDefaultType2 = res.body.filter(
(useRelationship) => useRelationship.default_type_id === defaultType2.id,
);
expect(filteredDefaultType2.length).toBe(5);
}
});
});
});
37 changes: 1 addition & 36 deletions packages/api/tests/animal_use.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,6 @@ describe('Animal Use Tests', () => {
return animalUse;
}

async function makeAnimalTypeUseRelationship(defaultType, use) {
const [animalTypeUseRelationship] = await mocks.animal_type_use_relationshipFactory({
promisedDefaultAnimalType: [defaultType],
promisedAnimalUse: [use],
});
return animalTypeUseRelationship;
}

beforeEach(async () => {
[farm] = await mocks.farmFactory();
[newOwner] = await mocks.usersFactory();
Expand All @@ -101,23 +93,6 @@ describe('Animal Use Tests', () => {
[1, 2, 3, 4, 5, 6, 7, 8].map(async () => await makeAnimalUse()),
);

const [defaultType1] = await mocks.default_animal_typeFactory();
const [defaultType2] = await mocks.default_animal_typeFactory();

const testCase = [
{ defaultType: defaultType1, uses: [use1, use2, use3, use6, use7, use8] },
{ defaultType: defaultType2, uses: [use1, use5, use6, use7, use8] },
{ defaultType: null, uses: [use1, use2, use3, use4, use5, use6, use7, use8] }, // for custom types
];

for (let { defaultType, uses } of testCase) {
if (defaultType) {
for (let use of uses) {
await makeAnimalTypeUseRelationship(defaultType, use);
}
}
}

for (const role of roles) {
const { mainFarm, user } = await returnUserFarms(role);

Expand All @@ -127,17 +102,7 @@ describe('Animal Use Tests', () => {
});

expect(res.status).toBe(200);
expect(res.body.length).toBe(3);

res.body.forEach(({ default_type_id, uses }) => {
const expectedTypeAndUses = testCase.find(({ defaultType }) => {
return default_type_id ? defaultType.id === default_type_id : !defaultType;
});

expect(uses.map(({ id }) => id).sort()).toEqual(
expectedTypeAndUses.uses.map(({ id }) => id).sort(),
);
});
expect(res.body.length).toBe(8);
}
});
});
Expand Down
2 changes: 2 additions & 0 deletions packages/webapp/src/apiConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export const animalIdentifierTypesUrl = `${URI}/animal_identifier_types`;
export const animalIdentifierColorsUrl = `${URI}/animal_identifier_colors`;
export const animalOriginsUrl = `${URI}/animal_origins`;
export const animalUsesUrl = `${URI}/animal_uses`;
export const animalTypeUseRelationshipsUrl = `${URI}/animal_type_use_relationships`;
export const animalRemovalReasonsUrl = `${URI}/animal_removal_reasons`;
export const soilAmendmentMethodsUrl = `${URI}/soil_amendment_methods`;
export const soilAmendmentPurposesUrl = `${URI}/soil_amendment_purposes`;
Expand Down Expand Up @@ -147,6 +148,7 @@ export default {
animalIdentifierColorsUrl,
animalOriginsUrl,
animalUsesUrl,
animalTypeUseRelationshipsUrl,
animalRemovalReasonsUrl,
soilAmendmentMethodsUrl,
soilAmendmentPurposesUrl,
Expand Down
Loading