|
1 | | -const _ = require( "lodash" ); |
2 | | -const squel = require( "safe-squel" ); |
3 | 1 | const { announcements } = require( "inaturalistjs" ); |
4 | 2 | const InaturalistAPI = require( "../../inaturalist_api" ); |
5 | | -const pgClient = require( "../../pg_client" ); |
6 | | -const Announcement = require( "../../models/announcement" ); |
7 | | -const AnnouncementImpression = require( "../../models/announcement_impression" ); |
8 | | -const Site = require( "../../models/site" ); |
9 | | -const util = require( "../../util" ); |
10 | 3 |
|
11 | 4 | const AnnouncementsController = class AnnouncementsController { |
12 | 5 | static async search( req ) { |
13 | | - let query = squel.select( ) |
14 | | - .field( "announcements.id" ) |
15 | | - .field( "body" ) |
16 | | - .field( "placement" ) |
17 | | - .field( "dismissible" ) |
18 | | - .field( "locales" ) |
19 | | - .field( "clients" ) |
20 | | - .field( "start" ) |
21 | | - .field( "\"end\"" ) |
22 | | - .field( "target_group_type" ) |
23 | | - .field( "target_group_partition" ) |
24 | | - .field( "to_char( include_donor_start_date, 'YYYY-MM-DD') AS include_donor_start_date" ) |
25 | | - .field( "to_char( include_donor_end_date, 'YYYY-MM-DD') AS include_donor_end_date" ) |
26 | | - .field( "to_char( exclude_donor_start_date, 'YYYY-MM-DD') AS exclude_donor_start_date" ) |
27 | | - .field( "to_char( exclude_donor_end_date, 'YYYY-MM-DD') AS exclude_donor_end_date" ) |
28 | | - .from( "announcements" ) |
29 | | - .where( "NOW() at time zone 'utc' between start and \"end\"" ) |
30 | | - .order( "announcements.id" ); |
31 | | - |
32 | | - // placement filter |
33 | | - if ( req.query.placement ) { |
34 | | - let placementClause = squel.expr( ); |
35 | | - _.each( req.query.placement.split( "," ), placement => { |
36 | | - switch ( placement ) { |
37 | | - case "mobile": |
38 | | - placementClause = placementClause.or( "placement LIKE 'mobile%'" ); |
39 | | - break; |
40 | | - default: |
41 | | - placementClause = placementClause.or( "placement = ?", placement ); |
42 | | - break; |
43 | | - } |
44 | | - } ); |
45 | | - query = query.where( placementClause ); |
46 | | - } |
47 | | - |
48 | | - const userAgentClient = util.userAgentClient( req ); |
49 | | - if ( req.query.client || userAgentClient ) { |
50 | | - // given a client parameter, return only announcements that include that client, |
51 | | - // or announcements with no client specified |
52 | | - query = query.where( "? = ANY( clients ) OR clients IS NULL OR clients = '{}'", |
53 | | - req.query.client || userAgentClient ); |
54 | | - } else { |
55 | | - // if there is no client parameter, return only announcements with no client specified |
56 | | - query = query.where( "clients IS NULL OR clients = '{}'" ); |
57 | | - } |
58 | | - |
59 | | - // site_id filter |
60 | | - if ( req.userSession ) { |
61 | | - // authenticated requests include announcements targeted at the users site, |
62 | | - // or that have no site affiliation |
63 | | - query = query.left_join( "announcements_sites", null, "announcements.id = announcements_sites.announcement_id" ) |
64 | | - .where( "announcements_sites.site_id IS NULL OR announcements_sites.site_id = ?", |
65 | | - req.userSession?.site_id || Site.defaultID ); |
66 | | - } else { |
67 | | - // unauthenticated requests exclude announcements associated with sites |
68 | | - query = query.left_join( "announcements_sites", null, "announcements.id = announcements_sites.announcement_id" ) |
69 | | - .where( "announcements_sites.site_id IS NULL" ); |
70 | | - } |
71 | | - |
72 | | - // exclude announcements the authenticated user has dismissed |
73 | | - if ( req.userSession ) { |
74 | | - query = query.where( "NOT ( ? = ANY( dismiss_user_ids ) )", req.userSession.user_id ); |
75 | | - } |
76 | | - |
77 | | - // when request is unauthenticated, or authenticated user has confirmed their email, |
78 | | - // exclude announcements that are targeting users with unconfirmed emails |
79 | | - if ( !req.userSession || ( req.userSession && req.userSession.emailConfirmed ) ) { |
80 | | - query = query.left_join( "preferences prefs_unconfirmed", null, "announcements.id = prefs_unconfirmed.owner_id AND" |
81 | | - + " prefs_unconfirmed.owner_type = 'Announcement' AND prefs_unconfirmed.name = 'target_unconfirmed_users'" |
82 | | - + " AND prefs_unconfirmed.value='t'" ) |
83 | | - .where( "prefs_unconfirmed.id IS NULL" ); |
84 | | - } |
85 | | - |
86 | | - // when request is unauthenticated, or authenticated is not staff, |
87 | | - // exclude announcements that are targeting staff |
88 | | - if ( !req.userSession || ( req.userSession && !req.userSession.isAdmin ) ) { |
89 | | - query = query.left_join( "preferences prefs_staff", null, "announcements.id = prefs_staff.owner_id AND" |
90 | | - + " prefs_staff.owner_type = 'Announcement' AND prefs_staff.name = 'target_staff'" |
91 | | - + " AND prefs_staff.value='t'" ) |
92 | | - .where( "prefs_staff.id IS NULL" ); |
93 | | - } |
94 | | - |
95 | | - // when request is authenticated and the authenticated user is a montly supporter, |
96 | | - // exclude announcements that prefer to exclude monthly supporters |
97 | | - if ( req.userSession?.isMonthlySupporter ) { |
98 | | - query = query.left_join( |
99 | | - "preferences prefs_exclude_supporters", |
100 | | - null, |
101 | | - "announcements.id = prefs_exclude_supporters.owner_id AND" |
102 | | - + " prefs_exclude_supporters.owner_type = 'Announcement' AND" |
103 | | - + " prefs_exclude_supporters.name = 'exclude_monthly_supporters' AND" |
104 | | - + " prefs_exclude_supporters.value='t'" |
105 | | - ).where( "prefs_exclude_supporters.id IS NULL" ); |
106 | | - } |
107 | | - |
108 | | - let announcementRows; |
109 | | - const queryLocale = req.query.locale || req.userSession?.locale; |
110 | | - // locale filter |
111 | | - if ( queryLocale ) { |
112 | | - // first attempt to fetch announcements with an exact match for locale |
113 | | - const exactLocaleQuery = query.clone( ).where( "? = ANY( locales )", queryLocale ); |
114 | | - ( { rows: announcementRows } = await pgClient.replica.query( exactLocaleQuery.toString( ) ) ); |
115 | | - // if none exist and the locale is hyphenated, fetch announcements with the base locale |
116 | | - if ( _.isEmpty( announcementRows ) && queryLocale.match( "-" ) ) { |
117 | | - const baseLocaleQuery = query.clone( ).where( "? = ANY( locales )", queryLocale.split( "-" )[0] ); |
118 | | - ( { rows: announcementRows } = await pgClient.replica.query( |
119 | | - baseLocaleQuery.toString( ) |
120 | | - ) ); |
121 | | - } |
122 | | - // if none exist for either version of the locale, fetch announcements with no locale |
123 | | - if ( _.isEmpty( announcementRows ) ) { |
124 | | - const noLocaleQuery = query.clone( ).where( "locales IS NULL OR locales = '{}'" ); |
125 | | - ( { rows: announcementRows } = await pgClient.replica.query( noLocaleQuery.toString( ) ) ); |
126 | | - } |
127 | | - } else { |
128 | | - // if no locale filter was requested, fetch announcements with the already applied filters |
129 | | - ( { rows: announcementRows } = await pgClient.replica.query( query.toString( ) ) ); |
130 | | - } |
131 | | - |
132 | | - // filter announcements using async functions |
133 | | - const filterPromises = _.map( announcementRows, async announcement => ( |
134 | | - Announcement.targetedToRequestor( announcement, req ) |
135 | | - && Announcement.requestorMatchesDonationRequirements( announcement, req ) |
136 | | - ) ); |
137 | | - const filterPromiseResults = await Promise.all( filterPromises ); |
138 | | - announcementRows = _.filter( announcementRows, |
139 | | - ( announcement, index ) => filterPromiseResults[index] ); |
140 | | - |
141 | | - // remove columns fetched for filtering that should not be in the final response |
142 | | - announcementRows = _.map( announcementRows, announcement => ( |
143 | | - _.omit( announcement, [ |
144 | | - "target_group_type", |
145 | | - "target_group_partition", |
146 | | - "include_donor_start_date", |
147 | | - "include_donor_end_date", |
148 | | - "exclude_donor_start_date", |
149 | | - "exclude_donor_end_date" |
150 | | - ] ) |
151 | | - ) ); |
152 | | - |
153 | | - await AnnouncementImpression.createAnnouncementImpressions( req, announcementRows ); |
| 6 | + const anncs = await InaturalistAPI.iNatJSWrap( announcements.search, req ); |
154 | 7 | return { |
155 | | - total_results: announcementRows.length, |
| 8 | + total_results: anncs.length, |
156 | 9 | page: 1, |
157 | | - per_page: announcementRows.length, |
158 | | - results: announcementRows |
| 10 | + per_page: anncs.length, |
| 11 | + results: anncs |
159 | 12 | }; |
160 | 13 | } |
161 | 14 |
|
|
0 commit comments