Skip to content
Open
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
21 changes: 7 additions & 14 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

166 changes: 166 additions & 0 deletions src/controllers/growerNote.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import { inject } from '@loopback/core';
import { repository } from '@loopback/repository';
import {
post,
param,
get,
patch,
del,
requestBody,
HttpErrors,
RestBindings,
Response,
} from '@loopback/rest';
import { GrowerNote } from '../models';
import { GrowerNoteRepository, PlanterRepository } from '../repositories';

export class GrowerNoteController {
constructor(
@repository(GrowerNoteRepository)
public growerNoteRepository: GrowerNoteRepository,
@repository(PlanterRepository)
public planterRepository: PlanterRepository,
) {}

@get('/planter/{growerId}/notes', {
responses: {
'200': {
description: 'Array of GrowerNote instances',
content: {
'application/json': {
schema: { type: 'array', items: { 'x-ts-type': GrowerNote } },
},
},
},
'404': { description: 'Planter not found' },
},
})
async find(
@param.path.number('growerId') growerId: number,
): Promise<GrowerNote[]> {
await this.planterRepository.findById(growerId).catch(() => {
throw new HttpErrors.NotFound(`Planter ${growerId} not found`);
});
return this.growerNoteRepository.find({ where: { planterId: growerId } });
}

@get('/organization/{organizationId}/planter/{growerId}/notes', {
responses: {
'200': {
description: 'Array of GrowerNote instances for org-scoped planter',
content: {
'application/json': {
schema: { type: 'array', items: { 'x-ts-type': GrowerNote } },
},
},
},
'404': { description: 'Planter not found' },
},
})
async findByOrg(
@param.path.number('organizationId') _organizationId: number,
@param.path.number('growerId') growerId: number,
): Promise<GrowerNote[]> {
await this.planterRepository.findById(growerId).catch(() => {
throw new HttpErrors.NotFound(`Planter ${growerId} not found`);
});
return this.growerNoteRepository.find({ where: { planterId: growerId } });
}

@post('/planter/{growerId}/notes', {
responses: {
'201': {
description: 'GrowerNote created successfully',
content: {
'application/json': { schema: { 'x-ts-type': GrowerNote } },
},
},
'404': { description: 'Planter not found' },
},
})
async create(
@param.path.number('growerId') growerId: number,
@requestBody() note: GrowerNote,
@inject(RestBindings.Http.RESPONSE) res: Response,
): Promise<GrowerNote> {
await this.planterRepository.findById(growerId).catch(() => {
throw new HttpErrors.NotFound(`Planter ${growerId} not found`);
});
note.planterId = growerId;
note.createdAt = new Date().toISOString();
note.updatedAt = new Date().toISOString();
const created = await this.growerNoteRepository.create(note);
res.status(201);
return created;
}

@patch('/planter/{growerId}/notes/{noteId}', {
responses: {
'200': {
description: 'GrowerNote updated successfully',
content: {
'application/json': { schema: { 'x-ts-type': GrowerNote } },
},
},
'403': { description: 'Note does not belong to this planter' },
'404': { description: 'GrowerNote not found' },
},
})
async updateById(
@param.path.number('growerId') growerId: number,
@param.path.number('noteId') noteId: number,
@requestBody() note: Partial<GrowerNote>,
@inject(RestBindings.Http.RESPONSE) res: Response,
): Promise<GrowerNote> {
const existing = await this.growerNoteRepository
.findById(noteId)
.catch(() => {
throw new HttpErrors.NotFound(`GrowerNote ${noteId} not found`);
});
if (existing.planterId !== growerId) {
throw new HttpErrors.Forbidden(
`Note ${noteId} does not belong to planter ${growerId}`,
);
}
note.updatedAt = new Date().toISOString();
await this.growerNoteRepository.updateById(noteId, note);
const updated = await this.growerNoteRepository.findById(noteId);
res.status(200);
return updated;
}

@del('/planter/{growerId}/notes/{noteId}', {
responses: {
'200': {
description: 'GrowerNote deleted successfully',
content: {
'application/json': {
schema: {
type: 'object',
properties: { message: { type: 'string' } },
},
},
},
},
'403': { description: 'Note does not belong to this planter' },
'404': { description: 'GrowerNote not found' },
},
})
async deleteById(
@param.path.number('growerId') growerId: number,
@param.path.number('noteId') noteId: number,
): Promise<{ message: string }> {
const existing = await this.growerNoteRepository
.findById(noteId)
.catch(() => {
throw new HttpErrors.NotFound(`GrowerNote ${noteId} not found`);
});
if (existing.planterId !== growerId) {
throw new HttpErrors.Forbidden(
`Note ${noteId} does not belong to planter ${growerId}`,
);
}
await this.growerNoteRepository.deleteById(noteId);
return { message: `Note ${noteId} deleted successfully` };
}
}
1 change: 1 addition & 0 deletions src/controllers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ export * from './tag.controller';
export * from './treeTag.controller';
export * from './treesTreeTag.controller';
export * from './organization.controller';
export * from './growerNote.controller';
10 changes: 9 additions & 1 deletion src/datasources/config.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import pg from 'pg';
pg.defaults.ssl = { rejectUnauthorized: false };
if (
process.env.DATABASE_URL &&
(process.env.DATABASE_URL.includes('ssl=false') ||
process.env.DATABASE_URL.includes('sslmode=disable'))
) {
pg.defaults.ssl = false;
} else {
pg.defaults.ssl = { rejectUnauthorized: false };
}
export interface DatasourceConfig {
name: string;
connector: string;
Expand Down
2 changes: 1 addition & 1 deletion src/models/adminUser.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export class AdminUser extends Entity {
postgresql: {
columnName: 'email',
dataType: 'character varying',
dataLength: null,
dataLength: 255,
dataPrecision: null,
dataScale: null,
nullable: 'YES',
Expand Down
124 changes: 124 additions & 0 deletions src/models/growerNote.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import { Entity, model, property } from '@loopback/repository';

/* eslint-disable @typescript-eslint/no-empty-interface */

@model({
settings: {
idInjection: false,
postgresql: { schema: 'public', table: 'grower_note' },
},
})
export class GrowerNote extends Entity {
@property({
type: Number,
required: false,
scale: 0,
id: 1,
postgresql: {
columnName: 'id',
dataType: 'serial',
dataLength: null,
dataPrecision: null,
dataScale: 0,
nullable: 'NO',
},
})
id: Number;

@property({
type: Number,
required: true,
scale: 0,
postgresql: {
columnName: 'planter_id',
dataType: 'integer',
dataLength: null,
dataPrecision: null,
dataScale: 0,
nullable: 'NO',
},
})
planterId: Number;

@property({
type: String,
required: true,
postgresql: {
columnName: 'content',
dataType: 'text',
dataLength: null,
dataPrecision: null,
dataScale: null,
nullable: 'NO',
},
})
content: String;

@property({
type: Number,
required: true,
scale: 0,
postgresql: {
columnName: 'author_id',
dataType: 'integer',
dataLength: null,
dataPrecision: null,
dataScale: 0,
nullable: 'NO',
},
})
authorId: Number;

@property({
type: String,
required: true,
postgresql: {
columnName: 'author_name',
dataType: 'character varying',
dataLength: 255,
dataPrecision: null,
dataScale: null,
nullable: 'NO',
},
})
authorName: String;

@property({
type: String,
required: false,
postgresql: {
columnName: 'created_at',
dataType: 'timestamp without time zone',
dataLength: null,
dataPrecision: 6,
dataScale: null,
nullable: 'YES',
},
})
createdAt?: String;

@property({
type: String,
required: false,
postgresql: {
columnName: 'updated_at',
dataType: 'timestamp without time zone',
dataLength: null,
dataPrecision: 6,
dataScale: null,
nullable: 'YES',
},
})
updatedAt?: String;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
[prop: string]: any;

constructor(data?: Partial<GrowerNote>) {
super(data);
}
}

export interface GrowerNoteRelations {}

export type GrowerNoteWithRelations = GrowerNote & GrowerNoteRelations;
1 change: 1 addition & 0 deletions src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './treeTag.model';
export * from './planterRegistration.model';
export * from './organization.model';
export * from './domainEvent.model';
export * from './growerNote.model';
Loading