Coder Social home page Coder Social logo

pretend's Introduction

pretend

npm GitHub license build codecov renovate badge

A decorator based http webservice client build with typescript (inspired bei feign).

Features

  • Handle REST based webservices
  • Configure a decoder (defaults to JSON)
  • Generic request/response interceptor chain
  • Basic authentication
  • Request parameters (currently on GET requests)
  • Custom headers per method

Usage

Installation

Install as npm package:

npm install pretend --save

Note: To work on node.js (server-side) the fetch must be polyfilled. This could easy be done importing isomorphic-fetch.

API

class Test {

  @Headers('Accept: application/json')
  @Get('/path/{id}', true)
  public async get(id: string, parameters: any) {}

  @Post('/path')
  public async post(body: any) {}

  @Post('/path')
  public async post(@FormData('name') blob: any) {}

  @Put('/path')
  public async put() {}

  @Delete('/path/:id')
  public async delete(id: string) {}

}

async function call() {
  const client = Pretend
                  .builder()
                  .target(Test, 'http://host:port/');
  const result = await client.get('some-id', {'name': 'value'});
}

// Executes a GET request to 'http://host:port/path/some-id?name=value'
call();

Decoders, basicAuthentication and requestInterceptors are all special forms of the more generic interceptors which could be chained per request/response.

// Configure a text based decoder
const client = Pretend.builder()
  .decoder(Pretend.TextDecoder)
  .target(Test, 'http://host:port/');
// Configure basic authentication
const client = Pretend.builder()
  .basicAuthentication('user', 'pass')
  .target(Test, 'http://host:port/');
// Configure a request interceptor
const client = Pretend.builder()
  .requestInterceptor((request) => {
    request.options.headers['X-Custom-Header'] = 'value';
    return request;
  })
  .target(Test, 'http://host:port/');

Interceptors

Multiple interceptors could be added to each builder. The order of interceptor calls will result in a chain of calls like illistrated below:

// Configure a request interceptor
const client = Pretend.builder()
  .interceptor(async (chain, request) => {
    console.log('interceptor 1: request');
    const response = await chain(request);
    console.log('interceptor 1: response');
    return response;
  })
  .interceptor(async (chain, request) => {
    console.log('interceptor 2: request');
    const response = await chain(request);
    console.log('interceptor 2: response');
    return response;
  })
  .target(Test, 'http://host:port/');
             +---------------+    +---------------+
Request ---> |               | -> |               |
             | Interceptor 1 |    | Interceptor 2 | -> HTTP REST call
Response <-- |               | <- |               |
             +---------------+    +---------------+

This leads to the following console output:

interceptor 1: request
interceptor 2: request
interceptor 2: response
interceptor 1: response

Data Mappers

DataMappers could be used to map response structures to TypeScript classes. This is done using the @ResponseType decorator.

class User {
  public name: string;

  constuctor(data: { name: string }) {
    this.name = data.name;
  }
}

class API {
  @Get('/path/{id}')
  @ResponseType(User)
  public async loadUser(id: string): Promise<User> {
    /*
     * `/path/{id}` returns a JSON like this from the server:
     *
     *  {
     *    name: 'some string'
     *  }
     */
  }
}

const client = Pretend.builder().target(API, 'http://host:port/');
const result: User = await client.loadUser(1);

There is a second parameter to the @ResponseType decorator which is a transform function. The input is the server response, the output need to match the class constructor parameters.

Note: The constructor parameters are always an array!

class User {
  public get name(): string {
    return this.data.name;
  }

  constuctor(private data: { name: string }) {}
}

class API {
  @Get('/path/{id}')
  @ResponseType(User, (data) => [
    { name: `${data.firstname} ${data.lastname}` }
  ])
  public async loadUser(id: string): Promise<User> {
    /*
     * `/path/{id}` returns a JSON like this from the server:
     *
     *  {
     *    firstname: 'John',
     *    lastname: 'Doe'
     *  }
     */
  }
}

const client = Pretend.builder().target(API, 'http://host:port/');
const result: User = await client.loadUser(1);

Future ideas / Roadmap

  • Named parameters

pretend's People

Contributors

