mirror of
https://fast.feibisi.com/https://github.com/parcelvoy/platform.git
synced 2025-09-01 12:26:08 +08:00
Merge pull request #57 from parcelvoy/chore/fix-list-generation-and-twilio
Fix list generation and Twilio
This commit is contained in:
commit
b35288d65e
14 changed files with 54 additions and 20 deletions
|
@ -0,0 +1,17 @@
|
|||
exports.up = async function(knex) {
|
||||
await knex.schema.table('user_list', function(table) {
|
||||
table.integer('version').after('event_id').defaultTo(0).index()
|
||||
})
|
||||
await knex.schema.table('lists', function(table) {
|
||||
table.integer('version').after('users_count').defaultTo(0)
|
||||
})
|
||||
}
|
||||
|
||||
exports.down = async function(knex) {
|
||||
await knex.schema.table('user_list', function(table) {
|
||||
table.dropColumn('version')
|
||||
})
|
||||
await knex.schema.table('lists', function(table) {
|
||||
table.dropColumn('version')
|
||||
})
|
||||
}
|
|
@ -24,7 +24,7 @@ export const pagedCampaigns = async (params: SearchParams, projectId: number) =>
|
|||
params,
|
||||
['name'],
|
||||
b => {
|
||||
b.where({ project_id: projectId })
|
||||
b.where({ project_id: projectId }).orderBy('id', 'desc')
|
||||
params?.tags?.length && b.whereIn('id', createTagSubquery(Campaign, projectId, params.tags))
|
||||
return b
|
||||
},
|
||||
|
|
|
@ -25,7 +25,7 @@ export default class TextJob extends Job {
|
|||
}
|
||||
|
||||
// Send and render text
|
||||
const channel = await loadTextChannel(campaign.project_id, project.id)
|
||||
const channel = await loadTextChannel(campaign.provider_id, project.id)
|
||||
if (!channel) {
|
||||
await updateSendState(campaign, user, 'failed')
|
||||
return
|
||||
|
|
|
@ -47,7 +47,7 @@ export default class TwilioTextProvider extends TextProvider {
|
|||
|
||||
async send(message: TextMessage): Promise<TextResponse> {
|
||||
const { to, text } = message
|
||||
const form = new FormData()
|
||||
const form = new URLSearchParams()
|
||||
form.append('From', this.phone_number)
|
||||
form.append('To', to)
|
||||
form.append('Body', text)
|
||||
|
|
|
@ -10,6 +10,7 @@ export default class List extends Model {
|
|||
type!: ListType
|
||||
state!: ListState
|
||||
rule?: Rule
|
||||
version!: number
|
||||
users_count?: number
|
||||
|
||||
static jsonAttributes = ['rule']
|
||||
|
@ -21,6 +22,7 @@ export class UserList extends Model {
|
|||
user_id!: number
|
||||
list_id!: number
|
||||
event_id!: number
|
||||
version!: number
|
||||
deleted_at?: Date
|
||||
|
||||
static tableName = 'user_list'
|
||||
|
|
|
@ -21,7 +21,7 @@ export default class ListPopulateJob extends Job {
|
|||
const list = await getList(listId, projectId) as DynamicList
|
||||
if (!list) return
|
||||
|
||||
await populateList(list.id, list.rule)
|
||||
await populateList(list, list.rule)
|
||||
|
||||
App.main.queue.enqueue(ListStatsJob.from(listId, projectId))
|
||||
}
|
||||
|
|
|
@ -56,7 +56,8 @@ export const createList = async (projectId: number, params: ListCreateParams): P
|
|||
project_id: projectId,
|
||||
})
|
||||
|
||||
if (list.type === 'dynamic') {
|
||||
const hasRules = (params.rule?.children?.length ?? 0) > 0
|
||||
if (list.type === 'dynamic' && hasRules) {
|
||||
App.main.queue.enqueue(
|
||||
ListPopulateJob.from(list.id, list.project_id),
|
||||
)
|
||||
|
@ -102,19 +103,21 @@ export const importUsersToList = async (list: List, stream: FileStream) => {
|
|||
await updateList(list.id, { state: 'ready' })
|
||||
}
|
||||
|
||||
export const populateList = async (id: number, rule: Rule) => {
|
||||
const list = await updateList(id, { state: 'loading' })
|
||||
export const populateList = async (list: List, rule: Rule) => {
|
||||
const { id, version: oldVersion } = list
|
||||
const version = oldVersion + 1
|
||||
await updateList(id, { state: 'loading', version })
|
||||
|
||||
type UserListChunk = { user_id: number, list_id: number }[]
|
||||
type UserListChunk = { user_id: number, list_id: number, version: number }[]
|
||||
const insertChunk = async (chunk: UserListChunk) => {
|
||||
return await UserList.query()
|
||||
.insert(chunk)
|
||||
.onConflict(['user_id', 'list_id'])
|
||||
.ignore()
|
||||
.merge(['version'])
|
||||
}
|
||||
|
||||
await ruleQuery(rule)
|
||||
.where('users.project_id', list!.project_id)
|
||||
.where('users.project_id', list.project_id)
|
||||
.stream(async function(stream) {
|
||||
|
||||
// Stream results and insert in chunks of 100
|
||||
|
@ -122,7 +125,7 @@ export const populateList = async (id: number, rule: Rule) => {
|
|||
let chunk: UserListChunk = []
|
||||
let i = 0
|
||||
for await (const { id: user_id } of stream) {
|
||||
chunk.push({ user_id, list_id: id })
|
||||
chunk.push({ user_id, list_id: id, version })
|
||||
i++
|
||||
if (i % chunkSize === 0) {
|
||||
await insertChunk(chunk)
|
||||
|
@ -133,6 +136,10 @@ export const populateList = async (id: number, rule: Rule) => {
|
|||
// Insert remaining items
|
||||
await insertChunk(chunk)
|
||||
})
|
||||
|
||||
// Once list is regenerated, drop any users from previous version
|
||||
await UserList.delete(qb => qb.where('version', '<', version))
|
||||
|
||||
await updateList(id, { state: 'ready' })
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ export const getUserEvents = async (id: number, params: SearchParams, projectId:
|
|||
params,
|
||||
['name'],
|
||||
b => b.where('project_id', projectId)
|
||||
.where('user_id', id),
|
||||
.where('user_id', id)
|
||||
.orderBy('id', 'desc'),
|
||||
)
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@
|
|||
}
|
||||
|
||||
.ui-button.secondary {
|
||||
background: var(--color-background);
|
||||
border: 1px solid var(--color-grey);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
|
|
@ -64,11 +64,13 @@
|
|||
}
|
||||
|
||||
.project-switcher .switcher-value {
|
||||
color: var(--color-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.project-switcher .project-switcher-icon {
|
||||
color: var(--color-primary);
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
flex-shrink: 0;
|
||||
|
|
|
@ -97,6 +97,7 @@
|
|||
border-radius: var(--border-radius-inner);
|
||||
font-weight: 500;
|
||||
margin: 3px 0;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.ui-select .select-option:first-child {
|
||||
|
@ -125,12 +126,12 @@
|
|||
|
||||
.ui-select .select-option.selected .option-icon {
|
||||
display: block;
|
||||
height: 20px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
.ui-select .select-option.selected svg {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
height: 15px;
|
||||
width: 15px;
|
||||
}
|
||||
|
||||
.ui-select .select-option.disabled {
|
||||
|
|
|
@ -69,7 +69,10 @@ export default function Campaigns() {
|
|||
key: 'delivery',
|
||||
cell: ({ item }) => `${item.delivery?.sent ?? 0} / ${item.delivery?.total ?? 0}`,
|
||||
},
|
||||
{ key: 'launched_at' },
|
||||
{
|
||||
key: 'send_at',
|
||||
title: 'Launched At',
|
||||
},
|
||||
{ key: 'updated_at' },
|
||||
{
|
||||
key: 'options',
|
||||
|
|
|
@ -21,7 +21,7 @@ const RuleSection = ({ list, onRuleSave }: { list: DynamicList, onRuleSave: (rul
|
|||
const [rule, setRule] = useState<WrapperRule>(list.rule)
|
||||
return <>
|
||||
<Heading size="h3" title="Rules" actions={
|
||||
<Button size="small" onClick={() => onRuleSave(rule) }>Save List</Button>
|
||||
<Button size="small" onClick={() => onRuleSave(rule) }>Save Rules</Button>
|
||||
} />
|
||||
<RuleBuilder rule={rule} setRule={setRule} />
|
||||
</>
|
||||
|
@ -86,8 +86,8 @@ export default function ListDetail() {
|
|||
<Dialog
|
||||
open={isDialogOpen}
|
||||
onClose={setIsDialogOpen}
|
||||
title="List Saved!">
|
||||
Initial list generation will happen in the background. Please reload the page to see the latest status.
|
||||
title="Success">
|
||||
List generation will happen in the background. Please reload the page to see the latest status.
|
||||
</Dialog>
|
||||
|
||||
<Modal
|
||||
|
|
|
@ -88,7 +88,7 @@ const RuleSet = ({ group, onChange, onDelete }: RuleSetParams) => {
|
|||
|
||||
return <div className="rule-set">
|
||||
{group && <div className="rule-set-header">
|
||||
{!onDelete && 'Target all users '}
|
||||
{!onDelete && 'Target users '}
|
||||
{isEvent
|
||||
? <>
|
||||
Did
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue