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

1351 Preview #1359

Merged
merged 32 commits into from
Jun 24, 2021
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
266f2b4
updates pull request template
seanmalbert May 20, 2021
54189a9
Merge remote-tracking branch 'remotes/upstream/master'
seanmalbert May 20, 2021
499db89
Merge remote-tracking branch 'remotes/upstream/master'
seanmalbert May 24, 2021
925e4d3
Merge remote-tracking branch 'remotes/upstream/master'
seanmalbert May 26, 2021
c890e67
removes BACKEND_API_BASE from netlify.toml
seanmalbert May 26, 2021
935a1ad
Merge remote-tracking branch 'remotes/upstream/master'
seanmalbert May 27, 2021
cf599f4
Merge remote-tracking branch 'remotes/upstream/master'
seanmalbert May 28, 2021
d296da6
updates for partners translation locale overrides
seanmalbert May 28, 2021
86e3db0
Merge remote-tracking branch 'remotes/upstream/master'
seanmalbert Jun 8, 2021
19f667d
resolve issue with out of sync lock and package files
seanmalbert Jun 8, 2021
db718d2
removes package-lock
seanmalbert Jun 8, 2021
13c3a81
updates Layout path
seanmalbert Jun 8, 2021
3b73e6a
Merge remote-tracking branch 'remotes/upstream/master'
seanmalbert Jun 9, 2021
8f6173f
removes gtm key for production, since it should be coming from netlif…
seanmalbert Jun 9, 2021
5963597
Merge remote-tracking branch 'remotes/upstream/master'
seanmalbert Jun 9, 2021
70bcb2f
Merge remote-tracking branch 'remotes/upstream/master' into 1351-preview
seanmalbert Jun 10, 2021
d7ed076
Merge remote-tracking branch 'remotes/upstream/master' into 1351-preview
seanmalbert Jun 13, 2021
b6f9a4f
adds simple filter capability to listings and implements
seanmalbert Jun 14, 2021
8e00194
regenerated swagger
seanmalbert Jun 14, 2021
8978f6f
changelog entry
seanmalbert Jun 14, 2021
77bfa51
Brings dev up-to-date with master (#1360)
seanmalbert Jun 14, 2021
a1eed1e
Merge remote-tracking branch 'remotes/upstream/dev' into 1351-preview
seanmalbert Jun 14, 2021
f366c82
update dev branch with master (#1373)
emilyjablonski Jun 15, 2021
68ce8ee
Merge remote-tracking branch 'remotes/upstream/master' into 1351-preview
seanmalbert Jun 16, 2021
dfa96e5
addresses issues from review
seanmalbert Jun 16, 2021
1ade8f7
Merge remote-tracking branch 'remotes/upstream/dev' into 1351-preview
seanmalbert Jun 16, 2021
7b507f3
updates filter implementation
seanmalbert Jun 21, 2021
2751d2c
completely changed filtering for listings
seanmalbert Jun 22, 2021
ace7f3d
=merge upstream
seanmalbert Jun 22, 2021
1adf530
updates to handle multiple values for the same key
seanmalbert Jun 22, 2021
248e1e5
=merge latest upstream dev
seanmalbert Jun 23, 2021
831ceba
aside cleanup and log removal
seanmalbert Jun 24, 2021
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
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ All notable changes to this project will be documented in this file. The format
### Frontend

- Added:

- Adds filtering capability to listings list and implements on public site ([#1351](https://github.com/bloom-housing/bloom/pull/1359))
- Listings Management pieces added to Parnter's app, including add and detail pages
- add accessible at `/listings/add`
- detail page accessible at `/listings/[id]`
Expand Down
2 changes: 1 addition & 1 deletion backend/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
"@nestjs/jwt": "^7.1.0",
"@nestjs/passport": "^7.1.0",
"@nestjs/platform-express": "^7.4.4",
"@nestjs/swagger": "4.6.1",
"@nestjs/swagger": "4.7.3",
"@nestjs/throttler": "^1.1.2",
"@nestjs/typeorm": "^7.1.0",
"@types/cache-manager": "^3.4.0",
Expand Down
20 changes: 20 additions & 0 deletions backend/core/src/listings/dto/listing.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { AddressCreateDto, AddressDto, AddressUpdateDto } from "../../shared/dto
import { ValidationsGroupsEnum } from "../../shared/types/validations-groups-enum"
import { UserBasicDto } from "../../user/dto/user.dto"
import { ListingStatus } from "../types/listing-status-enum"
import { BaseFilter } from "../../shared/dto/filter.dto"
import { UnitCreateDto, UnitDto, UnitUpdateDto } from "../../units/dto/unit.dto"
import { transformUnits } from "../../shared/units-transformations"
import { JurisdictionDto } from "../../jurisdictions/dto/jurisdiction.dto"
Expand Down Expand Up @@ -594,3 +595,22 @@ export class ListingUpdateDto extends OmitType(ListingDto, [
@Type(() => IdDto)
reservedCommunityType?: IdDto
}

// add other listing filter params here
export class ListingFilterParams extends BaseFilter {
@Expose()
@ApiProperty({
type: String,
example: "Coliseum",
required: false,
})
name?: string

@Expose()
@ApiProperty({
enum: Object.keys(ListingStatus),
example: "active",
required: false,
})
status?: ListingStatus
}
43 changes: 39 additions & 4 deletions backend/core/src/listings/listings.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,28 @@ import {
Post,
Put,
Query,
Req,
UseGuards,
UseInterceptors,
UsePipes,
ValidationPipe,
} from "@nestjs/common"
import { Request } from "express"
import { ListingsService } from "./listings.service"
import { ApiBearerAuth, ApiOperation, ApiTags } from "@nestjs/swagger"
import { ListingCreateDto, ListingDto, ListingUpdateDto } from "./dto/listing.dto"
import {
ApiBearerAuth,
ApiExtraModels,
ApiOperation,
ApiQuery,
ApiTags,
getSchemaPath,
} from "@nestjs/swagger"
import {
ListingCreateDto,
ListingDto,
ListingUpdateDto,
ListingFilterParams,
} from "./dto/listing.dto"
import { ResourceType } from "../auth/decorators/resource-type.decorator"
import { OptionalAuthGuard } from "../auth/guards/optional-auth.guard"
import { AuthzGuard } from "../auth/guards/authz.guard"
Expand All @@ -39,9 +53,30 @@ export class ListingsController {
required: false,
type: String,
})
@ApiExtraModels(ListingFilterParams)
@ApiQuery({
name: "filter",
required: false,
type: [String],
schema: {
type: "array",
example: [
{ $comparison: "=", status: "active" },
{ $comparison: "<>", name: "Coliseum" },
],
items: {
$ref: getSchemaPath(ListingFilterParams),
},
},
})
@UseInterceptors(CacheInterceptor)
public async getAll(@Query("jsonpath") jsonpath?: string): Promise<ListingDto[]> {
return mapTo(ListingDto, await this.listingsService.list(jsonpath))
public async getAll(
@Req() request: Request,
@Query("jsonpath") jsonpath?: string,
@Query("filter") filter?: ListingFilterParams[]
// TODO: Add options param here for paging and sorting
): Promise<ListingDto[]> {
return mapTo(ListingDto, await this.listingsService.list(jsonpath, filter))
}

@Post()
Expand Down
25 changes: 16 additions & 9 deletions backend/core/src/listings/listings.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ import { Injectable, NotFoundException } from "@nestjs/common"
import jp from "jsonpath"

import { Listing } from "./entities/listing.entity"
import { ListingCreateDto, ListingUpdateDto } from "./dto/listing.dto"
import { ListingCreateDto, ListingUpdateDto, ListingFilterParams } from "./dto/listing.dto"
import { InjectRepository } from "@nestjs/typeorm"
import { Repository } from "typeorm"
import { addFilter } from "../shared/filter"
import { plainToClass } from "class-transformer"
import { PropertyCreateDto, PropertyUpdateDto } from "../property/dto/property.dto"
import { Property } from "../property/entities/property.entity"
Expand All @@ -28,14 +29,20 @@ export class ListingsService {
.leftJoinAndSelect("listings.reservedCommunityType", "reservedCommunityType")
}

public async list(jsonpath?: string): Promise<Listing[]> {
let listings = await this.getQueryBuilder()
.orderBy({
"listings.id": "DESC",
"units.max_occupancy": "ASC",
"preferences.ordinal": "ASC",
})
.getMany()
public async list(jsonpath?: string, filter?: ListingFilterParams[]): Promise<Listing[]> {
const qb = this.getQueryBuilder()
console.log("***** filter ****** ", filter)
if (filter) {
addFilter<ListingFilterParams>(filter, "listings", qb)
}

qb.orderBy({
"listings.id": "DESC",
"units.max_occupancy": "ASC",
"preferences.ordinal": "ASC",
})

let listings = await qb.getMany()

if (jsonpath) {
listings = jp.query(listings, jsonpath)
Expand Down
2 changes: 1 addition & 1 deletion backend/core/src/shared/default-validation-pipe-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ export const defaultValidationPipeOptions: ValidationPipeOptions = {
enableImplicitConversion: false,
},
groups: [ValidationsGroupsEnum.default],
forbidUnknownValues: true,
forbidUnknownValues: false,
}
18 changes: 18 additions & 0 deletions backend/core/src/shared/dto/filter.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ApiProperty } from "@nestjs/swagger"
import { Expose } from "class-transformer"

// Add other comparisons as needed (>, <, etc)
export enum Compare {
"=" = "=",
"<>" = "<>",
}

export class BaseFilter {
@Expose()
@ApiProperty({
enum: Object.keys(Compare),
example: "=",
default: Compare["="],
})
$comparison: Compare
}
49 changes: 49 additions & 0 deletions backend/core/src/shared/filter/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { WhereExpression } from "typeorm"

/**
*
* @param filter
* @param schema
* @param qb
*/
/**
* This is super simple right now, but we can expand to include complex filter with ands, ors, more than one schema, etc
*/
export function addFilter<Filter>(filter: Filter[], schema: string, qb: WhereExpression): void {
const operator = "andWhere"
/**
* By specifying that the filter is an array, it keeps the keys in order, so we can iterate like below
*/
let comparisons: unknown[],
comparisonCount = 0

// eslint-disable-next-line @typescript-eslint/no-for-in-array
for (const key in filter) {
const value = filter[key]
if (key === "$comparison") {
if (Array.isArray(value)) {
comparisons = value
} else {
comparisons = [value]
}
} else {
if (value !== undefined) {
let values
// handle multiple values for the same key
if (Array.isArray(value)) {
values = value
} else {
values = [value]
}

values.forEach((val: unknown) => {
// eslint-disable-next-line @typescript-eslint/restrict-template-expressions
qb[operator](`${schema}.${key} ${comparisons[comparisonCount]} :${key}`, {
[key]: val,
})
comparisonCount++
})
}
}
}
}
25 changes: 23 additions & 2 deletions backend/core/types/src/backend-swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,14 +658,16 @@ export class ListingsService {
params: {
/** */
jsonpath?: string;
/** */
filter?: ListingFilterParams[];
} = {} as any,
options: IRequestOptions = {}
): Promise<Listing[]> {
return new Promise((resolve, reject) => {
let url = basePath + '/listings';

const configs: IRequestConfig = getConfigs('get', 'application/json', url, options);
configs.params = { jsonpath: params['jsonpath'] };
configs.params = { jsonpath: params['jsonpath'], filter: params['filter'] };
let data = null;

configs.data = data;
Expand Down Expand Up @@ -2729,6 +2731,17 @@ export interface JurisdictionUpdate {
name: string;
}

export interface ListingFilterParams {
/** */
$comparison: EnumListingFilterParamsComparison;

/** */
name?: string;

/** */
status?: EnumListingFilterParamsStatus;
}

export interface MinMaxCurrency {
/** */
min: string;
Expand Down Expand Up @@ -4244,7 +4257,15 @@ export enum InputType {
'address' = 'address',
'hhMemberSelect' = 'hhMemberSelect'
}

export enum EnumListingFilterParamsComparison {
'=' = '=',
'<>' = '<>'
}
export enum EnumListingFilterParamsStatus {
'active' = 'active',
'pending' = 'pending',
'closed' = 'closed'
}
export enum ListingStatus {
'active' = 'active',
'pending' = 'pending',
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"build:app:partners": "cd sites/partners && yarn build",
"dev:backend": "cd backend/core && yarn dev",
"dev:all": "concurrently --names \" BACKEND_CORE,APP_PUBLIC,APP_PARTNERS\" --prefix \"{name}\" \"yarn dev:backend\" \"yarn dev:app:public\" \"yarn dev:app:partners\"",
"dev:frontend": "concurrently --names \" APP_PUBLIC,APP_PARTNERS\" --prefix \"{name}\" \"yarn dev:app:public\" \"yarn dev:app:partners\"",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is already defined in the monorepo in the top level package.json

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But this is the top level package.json.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, my bad. :)

"dev:partners": "concurrently \"yarn dev:backend\" \"yarn dev:app:partners\"",
"dev:public": "concurrently \"yarn dev:backend\" \"yarn dev:app:public\"",
"test:shared:ui": "cd ui-components && yarn && yarn test",
Expand Down
3 changes: 2 additions & 1 deletion sites/partners/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
BACKEND_API_BASE=http://localhost:3100
LISTINGS_QUERY=/listings
NEXTJS_PORT=3001
SHOW_DUPLICATES=FALSE
SHOW_DUPLICATES=FALSE
PUBLIC_BASE_URL=http://localhost:3000
1 change: 1 addition & 0 deletions sites/partners/next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ module.exports = withCSS(
listingServiceUrl: BACKEND_API_BASE + LISTINGS_QUERY,
idleTimeout: process.env.IDLE_TIMEOUT,
showDuplicates: process.env.SHOW_DUPLICATES === "TRUE",
publicBaseUrl: process.env.PUBLIC_BASE_URL,
},
i18n: {
locales: process.env.LANGUAGES ? process.env.LANGUAGES.split(",") : ["en"],
Expand Down
26 changes: 15 additions & 11 deletions sites/partners/src/listings/Aside.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ type AsideType = "add" | "edit" | "details"
const Aside = ({ type, setStatusAndSubmit }: AsideProps) => {
const listing = useContext(ListingContext)

const lisitngId = listing?.id
const listingId = listing?.id

const recordUpdated = useMemo(() => {
if (!listing) return null
Expand All @@ -47,7 +47,7 @@ const Aside = ({ type, setStatusAndSubmit }: AsideProps) => {
if (type === "details") {
elements.push(
<GridCell key="btn-submitNew">
<LocalizedLink href={`/listings/${lisitngId}/edit`}>
<LocalizedLink href={`/listings/${listingId}/edit`}>
<Button styleType={AppearanceStyleType.secondary} fullWidth onClick={() => false}>
{t("t.edit")}
</Button>
Expand Down Expand Up @@ -79,17 +79,21 @@ const Aside = ({ type, setStatusAndSubmit }: AsideProps) => {
)
}

elements.push(
<GridCell key="btn-preview">
<Button styleType={AppearanceStyleType.secondary} fullWidth onClick={() => false}>
{t("listings.actions.preview")}
</Button>
</GridCell>,
cancel
)
if (type === "details") {
elements.push(
<GridCell key="btn-preview">
<a target="_blank" href={`${process.env.publicBaseUrl}/preview/listings/${listingId}`}>
<Button styleType={AppearanceStyleType.secondary} fullWidth onClick={() => false}>
{t("listings.actions.preview")}
</Button>
</a>
</GridCell>,
cancel
)
}

return elements
}, [lisitngId, setStatusAndSubmit, type])
}, [listingId, setStatusAndSubmit, type])

return (
<>
Expand Down
5 changes: 4 additions & 1 deletion sites/public/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ export default function Home(props: IndexProps) {
export async function getStaticProps() {
let listings = []
try {
const response = await axios.get(process.env.listingServiceUrl)
// const response = await axios.get(process.env.listingServiceUrl)
const response = await axios.get(
process.env.listingServiceUrl + "?filter[$comparison]=<>&filter[status]=pending"
)
listings = response.data
} catch (error) {
console.error(error)
Expand Down
8 changes: 6 additions & 2 deletions sites/public/pages/listing/[id]/[slug].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ export async function getStaticPaths(context: { locales: Array<string> }) {
let response

try {
response = await axios.get(process.env.listingServiceUrl)
response = await axios.get(
process.env.listingServiceUrl + "?filter[$comparison]=<>&filter[status]=pending"
)
} catch (e) {
return {
paths: [],
Expand All @@ -55,7 +57,9 @@ export async function getStaticPaths(context: { locales: Array<string> }) {
}

export async function getStaticProps(context: { params: Record<string, string> }) {
const response = await axios.get(`${process.env.backendApiBase}/listings/${context.params.id}`)
const response = await axios.get(
`${process.env.backendApiBase}/listings/${context.params.id}?filter[$comparison]=<>&filter[status]=pending`
)

return {
props: {
Expand Down
Loading