dependabot[bot] avatar greenkeeper[bot] avatar greenkeeperio-bot avatar knisterpeter avatar marionebl avatar misantronic avatar mosijava avatar murat-mehmet avatar renovate-bot avatar renovate[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

pretend's Issues

append dynamic headers

Currently the only ways to add a header is by using @Header(...) decorator or an interceptor. I am currently in need of generating a header and add it to the request in runtime. I think I will go with interceptor pattern but it would be great to have appendHeaders in GET,POST,.. decorators and add the latest object as header to the request. I checked the code and it seems possible but appendQuery index checks are little obstructive there. If it seems easy for you, could you try to implement please, otherwise I will have a look again in some free time.

Is there any way to define what is being returned from the service (wrapping it into a class)?

Hey everyone first of all thanks for creating this, I was thinking about doing something like this for a project but you guys got it covered! second here comes my question I still don't know if it is possible to define a response type like a class being created from what is returned from the service. I would probably also want to define which path of the object should be pointed to this class this could be a decorator. What do you guys think about all of this? I might be interested in helping this project it is really what I have been doing with Swift using Sourcery :)

Dist folder is not deployed with v4

There is a problem with the latest v4 release. Dist folder is not deployed to npm, that's why we are getting "Cannot find module 'pretend'" error.

Dependency Dashboard

This issue lists Renovate updates and detected dependencies. Read the Dependency Dashboard docs to learn more.

Open

These updates have all been created already. Click a checkbox below to force a retry/rebase of any.

Detected dependencies

github-actions
.github/workflows/build.yml
  • actions/checkout v3
  • actions/setup-node v3
  • actions/checkout v3
  • actions/setup-node v3
npm
package.json
  • @knisterpeter/standard-tslint 1.7.2
  • @types/jest 29.5.10
  • @types/node 20.10.0
  • codecov 3.8.3
  • isomorphic-fetch 3.0.0
  • isomorphic-form-data 2.0.0
  • jest 29.7.0
  • nock 13.2.9
  • prettier 2.8.8
  • rimraf 3.0.2
  • ts-jest 29.1.1
  • tslint 6.1.3
  • typescript 4.9.5
  • isomorphic-fetch 3.0.0
  • isomorphic-form-data 2.0.0

  • Check this box to trigger a request for Renovate to run again on this repository

An in-range update of nock is breaking the build 🚨

Version 9.0.21 of nock just got published.

Branch Build failing 🚨
Dependency nock
Current Version 9.0.20
Type devDependency

This version is covered by your current version range and after updating it in your project the build failed.

As nock is β€œonly” a devDependency of this project it might not break production or downstream projects, but β€œonly” your build or test tools – preventing new deploys or publishes.

I recommend you give this issue a high priority. I’m sure you can resolve this πŸ’ͺ

Status Details
  • ❌ coverage/coveralls Coverage pending from Coveralls.io Details
  • ❌ continuous-integration/travis-ci/push The Travis CI build could not complete due to an error Details

Commits

The new version differs by 6 commits.

  • 5209262 9.0.21
  • 9ffab38 fix: compare falsy header values & numbers correctly
  • 333e8e3 test: match number header values
  • fe00f67 multiple interceptors override headers from unrelated requests
  • 4196cf0 Add an error code to NetConnectNotAllowedError
  • 66cd477 chore(CHANGELOG): v9.0.20

See the full diff

Not sure how things should work exactly?

There is a collection of frequently asked questions and of course you may always ask my humans.


Your Greenkeeper Bot 🌴

ReferenceError: Headers is not defined

Following vscode-github I write my own git tool for a local git service.
I got an error :

ReferenceError: Headers is not defined
    at prepareHeaders 
(/User/XXX/Code/oschina/gitee/node_modules/pretend/src/index.ts:70:19)
    at execute (/User/XXX/Code/oschina/gitee/node_modules/pretend/src/index.ts:111:19)
    at GiteeBlueprint.<anonymous> (/User/XXX/Code/oschina/gitee/node_modules/pretend/src/index.ts:132:14)
    at /User/XXX/Code/oschina/gitee/node_modules/pretend/src/index.ts:169:36
    at processTicksAndRejections (internal/process/task_queues.js:85:5)

Does Header must be set ? Where to set header ?
I set header like this:

  export class GiteeBlueprint implements Gitee {
    @Header('Accept: application/json')
    @Post('/user/repos')
    public createRepository(params: ICreateRepoParam): any { }
  }

Why this lib so dead?

This is the best (and almost only) declarative lib that I could find which works best across all of my projects (Vanilla React, Next.js SSR, Vanilla React Native, Expo managed RN etc). I'm using it quite a while and it's working great. Is there any more popular alternative that people are using? I would like to see this lib go over 10k stars :) Thousands of thanks for taking your time to share this with the world.

Action Required: Fix Renovate Configuration

There is an error with this repository's Renovate configuration that needs to be fixed. As a precaution, Renovate will stop PRs until it is resolved.

Location: package.json
Error type: The renovate configuration file contains some invalid settings
Message: Invalid configuration option: author, Invalid configuration option: files, Invalid configuration option: jest, Invalid configuration option: license, Invalid configuration option: main, Invalid configuration option: name, Invalid configuration option: packageRules[0].@knisterpeter/standard-tslint, Invalid configuration option: packageRules[0].@types/jest, Invalid configuration option: packageRules[0].@types/node, Invalid configuration option: packageRules[0].codecov, Invalid configuration option: packageRules[0].isomorphic-fetch, Invalid configuration option: packageRules[0].isomorphic-form-data, Invalid configuration option: packageRules[0].jest, Invalid configuration option: packageRules[0].nock, Invalid configuration option: packageRules[0].prettier, Invalid configuration option: packageRules[0].rimraf, Invalid configuration option: packageRules[0].shipjs, Invalid configuration option: packageRules[0].ts-jest, Invalid configuration option: packageRules[0].tslint, Invalid configuration option: packageRules[0].typescript, Invalid configuration option: packageRules[1].isomorphic-fetch, Invalid configuration option: packageRules[1].isomorphic-form-data, Invalid configuration option: prettier, Invalid configuration option: renovate, Invalid configuration option: scripts, Invalid configuration option: types, Invalid configuration option: version

Can't declaration method return type

import { Get } from 'pretend';
import { UserEntity } from '../entities/user.entity';

export class UserWebService {

  @Get('user/{id}')
  public async get(id: number): Promise<UserEntity> {}    // <-- error here, can't declaration return type

}

Is there any solution? thanks

Can't call chain more than once in interceptor

If we create an interceptor and call the chain more than once, we get an error:

        let response = await chain(request);
        response2 = await chain(request);
...
[TypeError: interceptors[i++] is not a function. (In 'interceptors[i++](chainStep, request)', 'interceptors[i++]' is undefined)]

I need to call chain twice in my auth interceptor, if the token is expired, I refresh token and reissue the request. This functionality was working great in android java ok-http interceptors. I think we can fix this with a little tweak in code. I will send a PR soon.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.