Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
cc703cc
eagle eye content v2 start
epipav Feb 1, 2023
12b766f
schema updates
epipav Feb 2, 2023
79266bd
schema updates, actions repo start
epipav Feb 2, 2023
47e75a8
repo layer progress
epipav Feb 2, 2023
f786661
filtering with tests, endpoint routing remaining
epipav Feb 5, 2023
718311b
comments
epipav Feb 5, 2023
7cf414b
eagleEye upsert routing
epipav Feb 5, 2023
13079a1
transactions for upsert
epipav Feb 5, 2023
ee65fc2
error translations
epipav Feb 5, 2023
e8946ab
formatting, all routing finalized
epipav Feb 5, 2023
87a7a41
removed un-used get posts by keywords file
epipav Feb 5, 2023
9452bbf
getPostsByKeyword re-added bcs it's being used by hackernews
epipav Feb 5, 2023
cb967c0
Added endpoint for EagleEye settings
Feb 7, 2023
409efc1
Added comments
Feb 7, 2023
fb66be5
Fixed frontend sync
Feb 7, 2023
061fd79
eagle eye content v2 start
epipav Feb 1, 2023
ffc51d3
schema updates
epipav Feb 2, 2023
6aa581b
schema updates, actions repo start
epipav Feb 2, 2023
94f4ddd
repo layer progress
epipav Feb 2, 2023
bbe8c20
filtering with tests, endpoint routing remaining
epipav Feb 5, 2023
5462fd8
comments
epipav Feb 5, 2023
661fb7a
eagleEye upsert routing
epipav Feb 5, 2023
ff07809
transactions for upsert
epipav Feb 5, 2023
792932a
error translations
epipav Feb 5, 2023
5260efc
formatting, all routing finalized
epipav Feb 5, 2023
127c10b
removed un-used get posts by keywords file
epipav Feb 5, 2023
7361d59
getPostsByKeyword re-added bcs it's being used by hackernews
epipav Feb 5, 2023
674d3af
Added endpoint for EagleEye settings
Feb 7, 2023
3cb682c
Added comments
Feb 7, 2023
729828f
Fixed frontend sync
Feb 7, 2023
3e05210
Added EagleEye search
Feb 7, 2023
477208b
d
Feb 7, 2023
b3d4eb0
Added fixes and missing endpoints
Feb 7, 2023
bb6582d
Added proper search
Feb 7, 2023
1485611
Is feature flagging done?
Feb 8, 2023
be317b2
Protected all EagleEye endpoints
Feb 8, 2023
9a94a14
Re-made hackernews integration (#494)
Feb 8, 2023
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
6 changes: 5 additions & 1 deletion backend/.env.dist.local
Original file line number Diff line number Diff line change
Expand Up @@ -133,4 +133,8 @@ CROWD_QDRANT_PORT=6333

# Enrichment settings
CROWD_ENRICHMENT_URL=
CROWD_ENRICHMENT_API_KEY=
CROWD_ENRICHMENT_API_KEY=

# EagleEye settings
CROWD_EAGLE_EYE_URL=
CROWD_EAGLE_EYE_API_KEY=
4 changes: 4 additions & 0 deletions backend/config/custom-environment-variables.json
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,9 @@
"enrichment": {
"url": "CROWD_ENRICHMENT_URL",
"apiKey": "CROWD_ENRICHMENT_API_KEY"
},
"eagleEye": {
"url": "CROWD_EAGLE_EYE_URL",
"apiKey": "CROWD_EAGLE_EYE_API_KEY"
}
}
3 changes: 2 additions & 1 deletion backend/config/default.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,6 @@
"maxRetrospectInSeconds": 3600
},
"github": {},
"enrichment": {}
"enrichment": {},
"eagleEye": {}
}
11 changes: 11 additions & 0 deletions backend/src/api/eagleEyeContent/eagleEyeActionCreate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Permissions from '../../security/permissions'
import EagleEyeActionService from '../../services/eagleEyeActionService'
import PermissionChecker from '../../services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.eagleEyeActionCreate)

const payload = await new EagleEyeActionService(req).create(req.body, req.params.contentId)

await req.responseHandler.success(req, res, payload)
}
11 changes: 11 additions & 0 deletions backend/src/api/eagleEyeContent/eagleEyeActionDestroy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Permissions from '../../security/permissions'
import EagleEyeActionService from '../../services/eagleEyeActionService'
import PermissionChecker from '../../services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.eagleEyeActionDestroy)

const payload = await new EagleEyeActionService(req).destroy(req.params.actionId)

await req.responseHandler.success(req, res, payload)
}
17 changes: 0 additions & 17 deletions backend/src/api/eagleEyeContent/eagleEyeContentList.ts

This file was deleted.

8 changes: 3 additions & 5 deletions backend/src/api/eagleEyeContent/eagleEyeContentSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,9 @@ import EagleEyeContentService from '../../services/eagleEyeContentService'
import PermissionChecker from '../../services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.eagleEyeContentSearch)

const payload = await new EagleEyeContentService(req).search(req.body)

track('EagleEyeSearch', { ...req.body }, { ...req })
new PermissionChecker(req).validateHas(Permissions.values.eagleEyeActionCreate)

const payload = await new EagleEyeContentService(req).search()
track('EagleEye backend search', { ...req.body }, { ...req })
await req.responseHandler.success(req, res, payload)
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import EagleEyeContentService from '../../services/eagleEyeContentService'
import PermissionChecker from '../../services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.eagleEyeContentEdit)
new PermissionChecker(req).validateHas(Permissions.values.eagleEyeContentCreate)

const payload = await new EagleEyeContentService(req).update(req.params.id, req.body)
const payload = await new EagleEyeContentService(req).upsert(req.body)

await req.responseHandler.success(req, res, payload)
}
11 changes: 11 additions & 0 deletions backend/src/api/eagleEyeContent/eagleEyeSettingsUpdate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Permissions from '../../security/permissions'
import EagleEyeSettingsService from '../../services/eagleEyeSettingsService'
import PermissionChecker from '../../services/user/permissionChecker'

export default async (req, res) => {
new PermissionChecker(req).validateHas(Permissions.values.eagleEyeActionCreate)

const payload = await new EagleEyeSettingsService(req).update(req.body)

await req.responseHandler.success(req, res, payload)
}
42 changes: 34 additions & 8 deletions backend/src/api/eagleEyeContent/index.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,47 @@
import { safeWrap } from '../../middlewares/errorMiddleware'
import { featureFlagMiddleware } from '../../middlewares/featureFlagMiddleware'
import { FeatureFlag } from '../../types/common'

