Skip to content

Commit

Permalink
Merge pull request #1034 from interval/multiple-files-js
Browse files Browse the repository at this point in the history
🗄 io.input.file w/ multiple()
  • Loading branch information
jacobmischka authored Jan 12, 2023
2 parents 60cc72a + 092f6bb commit d1b15ce
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 64 deletions.
14 changes: 10 additions & 4 deletions src/classes/IOPromise.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,9 @@ export class MultipleableIOPromise<
MethodName extends T_IO_MULTIPLEABLE_METHOD_NAMES,
Props extends T_IO_PROPS<MethodName> = T_IO_PROPS<MethodName>,
Output = ComponentReturnValue<MethodName>,
DefaultValue = Output
DefaultValue = T_IO_PROPS<MethodName> extends { defaultValue?: any }
? Output
: never
> extends InputIOPromise<MethodName, Props, Output> {
defaultValueGetter:
| ((defaultValue: DefaultValue) => T_IO_RETURNS<MethodName>)
Expand All @@ -305,16 +307,20 @@ export class MultipleableIOPromise<
this.defaultValueGetter = defaultValueGetter
}

multiple({ defaultValue }: { defaultValue?: DefaultValue[] } = {}) {
multiple({
defaultValue,
}: {
defaultValue?: DefaultValue[]
} = {}): MultipleIOPromise<MethodName, Props, Output> {
let transformedDefaultValue: T_IO_RETURNS<MethodName>[] | undefined
if (defaultValue) {
const propsSchema = ioSchema[this.methodName].props
if (defaultValue && 'defaultValue' in propsSchema.shape) {
const { defaultValueGetter } = this
const potentialDefaultValue = defaultValueGetter
? defaultValue.map(dv => defaultValueGetter(dv))
: (defaultValue as unknown as T_IO_RETURNS<MethodName>[])

try {
const propsSchema = ioSchema[this.methodName].props
const defaultValueSchema = propsSchema.shape.defaultValue
transformedDefaultValue = z
.array(defaultValueSchema.unwrap())
Expand Down
23 changes: 8 additions & 15 deletions src/components/upload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async function retryFetch(url: string): Promise<Response> {

type UploaderProps = T_IO_PROPS<'UPLOAD_FILE'> & {
generatePresignedUrls?: (
state: T_IO_STATE<'UPLOAD_FILE'>
state: NonNullable<T_IO_STATE<'UPLOAD_FILE'>['files']>[0]
) => Promise<{ uploadUrl: string; downloadUrl: string }>
}

Expand All @@ -34,8 +34,7 @@ export function file(logger: Logger) {
return {
props: {
...props,
uploadUrl: isProvidingUrls ? null : undefined,
downloadUrl: isProvidingUrls ? null : undefined,
fileUrls: isProvidingUrls ? null : undefined,
},
getValue({ url, ...response }: T_IO_RETURNS<'UPLOAD_FILE'>) {
return {
Expand All @@ -61,20 +60,14 @@ export function file(logger: Logger) {
}
},
async onStateChange(newState: T_IO_STATE<'UPLOAD_FILE'>) {
if (!generatePresignedUrls) {
return { uploadUrl: undefined, downloadUrl: undefined }
if (!generatePresignedUrls || !newState.files) {
return { fileUrls: undefined }
}

try {
const urls = await generatePresignedUrls(newState)
return urls
} catch (error) {
// TODO: We should not swallow this error after merging #1012
logger.error(
'An error was unexpectedly thrown from the `generatePresignedUrls` function:'
)
logger.error(error)
return { uploadUrl: 'error', downloadUrl: 'error' }
return {
fileUrls: await Promise.all(
newState.files.map(fileState => generatePresignedUrls(fileState))
),
}
},
}
Expand Down
110 changes: 68 additions & 42 deletions src/examples/basic/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1101,15 +1101,15 @@ const interval = new Interval({
# What to expect from here on out
_This has been adapted from the [Tailwind](https://tailwindcss.com) typography plugin demo._
What follows from here is just a bunch of absolute nonsense I've written to demo typography. It includes every sensible typographic element I could think of, like **bold text**, unordered lists, ordered lists, code blocks, block quotes, and _even italics_.
It's important to cover all of these use cases for a few reasons:
1. We want everything to look good out of the box.
2. Really just the first reason, that's the whole point of the plugin.
3. Here's a third pretend reason though a list with three items looks more realistic than a list with two items.
Now we're going to try out another header style.
## Typography should be easy
Expand All @@ -1128,7 +1128,7 @@ const interval = new Interval({
- And this is the last item in the list.
### What does code look like?
Code blocks should look okay by default, although most people will probably want to use \`io.display.code\`:
\`\`\`
Expand All @@ -1139,7 +1139,7 @@ const interval = new Interval({
}
})
\`\`\`
#### And finally, an H4
And that's the end of this demo.
Expand Down Expand Up @@ -1311,49 +1311,75 @@ const interval = new Interval({

return { message: 'OK, notified!' }
},
upload: async (io, ctx) => {
const customDestinationFile = await io.input.file('Upload an image!', {
helpText: 'Will be uploaded to the custom destination.',
allowedExtensions: ['.gif', '.jpg', '.jpeg', '.png'],
generatePresignedUrls: async ({ name }) => {
const urlSafeName = name.replace(/ /g, '-')
const path = `custom-endpoint/${new Date().getTime()}-${urlSafeName}`

return generateS3Urls(path)
},
})

console.log(await customDestinationFile.url())
uploads: new Page({
name: 'Uploads',
routes: {
custom_destination: async io => {
const customDestinationFile = await io.input.file(
'Upload an image!',
{
helpText: 'Will be uploaded to the custom destination.',
allowedExtensions: ['.gif', '.jpg', '.jpeg', '.png'],
generatePresignedUrls: async ({ name }) => {
const urlSafeName = name.replace(/ /g, '-')
const path = `custom-endpoint/${new Date().getTime()}-${urlSafeName}`

const file = await io.input.file('Upload an image!', {
helpText:
'Will be uploaded to Interval and expire after the action finishes running.',
allowedExtensions: ['.gif', '.jpg', '.jpeg', '.png'],
})
return generateS3Urls(path)
},
}
)

console.log(file)
console.log(await customDestinationFile.url())

const { text, json, buffer, url, ...rest } = file
const { text, json, buffer, url, ...rest } = customDestinationFile

return {
...rest,
url: await url(),
text: rest.type.includes('text/')
? await text().catch(err => {
console.log('Invalid text', err)
return undefined
return {
...rest,
url: await url(),
text: rest.type.includes('text/')
? await text().catch(err => {
console.log('Invalid text', err)
return undefined
})
: undefined,
json: rest.type.includes('text/')
? await json()
.then(obj => JSON.stringify(obj))
.catch(err => {
console.log('Invalid JSON', err)
return undefined
})
: undefined,
}
},
multiple: async io => {
const files = await io.input
.file('Upload an image!', {
helpText:
'Will be uploaded to Interval and expire after the action finishes running.',
allowedExtensions: ['.gif', '.jpg', '.jpeg', '.png'],
})
: undefined,
json: rest.type.includes('text/')
? await json()
.then(obj => JSON.stringify(obj))
.catch(err => {
console.log('Invalid JSON', err)
return undefined
})
: undefined,
}
},
.multiple()
.optional()

if (!files) return 'None selected.'

await io.group(
(
await Promise.all(
files.map(async file => [
io.display.image(file.name, {
url: await file.url(),
}),
])
)
).map(([p]) => p)
)

return Object.fromEntries(files.map((file, i) => [i, file.name]))
},
},
}),
advanced_data: async io => {
const data = {
bigInt: BigInt(5),
Expand Down
27 changes: 24 additions & 3 deletions src/ioSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,13 +661,33 @@ const INPUT_SCHEMA = {
props: z.object({
helpText: z.string().optional(),
allowedExtensions: z.array(z.string()).optional(),
disabled: z.optional(z.boolean().default(false)),
fileUrls: z
.array(
z.object({
uploadUrl: z.string(),
downloadUrl: z.string(),
})
)
.nullish(),

// Deprecated
uploadUrl: z.string().nullish().optional(),
downloadUrl: z.string().nullish().optional(),
disabled: z.optional(z.boolean().default(false)),
}),
state: z.object({
name: z.string(),
type: z.string(),
files: z
.array(
z.object({
name: z.string(),
type: z.string(),
})
)
.optional(),

// Deprecated
name: z.string().optional(),
type: z.string().optional(),
}),
returns: z.object({
name: z.string(),
Expand All @@ -676,6 +696,7 @@ const INPUT_SCHEMA = {
size: z.number(),
url: z.string(),
}),
supportsMultiple: true,
},
SEARCH: {
props: z.object({
Expand Down

0 comments on commit d1b15ce

Please sign in to comment.