I am new to FP-TS and still don't quite understand how to work with TaskEither. I am attempting to asynchronously read a file and then parse the resulting string with yaml-parse-promise.
==EDIT==
I updated the code with the full contents of the file to give more context and applied some of the suggestions provided by MnZrK. Sorry I am still new to FP-TS and I am still struggling with getting the types to match up.
Now my error is with the the map(printConfig) line:
Argument of type '<E>(fa: TaskEither<E, AppConfig>) => TaskEither<E, AppConfig>' is not assignable to parameter of type '(a: TaskEither<unknown, AppConfig>) => Either<unknown, Task<any>>'.
Type 'TaskEither<unknown, AppConfig>' is not assignable to type 'Either<unknown, Task<any>>'.
Type 'TaskEither<unknown, AppConfig>' is missing the following properties from type 'Right<Task<any>>': _tag, rightts(2345)
[ I resolved this by using the getOrElse from TaskEither rather than from Either library]
==END EDIT==
I have successfully performed this with IOEither as a synchronous operation with this project: https://github.com/anotherhale/fp-ts_sync-example.
I have also looked at the example code here:
https://gcanti.github.io/fp-ts/recipes/async.html
Full code is here: https://github.com/anotherhale/fp-ts_async-example
import { pipe } from 'fp-ts/lib/pipeable'
import { TaskEither, tryCatch, chain, map, getOrElse } from "fp-ts/lib/TaskEither";
import * as T from 'fp-ts/lib/Task';
import { promises as fsPromises } from 'fs';
const yamlPromise = require('js-yaml-promise');
// const path = require('path');
export interface AppConfig {
service: {
interface: string
port: number
};
}
function readFileAsyncAsTaskEither(path: string): TaskEither<unknown, string> {
return tryCatch(() => fsPromises.readFile(path, 'utf8'), e => e)
}
function readYamlAsTaskEither(content: string): TaskEither<unknown, AppConfig> {
return tryCatch(() => yamlPromise.safeLoad(content), e => e)
}
// function getConf(filePath:string){
// return pipe(
// readFileAsyncAsTaskEither(filePath)()).then(
// file=>pipe(file,foldE(
// e=>left(e),
// r=>right(readYamlAsTaskEither(r)().then(yaml=>
// pipe(yaml,foldE(
// e=>left(e),
// c=>right(c)
// ))
// ).catch(e=>left(e)))
// ))
// ).catch(e=>left(e))
// }
function getConf(filePath: string): TaskEither<unknown, AppConfig> {
return pipe(
readFileAsyncAsTaskEither(filePath),
chain(readYamlAsTaskEither)
)
}
function printConfig(config: AppConfig): AppConfig {
console.log("AppConfig is: ", config);
return config;
}
async function main(filePath: string): Promise<void> {
const program: T.Task<void> = pipe(
getConf(filePath),
map(printConfig),
getOrElse(e => {
return T.of(undefined);
})
);
await program();
}
main('./app-config.yaml')
The resulting output is:
{ _tag: 'Right', right: Promise { <pending> } }
But I want the resulting AppConfig:
{ service: { interface: '127.0.0.1', port: 9090 } }
All these e=>left(e) and .catch(e=>left(e)) are unnecessary.
Your second approach is more idiomatic.
// convert nodejs-callback-style function to function returning TaskEither
const readFile = taskify(fs.readFile);
// I don't think there is `taskify` alternative for Promise-returning functions but you can write it yourself quite easily
const readYamlAsTaskEither = r => tryCatch(() => readYaml(r), e => e);
function getConf(filePath: string): TaskEither<unknown, AppConfig> {
return pipe(
readFile(path.resolve(filePath)),
chain(readYamlAsTaskEither)
);
}
Now your getConf returns TaskEither<unknown, AppConfig> which is actually a () => Promise<Either<unknown, AppConfig>>. If you have more specific error type than unknown, then use that instead.
In order to "unpack" the actual value, you need to have some main entry point function where you compose other stuff you need to do with your config using map or chain (ie printing it to console), then apply some error handling to get rid of Either part and finally get just Task (which is actually simply lazy () => Promise):
import * as T from 'fp-ts/lib/Task';
function printConfig(config: AppConfig): AppConfig {
console.log("AppConfig is", config);
return config;
}
function doSomethingElseWithYourConfig(config: AppConfig): TaskEither<unknown, void> {
// ...
}
async function main(filePath: string): Promise<void> {
const program: T.Task<void> = pipe(
getConf(filePath),
map(printConfig),
chain(doSomethingElseWithYourConfig),
// getting rid of `Either` by using `getOrElse` or `fold`
getOrElse(e => {
// error handling (putting it to the console, sending to sentry.io, whatever is needed for you app)
// ...
return T.of(undefined);
})
);
await program();
}
Related
I will try to keep my problem as simple as possible, I have this function that I created:
get_total_by_status(status: string){
const total = imports.index(status).then((d) => {
return d.total
})
return total
}
and I'm calling this function like this:
var status_published = this.get_total_by_status("pending payment")
but it's not working, I have put a console.log(total) inside the function, and I got this:
Promise {<pending>}
[[Prototype]]:Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: 202
how can I return the 202 value??
I know that there are tons of questions similar to this one, and basically, all of them tell me to add async() at the function, and a await on the moment I call the function, I have tried this without success, and I have no idea what I'm missing here, I'm a python programer with 0 knowledge in react
Edit:
Tried this approach, still can't return the value
get_total_by_status(status: string, onSuccess) {
imports.index(status).then(
(d) => {
onSuccess(d.total);
}
);
}
# STUFFF
const status_published = this.get_total_by_status("published",
(response) => {
return response //also tried status_published = response
})
if I place a console.log(response) I do indeed can log the 202 that I was expecting, but status_published keeps being null
There are two options.
Option 1. Wrap your get_total_by_status call in an async function and await your get_total_by_status to get resolved value as follows:
async function get_total_by_status(status) {
const total = imports.index(status).then(
(d) => {
return d.total;
}
);
return total;
}
async function getPromiseValue() {
let status_published = await get_total_by_status("My status");
console.log(status_published);
}
getPromiseValue(); // prints "My status"
Option 2. Another option would be pass callback to your get_total_by_status function as an argument and call that callback in .then() block as follows:
function get_total_by_status(status, onSuccess) {
// you don't need to store any return value in this case
imports.index(status).then(
(d) => {
onSuccess(d.total);
}
);
}
get_total_by_status("My status", (response) => {
console.log(response); // prints "My status"
});
Let me know if you have any kind of query or doubt regarding above code snippets :)
I'm new to NextJS, and trying to figure out, how to create a global variable that I could assign a different value anytime. Could someone give a simple example? (I know global might not be the best approach, but still I would like to know how to set up a global variable).
Let's say:
_app.js
NAME = "Ana" // GLOBAL VARIABLE
page_A.js
console.log(NAME) // "Ana"
NAME = "Ben"
page_B.js
console.log(NAME) // "Ben"
try using Environment Variables
/next.config.js
module.exports = {
env: {
customKey: 'my-value',
},
}
/pages/page_A.js
function Page() {
return <h1>The value of customKey is: {process.env.customKey}</h1>
}
export default Page
but you can not change its contents, except by changing it directly in next.config.js
Nextjs no special ways to provide global variables you want. You can achieve by:
Stateful management tool, like redux-react
Using Context
It's not like it's impossible,
I created a file called _customGlobals.jsx and put this as content
String.prototype.title = function () {
const sliced = this.slice(1);
return (
this.charAt(0).toUpperCase() +
(sliced.toUpperCase() === sliced ? sliced.toLowerCase() : sliced)
);
};
and imported it in _app.jsx like this:
import "./_customGlobals";
So now I can call this function on any string anywhere in my project like this:
"this is a title".title()
Database designed for this purpose. But for one var it's not wise to install whole db!
So, you can do it in a JSON file.
Add a var to a JSON file and use a function to update it.
this is a simple function for this usage:
const fs = require('fs');
function updateJSONFile(filePath, updates) {
fs.readFile(filePath, 'utf8', function (err, data) {
if (err) {
console.error(err);
return;
}
let json = JSON.parse(data);
for (let key in updates) {
json[key] = updates[key];
}
fs.writeFile(filePath, JSON.stringify(json, null, 2), 'utf8', function (err) {
if (err) {
console.error(err);
}
});
});
}
So, use it like this:
updateJSONFile('file.json', { name: 'John Doe', age: 30 });
You can create another function to read it dynamicly:
function readJSONFile(filePath) {
fs.readFile(filePath, 'utf8', function (err, data) {
if (err) {
return callback(err);
}
let json;
try {
json = JSON.parse(data);
} catch (error) {
return callback(error);
}
return callback(null, json);
});
}
and you can use it like this:
const readedFile = readJSONFile('file.json')
I deleted the callback function to have a simple code but you can add callback function to log error messages.
Hi I am building function to recursively "denest" a object of following interface:
export interface IUnit {
code: string
artifacts: IArtifact[]
units: IUnit[]
}
The idea is that I have a separate array that I returns with every immersion and after each return the returned array is "pushed" to local array and so on...
the function is as following:
const denestList = async (incomingUnit: IUnit): Promise<{ allUnits: IUnit[], allArtifacts: IArtifact[] }> => {
var units = [incomingUnit];
var artifacts = [...incomingUnit.artifacts];
//Array is full
console.log(unit.units)
//Array is empty
console.log(unit.units.length)
console.log([...unit.units])
console.log(Array.from(unit.units)?.length)
for (unit of incomingUnit.units) {
console.log(unit.code)
//Recursion happens here.
var result = await denestList(unit)
units.push(...result.allUnits)
artifacts.push(...result.allArtifacts)
}
return { allUnits: units, allArtifacts: artifacts }
}
The problem is that for (unit of incomingUnit.units) never happens. When I log unit.units it shows array full of IArtifact[], but when I run console.log(unit.units.length) it return 0.
Here is how to "denestList" function is called:
useEffect(() => {
asyncStart(unit)
}, []);
const asyncStart = async (mainUnit: IUnit) => {
var result = await denestList(mainUnit);
setAllUnits(result.allUnits)
setAllArtifacts(result.allArtifacts)
}
I would really appreciate any help. Thank you in advance
An error occurs in the following configuration. Please tell me the cause of the error and how to fix it.
■Error message
Error: Cannot determine GraphQL input type for 'zzzzzInputs' of 'XxxxxInput' class. Is the value, that is used as its TS type or explicit type, decorated with a proper decorator or is it a proper input value?
■Reproduction environment and method
Clone
git clone https://github.com/isoittech/hellpj-type-graphql
Execute commands
npm ci
npm run start
■Application/library stack
Node.js/Express
sequelize
sequelize-typescript
type-graphql
■Source
https://github.com/isoittech/hellpj-type-graphql/blob/master/src/main/graphql/ppppp.resolver.ts
#ObjectType()
export class ZzzzzInput {
#Field((type) => ZzzzzType)
zzzzz!: ZzzzzType;
// #Field()
// zzzzz!: string;
}
#InputType()
export class XxxxxInput {
#Field((type) => [ZzzzzInput])
zzzzzInputs!: ZzzzzInput[];
// #Field((type) => [String])
// zzzzzInputs!: string[];
}
#Resolver((of) => Yyyyy)
export class XxxxxResolver {
#Mutation((returns) => Yyyyy)
async addXxxxx(#Arg("Xxxxx") XxxxxInput: XxxxxInput): Promise<Yyyyy> {
const serviceOutput: XxxxxServiceOutputDto = {};
return Promise.resolve(serviceOutput.addedXxxxx!);
}
}
■I coded it by referring to here.
https://typegraphql.com/docs/types-and-fields.html
Cannot determine GraphQL input type for argument named
I solved my problem by modifying like below. Look at '★'.
(And found another one, solved too.)
■Source
// #ObjectType() // ★ Before
#InputType() // ★ After
export class ZzzzzInput {
#Field((type) => ZzzzzType)
zzzzz!: ZzzzzType;
// #Field()
// zzzzz!: string;
}
#InputType()
export class XxxxxInput {
#Field((type) => [ZzzzzInput])
zzzzzInputs!: ZzzzzInput[];
// #Field((type) => [String])
// zzzzzInputs!: string[];
}
#Resolver((of) => Yyyyy)
export class XxxxxResolver {
#Mutation((returns) => Yyyyy)
async addXxxxx(#Arg("Xxxxx") XxxxxInput: XxxxxInput): Promise<Yyyyy> {
const serviceOutput: XxxxxServiceOutputDto = {};
return Promise.resolve(serviceOutput.addedXxxxx!);
}
}
■Related source
node_modules/type-graphql/dist/schema/schema-generator.js
static getGraphQLInputType(target, propertyName, type, typeOptions = {}, parameterIndex, argName) {
let gqlType;
gqlType = types_1.convertTypeIfScalar(type);
if (!gqlType) {
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
★ Finding process below not worked when #ObjectType.
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
const inputType = this.inputTypesInfo.find(it => it.target === type);
if (inputType) {
gqlType = inputType.type;
}
}
if (!gqlType) {
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
☆ Finding process below not worked when wrong order in src/index.ts.
☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆
const enumType = this.enumTypesInfo.find(it => it.enumObj === type);
if (enumType) {
gqlType = enumType.type;
}
}
if (!gqlType) {
throw new errors_1.CannotDetermineGraphQLTypeError("input", target.name, propertyName, parameterIndex, argName);
}
const { nullableByDefault } = build_context_1.BuildContext;
return types_1.wrapWithTypeOptions(target, propertyName, gqlType, typeOptions, nullableByDefault);
}
■Another problem
After I've solved above problem, another one occured. Error message is below:
Error: Cannot determine GraphQL input type for 'zzzzz' of 'ZzzzzInput' class. Is the value, that is used as its TS type or explicit type, decorated with a proper decorator or is it a proper input value?
Look at ☆ in schema-generator.js.
Enum type 'ZzzzzType' couldn't be found, so error occured.
Because this.enumTypesInfo doesn't contain 'ZzzzzType'.
Because I executed registering Enumtype after SchemaGenerating process.
I had to modify below.
// enable Enum
registerEnumType(ZzzzzType, {
name: "ZzzzzType",
});
const schema = await buildSchema({
resolvers: [__dirname + "/graphql/*.resolver.ts"],
emitSchemaFile: true,
validate: false,
});
// // enable Enum
// registerEnumType(ZzzzzType, {
// name: "ZzzzzType",
// });
i have a question using Observables in Silverlight 4 to make WCF calls.
Consider the simple webservice call below.
var adminclient = ServiceProxy.WithFactory<AuthenticationClient>();
var results= Observable.FromEvent<AuthorizeAdministratorCompletedEventArgs>(
s => adminclient.AuthorizeAdministratorCompleted += s,
s => adminclient.AuthorizeAdministratorCompleted -= s).ObserveOnDispatcher();
adminclient.AuthorizeAdministratorAsync();
results.Subscribe(e =>
{
//Enable UI Button
});
i have implemented an extension method, that wraps the subscribe method, it does some error validation on the return.
On the return results.Subscribe(e =>
e is System.Collections.Generic.Event<AuthorizeAdministratorCompletedEventArgs>
almost every query will have a different return type such as:
e is System.Collections.Generic.Event<AsyncCompletedEventArgs>
if i had a wrapper that looked something like this, how can i cast every type of xxxCompletedEventArgs to its base type AsyncCompletedEventArgs so that i can access e.EventArgs and inspect the Error property
public static IDisposable Subscribe<TSource>(this IObservable<TSource> source, Action<TSource> onNext = null, Action onError = null, Action onFinal = null)
{
Action<TSource> onNextWrapper = (s) =>
{
var args = (System.Collections.Generic.IEvent<AsyncCompletedEventArgs>)s;
try
{
if (WCFExceptionHandler.HandleError(args.EventArgs))
{
if (onNext != null)
onNext(s);
}
else
{
if (onError != null)
onError();
}
}
finally
{
if (onFinal != null)
onFinal();
}
};
return source.Subscribe<TSource>(onNextWrapper, (e) => { throw e; });
}
The code above will fail
Unable to cast object of type 'System.Collections.Generic.Event1[MyProject.Provider.AuthorizeAdministratorCompletedEventArgs]' to type 'System.Collections.Generic.IEvent1[System.ComponentModel.AsyncCompletedEventArgs]'
This is the method definition of WCFExceptionHandler.HandleError(args.EventArgs))
public static bool HandleError(AsyncCompletedEventArgs e)
I'd probably change you extension method so that it acts to handle the the events as a non blocking operator (much the same as the majority of the Rx extension method operators). Something like:
public static IObservable<IEvent<TEventArgs>> GetHandledEvents<TEventArgs>(this IObservable<IEvent<TEventArgs>> source)
where TEventArgs : AsyncCompletedEventArgs
{
return Observable.CreateWithDisposable<IEvent<TEventArgs>>(observer =>
{
return source.Subscribe(evt =>
{
try
{
if (WCFExceptionHandler.HandleError(evt.EventArgs))
{
observer.OnNext(evt);
}
else
{
observer.OnError(new Exception("Some Exception"));
}
}
finally
{
observer.OnError(new Exception("Some Other Exception"));
}
},
observer.OnError,
observer.OnCompleted);
});
}
Then call it through:
results.GetHandledEvents()
.Finally(() => DoSomethingFinally())
.Subscribe(e =>
{
//Enable UI Button
},
ex => HandleException(ex),
() => HandleComplete());
I think this should solve your issues as the events will funnel through as their original type and it ensures that HandleError gets event args that are the correct type.