generated from tpl/purs
fix: form bug
This commit is contained in:
parent
fc33a076db
commit
1a1d5526b7
23
.spec-results
Normal file
23
.spec-results
Normal file
@ -0,0 +1,23 @@
|
||||
[
|
||||
[
|
||||
"Form fromRaw",
|
||||
{
|
||||
"timestamp": "1732391355864.0",
|
||||
"success": true
|
||||
}
|
||||
],
|
||||
[
|
||||
"Form ok",
|
||||
{
|
||||
"timestamp": "1732390464524.0",
|
||||
"success": true
|
||||
}
|
||||
],
|
||||
[
|
||||
"Form toRaw",
|
||||
{
|
||||
"timestamp": "1732391355864.0",
|
||||
"success": true
|
||||
}
|
||||
]
|
||||
]
|
2234
spago.lock
2234
spago.lock
File diff suppressed because it is too large
Load Diff
@ -10,6 +10,11 @@ package:
|
||||
location:
|
||||
githubOwner: 'cakekindel'
|
||||
githubRepo: 'purescript-ezfetch'
|
||||
test:
|
||||
main: 'Test.Main'
|
||||
dependencies:
|
||||
- spec
|
||||
- spec-node
|
||||
dependencies:
|
||||
- aff: ">=7.1.0 <8.0.0"
|
||||
- aff-promise: ">=4.0.0 <5.0.0"
|
||||
|
@ -1,4 +1,4 @@
|
||||
/** @type {(_: Record<string, Array<string | Blob>>) => () => FormData} */
|
||||
/** @type {(_: Record<string, Array<string | File>>) => () => FormData} */
|
||||
export const unsafeMakeFormData = o => () => {
|
||||
const form = new FormData()
|
||||
|
||||
@ -11,20 +11,20 @@ export const unsafeMakeFormData = o => () => {
|
||||
return form
|
||||
}
|
||||
|
||||
/** @typedef {{filename: string | null, mime: string, buf: ArrayBuffer}} FileRecord */
|
||||
/** @typedef {{filename: string, mime: string, buf: ArrayBuffer}} FileRecord */
|
||||
|
||||
/** @type {(_: FileRecord) => () => Blob} */
|
||||
export const unsafeMakeBlob =
|
||||
({ mime, buf }) =>
|
||||
/** @type {(_: FileRecord) => () => File} */
|
||||
export const unsafeMakeFile =
|
||||
({ mime, buf, filename }) =>
|
||||
() =>
|
||||
new Blob([buf], { type: mime })
|
||||
new File([buf], filename, { type: mime })
|
||||
|
||||
/** @type {(_: FormData) => () => Promise<Record<string, Array<string | FileRecord>>>} */
|
||||
export const unsafeUnmakeFormData = fd => async () => {
|
||||
/** @type {Record<string, Array<string | FileRecord>>} */
|
||||
const rec = {}
|
||||
for (const [k, ent_] of fd.entries()) {
|
||||
/** @type {File | Blob | string} */
|
||||
/** @type {File | string} */
|
||||
const ent = ent_
|
||||
|
||||
/** @type {string | FileRecord} */
|
||||
@ -35,8 +35,6 @@ export const unsafeUnmakeFormData = fd => async () => {
|
||||
buf: await ent.arrayBuffer(),
|
||||
mime: ent.type,
|
||||
}
|
||||
} else if (ent instanceof Blob) {
|
||||
append = { filename: null, buf: await ent.arrayBuffer(), mime: ent.type }
|
||||
} else {
|
||||
append = ent
|
||||
}
|
||||
@ -54,3 +52,29 @@ export const unsafeUnmakeFormData = fd => async () => {
|
||||
|
||||
return rec
|
||||
}
|
||||
|
||||
/** @type {(a: ArrayBuffer) => (b: ArrayBuffer) => boolean} */
|
||||
export const unsafeEqArrayBuffer = a => b => {
|
||||
try {
|
||||
if (a.byteLength !== b.byteLength) return false
|
||||
const ua = new Uint8Array(a)
|
||||
const ub = new Uint8Array(b)
|
||||
let pass = true
|
||||
for (let i = 0; i < a.byteLength; i++) {
|
||||
pass = pass && ua[i] === ub[i]
|
||||
}
|
||||
|
||||
return pass
|
||||
} catch {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {(a: ArrayBuffer) => string} */
|
||||
export const unsafeShowArrayBuffer = a => {
|
||||
try {
|
||||
return Buffer.from(a).toString('base64url')
|
||||
} catch (e) {
|
||||
return e instanceof Error ? e.toString() : ''
|
||||
}
|
||||
}
|
||||
|
@ -10,12 +10,12 @@ import Data.ArrayBuffer.Types (ArrayBuffer)
|
||||
import Data.Either (hush)
|
||||
import Data.FoldableWithIndex (foldlWithIndex)
|
||||
import Data.Generic.Rep (class Generic)
|
||||
import Data.MIME (MIME)
|
||||
import Data.MIME as MIME
|
||||
import Data.Map (Map)
|
||||
import Data.Map as Map
|
||||
import Data.Maybe (Maybe(..))
|
||||
import Data.Newtype (class Newtype, unwrap, wrap)
|
||||
import Data.Nullable (Nullable)
|
||||
import Data.Nullable as Nullable
|
||||
import Data.Show.Generic (genericShow)
|
||||
import Data.Traversable (for)
|
||||
import Effect (Effect)
|
||||
@ -25,16 +25,16 @@ import Effect.Exception (error)
|
||||
import Foreign (Foreign, unsafeReadTagged, unsafeToForeign)
|
||||
import Foreign.Object (Object)
|
||||
import Foreign.Object as Object
|
||||
import Data.MIME (MIME)
|
||||
import Data.MIME as MIME
|
||||
import Simple.JSON (readImpl, unsafeStringify)
|
||||
import Unsafe.Coerce (unsafeCoerce)
|
||||
import Web.File.Blob (Blob)
|
||||
import Web.File.File (File)
|
||||
|
||||
type FileRecord = { filename :: Nullable String, mime :: String, buf :: ArrayBuffer }
|
||||
type FileRecord = { filename :: String, mime :: String, buf :: ArrayBuffer }
|
||||
|
||||
foreign import data RawFormData :: Type
|
||||
foreign import unsafeMakeBlob :: FileRecord -> Effect Blob
|
||||
foreign import unsafeShowArrayBuffer :: ArrayBuffer -> String
|
||||
foreign import unsafeEqArrayBuffer :: ArrayBuffer -> ArrayBuffer -> Boolean
|
||||
foreign import unsafeMakeFile :: FileRecord -> Effect File
|
||||
foreign import unsafeMakeFormData :: Object (Array Foreign) -> Effect RawFormData
|
||||
foreign import unsafeUnmakeFormData :: RawFormData -> Effect (Promise (Object (Array Foreign)))
|
||||
|
||||
@ -50,16 +50,16 @@ derive newtype instance Ord Filename
|
||||
|
||||
data Value
|
||||
= ValueString String
|
||||
| ValueFile (Maybe Filename) ArrayBuffer MIME
|
||||
| ValueFile Filename ArrayBuffer MIME
|
||||
|
||||
valueForeign :: Value -> Effect Foreign
|
||||
valueForeign (ValueString s) = pure $ unsafeToForeign s
|
||||
valueForeign (ValueFile filename buf mime) = unsafeToForeign <$> unsafeMakeBlob { filename: Nullable.toNullable $ unwrap <$> filename, buf, mime: MIME.toString mime }
|
||||
valueForeign (ValueFile filename buf mime) = unsafeToForeign <$> unsafeMakeFile { filename: unwrap filename, buf, mime: MIME.toString mime }
|
||||
|
||||
valueFromForeign :: Foreign -> Effect Value
|
||||
valueFromForeign f = do
|
||||
let
|
||||
file :: Maybe { filename :: Nullable String, buf :: Foreign, mime :: String }
|
||||
file :: Maybe { filename :: String, buf :: Foreign, mime :: String }
|
||||
file = hush $ runExcept $ readImpl f
|
||||
string = hush $ runExcept $ unsafeReadTagged "String" f
|
||||
|
||||
@ -69,7 +69,7 @@ valueFromForeign f = do
|
||||
buf' :: ArrayBuffer
|
||||
buf' = unsafeCoerce buf
|
||||
in
|
||||
pure $ ValueFile (wrap <$> Nullable.toMaybe filename) buf' (MIME.fromString mime)
|
||||
pure $ ValueFile (wrap filename) buf' (MIME.fromString mime)
|
||||
Nothing -> do
|
||||
s <- liftMaybe (error $ "invalid form value " <> unsafeStringify f) string
|
||||
pure $ ValueString s
|
||||
@ -77,12 +77,21 @@ valueFromForeign f = do
|
||||
derive instance Generic Value _
|
||||
instance Show Value where
|
||||
show (ValueString s) = "(ValueString " <> show s <> ")"
|
||||
show (ValueFile filename _ mime) = "(ValueFile (" <> show filename <> ") <ArrayBuffer> (" <> show mime <> "))"
|
||||
show (ValueFile filename buf mime) = "(ValueFile (" <> show filename <> ") (ArrayBuffer " <> unsafeShowArrayBuffer buf <> ") (" <> show mime <> "))"
|
||||
|
||||
instance Eq Value where
|
||||
eq (ValueString a) (ValueString b) = a == b
|
||||
eq (ValueFile namea bufa mimea) (ValueFile nameb bufb mimeb)
|
||||
| namea /= nameb = false
|
||||
| mimea /= mimeb = false
|
||||
| otherwise = unsafeEqArrayBuffer bufa bufb
|
||||
eq _ _ = false
|
||||
|
||||
newtype Form = Form (Map String (Array Value))
|
||||
|
||||
derive instance Newtype Form _
|
||||
derive newtype instance Show Form
|
||||
derive newtype instance Eq Form
|
||||
|
||||
fromRaw :: forall m. MonadAff m => RawFormData -> m Form
|
||||
fromRaw f = do
|
||||
@ -101,3 +110,4 @@ toRawFormData =
|
||||
pure $ Object.insert k vs' o'
|
||||
in
|
||||
liftEffect <<< flip bind unsafeMakeFormData <<< foldlWithIndex collect (pure Object.empty) <<< unwrap
|
||||
|
||||
|
7
test/Test.Effect.Aff.HTTP.Form.js
Normal file
7
test/Test.Effect.Aff.HTTP.Form.js
Normal file
@ -0,0 +1,7 @@
|
||||
export const dummyForm = () => {
|
||||
const form = new FormData()
|
||||
form.set('foo', 'bar')
|
||||
const hi = Buffer.from('hello, world!', 'utf8')
|
||||
form.set('baz', new Blob([hi.buffer.slice(hi.byteOffset, hi.byteOffset + hi.byteLength)], {type: 'text/plain'}), 'foo.txt')
|
||||
return form
|
||||
}
|
32
test/Test.Effect.Aff.HTTP.Form.purs
Normal file
32
test/Test.Effect.Aff.HTTP.Form.purs
Normal file
@ -0,0 +1,32 @@
|
||||
module Test.Effect.Aff.HTTP.Form where
|
||||
|
||||
import Prelude
|
||||
|
||||
import Data.MIME as MIME
|
||||
import Data.Map as Map
|
||||
import Data.Tuple.Nested ((/\))
|
||||
import Effect (Effect)
|
||||
import Effect.Aff.HTTP.Form (Form(..), RawFormData)
|
||||
import Effect.Aff.HTTP.Form as Form
|
||||
import Effect.Class (liftEffect)
|
||||
import Node.Buffer as Buffer
|
||||
import Node.Encoding (Encoding(..))
|
||||
import Test.Spec (Spec, describe, it)
|
||||
import Test.Spec.Assertions (shouldEqual)
|
||||
|
||||
foreign import dummyForm :: Effect RawFormData
|
||||
|
||||
spec :: Spec Unit
|
||||
spec = describe "Form" do
|
||||
it "fromRaw" do
|
||||
dummy <- liftEffect dummyForm
|
||||
f <- Form.fromRaw dummy
|
||||
buf <- Buffer.fromString "hello, world!" UTF8 >>= Buffer.toArrayBuffer # liftEffect
|
||||
f `shouldEqual` Form (Map.fromFoldable [ "foo" /\ [ Form.ValueString "bar" ], "baz" /\ [ Form.ValueFile (Form.Filename "foo.txt") buf MIME.Txt ] ])
|
||||
it "toRaw" do
|
||||
expect <- liftEffect dummyForm >>= Form.fromRaw
|
||||
buf <- Buffer.fromString "hello, world!" UTF8 >>= Buffer.toArrayBuffer # liftEffect
|
||||
let
|
||||
f = Form (Map.fromFoldable [ "foo" /\ [ Form.ValueString "bar" ], "baz" /\ [ Form.ValueFile (Form.Filename "foo.txt") buf MIME.Txt ] ])
|
||||
actual <- Form.toRawFormData f >>= Form.fromRaw
|
||||
expect `shouldEqual` actual
|
12
test/Test.Main.purs
Normal file
12
test/Test.Main.purs
Normal file
@ -0,0 +1,12 @@
|
||||
module Test.Main where
|
||||
|
||||
import Prelude
|
||||
|
||||
import Effect (Effect)
|
||||
import Test.Spec.Reporter (specReporter)
|
||||
import Test.Spec.Runner.Node (runSpecAndExitProcess)
|
||||
import Test.Effect.Aff.HTTP.Form as Test.Form
|
||||
|
||||
main :: Effect Unit
|
||||
main = runSpecAndExitProcess [ specReporter ] do
|
||||
Test.Form.spec
|
Loading…
Reference in New Issue
Block a user