export default (app) => {
app.post(
`/tenant/:tenantId/eagleEyeContent`,
safeWrap(require('./eagleEyeContentSearch').default),
)
app.post(
`/tenant/:tenantId/eagleEyeContent/query`,
featureFlagMiddleware(FeatureFlag.EAGLE_EYE, 'entities.eagleEye.errors.planLimitExceeded'),
safeWrap(require('./eagleEyeContentQuery').default),
)
app.put(
`/tenant/:tenantId/eagleEyeContent/:id`,
safeWrap(require('./eagleEyeContentUpdate').default),

app.post(
`/tenant/:tenantId/eagleEyeContent`,
featureFlagMiddleware(FeatureFlag.EAGLE_EYE, 'entities.eagleEye.errors.planLimitExceeded'),
safeWrap(require('./eagleEyeContentUpsert').default),
)

app.get(
`/tenant/:tenantId/eagleEyeContent/search`,
featureFlagMiddleware(FeatureFlag.EAGLE_EYE, 'entities.eagleEye.errors.planLimitExceeded'),
safeWrap(require('./eagleEyeContentSearch').default),
)
app.get(`/tenant/:tenantId/eagleEyeContent`, safeWrap(require('./eagleEyeContentList').default))

app.get(
`/tenant/:tenantId/eagleEyeContent/:id`,
featureFlagMiddleware(FeatureFlag.EAGLE_EYE, 'entities.eagleEye.errors.planLimitExceeded'),
safeWrap(require('./eagleEyeContentFind').default),
)

app.post(
`/tenant/:tenantId/eagleEyeContent/:contentId/action`,
featureFlagMiddleware(FeatureFlag.EAGLE_EYE, 'entities.eagleEye.errors.planLimitExceeded'),
safeWrap(require('./eagleEyeActionCreate').default),
)

app.put(
`/tenant/:tenantId/eagleEyeContent/settings`,
featureFlagMiddleware(FeatureFlag.EAGLE_EYE, 'entities.eagleEye.errors.planLimitExceeded'),
safeWrap(require('./eagleEyeSettingsUpdate').default),
)

app.delete(
`/tenant/:tenantId/eagleEyeContent/:contentId/action/:actionId`,
featureFlagMiddleware(FeatureFlag.EAGLE_EYE, 'entities.eagleEye.errors.planLimitExceeded'),
safeWrap(require('./eagleEyeActionDestroy').default),
)
}
5 changes: 5 additions & 0 deletions backend/src/config/configTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,8 @@ export interface EnrichmentConfiguration {
url: string
apiKey: string
}

export interface EagleEyeConfiguration {
url: string
apiKey: string
}
8 changes: 8 additions & 0 deletions backend/src/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
PosthogConfiguration,
PizzlyConfiguration,
EnrichmentConfiguration,
EagleEyeConfiguration,
} from './configTypes'

// TODO-kube
Expand Down Expand Up @@ -233,3 +234,10 @@ export const ENRICHMENT_CONFIG: EnrichmentConfiguration = KUBE_MODE
url: process.env.ENRICHMENT_URL,
apiKey: process.env.ENRICHMENT_SECRET_KEY,
}

export const EAGLE_EYE_CONFIG: EagleEyeConfiguration = KUBE_MODE
? config.get<EagleEyeConfiguration>('eagleEye')
: {
url: process.env.EAGLE_EYE_URL,
apiKey: process.env.EAGLE_EYE_SECRET_KEY,
}
59 changes: 59 additions & 0 deletions backend/src/database/migrations/U1675259471__eagleEyeActions.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
DROP TABLE IF EXISTS "eagleEyeContents";

DROP TYPE "eagleEyeContents_actions_type";

create table "eagleEyeContents";
(
id uuid not null primary key,
"sourceId" text not null,
"vectorId" text not null,
status varchar(255) default NULL::character varying,
title text not null,
username text not null,
url text not null,
text text,
timestamp timestamp with time zone not null,
platform text not null,
keywords text [],
"similarityScore" double precision,
"userAttributes" jsonb,
"postAttributes" jsonb,
"importHash" varchar(255),
"createdAt" timestamp with time zone not null,
"updatedAt" timestamp with time zone not null,
"deletedAt" timestamp with time zone,
"tenantId" uuid not null references tenants on update cascade,
"createdById" uuid references users on update cascade on delete
set null,
"updatedById" uuid references users on update cascade on delete
set null,
"exactKeywords" text []
);

alter table "eagleEyeContents" owner to postgres;

create index discord on "eagleEyeContents" ("vectorId", status);

create index members_email_tenant_id on "eagleEyeContents" (id)
where ("deletedAt" IS NULL);

create index members_joined_at_tenant_id on "eagleEyeContents" (id)
where ("deletedAt" IS NULL);

create index members_username on "eagleEyeContents" using gin (id);

create index slack on "eagleEyeContents" (id);

create index twitter on "eagleEyeContents" (id);

create unique index eagle_eye_contents_import_hash_tenant_id on "eagleEyeContents" ("importHash", "tenantId")
where ("deletedAt" IS NULL);

create index eagle_eye_contents_platform_tenant_id_timestamp on "eagleEyeContents" (platform, "tenantId", timestamp)
where ("deletedAt" IS NULL);

create index eagle_eye_contents_status_tenant_id_timestamp on "eagleEyeContents" (status, "tenantId", timestamp)
where ("deletedAt" IS NULL);

create index eagle_eye_contents_tenant_id_timestamp on "eagleEyeContents" ("tenantId", timestamp)
where ("deletedAt" IS NULL);
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE public."users"
DROP COLUMN "eagleEyeSettings";
33 changes: 33 additions & 0 deletions backend/src/database/migrations/V1675259471__eagleEyeActions.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
DROP TABLE IF EXISTS "eagleEyeContents";

CREATE TABLE public."eagleEyeContents" (
"id" uuid NOT NULL,
"platform" text NOT NULL,
"url" text NOT NULL,
"post" jsonb NOT NULL,
"tenantId" uuid NOT NULL,
"postedAt" timestamptz NOT NULL,
"createdAt" timestamptz NOT NULL,
"updatedAt" timestamptz NOT NULL,
CONSTRAINT "eagleEyeContents_pkey" PRIMARY KEY ("id")
);

ALTER TABLE public."eagleEyeContents" ADD CONSTRAINT "eagleEyeContents_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES public.tenants(id) ON DELETE NO ACTION ON UPDATE NO ACTION;

CREATE TYPE public."eagleEyeActionTypes_type" AS ENUM ('thumbs-up', 'thumbs-down', 'bookmark');

CREATE TABLE public."eagleEyeActions" (
"id" uuid NOT NULL,
"type" public."eagleEyeActionTypes_type" NOT NULL,
"timestamp" timestamptz NOT NULL,
"contentId" uuid NOT NULL,
"tenantId" uuid NOT NULL,
"actionById" uuid NOT NULL,
"createdAt" timestamptz NOT NULL,
"updatedAt" timestamptz NOT NULL,
CONSTRAINT "eagleEyeActions_pkey" PRIMARY KEY ("id")
);

ALTER TABLE public."eagleEyeActions" ADD CONSTRAINT "eagleEyeActions_tenantId_fkey" FOREIGN KEY ("tenantId") REFERENCES public.tenants(id) ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE public."eagleEyeActions" ADD CONSTRAINT "eagleEyeActions_actionBy_fkey" FOREIGN KEY ("actionById") REFERENCES public.users(id) ON DELETE NO ACTION ON UPDATE NO ACTION;
ALTER TABLE public."eagleEyeActions" ADD CONSTRAINT "eagleEyeActions_contentId_fkey" FOREIGN KEY ("contentId") REFERENCES public."eagleEyeContents"(id) ON DELETE CASCADE ON UPDATE NO ACTION;
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
ALTER TABLE public."users"
ADD COLUMN "eagleEyeSettings" JSONB DEFAULT '{"onboarded": false}';
48 changes: 48 additions & 0 deletions backend/src/database/models/eagleEyeAction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { DataTypes } from 'sequelize'

const eagleEyeActionModel = {
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
},
type: {
type: DataTypes.TEXT,
validate: {
isIn: [['thumbs-up', 'thumbs-down', 'bookmark']],
},
defaultValue: null,
},
timestamp: {
type: DataTypes.DATE,
allowNull: false,
},
}

export default (sequelize) => {
const eagleEyeAction = sequelize.define('eagleEyeAction', eagleEyeActionModel, {
timestamps: true,
paranoid: false,
})

eagleEyeAction.associate = (models) => {
models.eagleEyeAction.belongsTo(models.tenant, {
as: 'tenant',
foreignKey: {
allowNull: false,
},
})

models.eagleEyeAction.belongsTo(models.user, {
as: 'actionBy',
})

models.eagleEyeAction.belongsTo(models.eagleEyeContent, {
as: 'content',
})
}

return eagleEyeAction
}

export { eagleEyeActionModel }
Loading