New definitions for nat-upnp (#47551)

* Add nat-upnp typings

* Fixed index.d.ts header

* Removed accidental docstring meant to be source comment

* Apply suggestions from code review

Co-authored-by: Piotr Błażejewicz (Peter Blazejewicz) <peterblazejewicz@users.noreply.github.com>

* Add test cases for nat-upnp optional callbacks missing

* Fix linter errors

Co-authored-by: Piotr Błażejewicz (Peter Blazejewicz) <peterblazejewicz@users.noreply.github.com>
This commit is contained in:
Linn Dahlgren 2020-09-15 01:10:04 +02:00 committed by GitHub
parent f03fd4b231
commit 35d8a9f149
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 385 additions and 0 deletions

230
types/nat-upnp/index.d.ts vendored Normal file
View File

@ -0,0 +1,230 @@
// Type definitions for nat-upnp 1.1
// Project: https://github.com/indutny/node-nat-upnp
// Definitions by: SimplyLinn <https://github.com/SimplyLinn>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
/// <reference types="node" />
import { EventEmitter } from 'events';
/**
* Standard options that many options use.
*/
export interface StandardOpts {
public?:
| number
| null
| {
port?: number;
host?: string;
};
private?:
| number
| null
| {
port?: number;
host?: string;
};
protocol?: string;
}
/**
* Raw SSDP/UPNP repsonse
* Entire SSDP/UPNP schema is beyond the scope of these typings.
* Please look up the protol documentation if you wanna do
* lower level communication.
*/
export type RawResponse = Partial<
Record<
string,
{
'@': { 'xmlns:u': string };
[key: string]: unknown;
}
>
>;
/**
* Callback with a generic as result type
*/
export type CB<T> = (err: Error | null, res?: T) => void;
export interface RawService {
serviceType: string;
serviceId: string;
controlURL?: string;
eventSubURL?: string;
SCPDURL?: string;
}
export interface RawDevice {
deviceType: string;
presentationURL: string;
friendlyName: string;
manufacturer: string;
manufacturerURL: string;
modelDescription: string;
modelName: string;
modelNumber: string;
modelURL: string;
serialNumber: string;
UDN: string;
UPC: string;
serviceList?: { service: RawService | RawService[] };
deviceList?: { device: RawDevice | RawDevice[] };
}
export interface Device {
/**
* Get the available services on the network device
* @param types List of service types to lookf or
* @param callback
*/
getService(
types: string[],
callback: CB<{
service: string;
SCPDURL: string;
controlURL: string;
}>,
): void;
/**
* Parse out available services
* and devices from a root device
* @param info
* @returns the available devices and services in array form
*/
parseDescription(info: {
device?: RawDevice;
}): {
services: RawService[];
devices: RawDevice[];
};
/**
* Perform a SSDP/UPNP request
* @param action the action to perform
* @param args arguments of said action
* @param callback Callback to be run when completed, or on error
*/
run(action: string, args: string[], callback: CB<RawResponse>): void;
}
// Note for the SSDP class/interface
// The implementation leads me to believe that
// createSockets()
// createSocket(interface)
// parseResponse(response, addr)
// are all private methods.
// and have thusly not been typed in the interface
export interface Ssdp extends EventEmitter {
/**
* Search for a SSDP compatible server on the network
* @param device Search Type (ST) header, specifying which device to search for
* @param promise An existing EventEmitter to emit event on
* @returns The event emitter provided in Promise, or a newly instantiated one.
*/
search(device: string, promise?: EventEmitter): EventEmitter;
/**
* Close all sockets
*/
close(): void;
}
//
// Types for client.
//
export interface NewPortMappingOpts extends StandardOpts {
description?: string;
ttl?: number;
}
export type DeletePortMappingOpts = StandardOpts;
export interface GetMappingOpts {
local?: boolean;
description?: RegExp | string;
}
export interface Mapping {
public: { host: string; port: number };
private: { host: string; port: number };
protocol: string;
enabled: boolean;
description: string;
ttl: number;
local: boolean;
}
/**
* Main client interface.
*/
export interface Client {
/**
* Create a new port mapping
* @param options Options for the new port mapping
* @param [callback] Callback to be run when completed, or on error
*/
portMapping(options: NewPortMappingOpts, callback?: CB<RawResponse>): void;
/**
* Remove a port mapping
* @param options Specify which port mapping to remove
* @param [callback] Callback to be run when completed, or on error
*/
portUnmapping(options: DeletePortMappingOpts, callback?: CB<RawResponse>): void;
/**
* Get a list of existing mappings
* @param callback Callback to be run when completed, or on error
*/
getMappings(callback: CB<Mapping[]>): void;
/**
* Get a list of existing mappings
* @param options Filter mappings based on these options
* @param callback Callback to be run when completed, or on error
*/
getMappings(options: GetMappingOpts, callback: CB<Mapping[]>): void;
/**
* Fetch the external IP from the gateway
* @param callback Callback to be run when completed, or on error
*/
externalIp(callback: CB<string>): void;
/**
* Get the gateway device for communication
* @param callback
*/
findGateway(callback: CB<Device>): void;
/**
* Close the underlaying sockets and resources
*/
close(): void;
}
/**
* Create a NAT-UPNP client
*/
export function createClient(): Client;
/**
* The other creator functions are exported as
* an object with the create function as a property.
*/
export const device: {
/**
* Create a gateway device object with the specified url
* @param url
*/
create(url: string): Device;
};
export const ssdp: {
/**
* Create a Simple Service Discovery Protocol client
*/
create(): Ssdp;
};
/**
* Exported utility function.
*/
export const utils: {
getNamespace(
data: {
'@'?: Record<string, string>;
},
uri: string,
): string;
};

View File

@ -0,0 +1,131 @@
import { ssdp, device, utils, createClient } from 'nat-upnp';
const ssdpClient = ssdp.create();
const promiseEventEmitter = ssdpClient.search('urn:schemas-upnp-org:device:InternetGatewayDevice:1');
promiseEventEmitter.on('device', res => {
console.log('onDevice', res);
});
setTimeout(() => ssdpClient.close(), 100);
utils.getNamespace(
{
'@': {
'xmlns:u': 'urn:schemas-upnp-org:service:WANIPConnection:1',
},
},
'urn:schemas-upnp-org:service:WANIPConnection:1',
);
const Device = device.create('');
console.log(Device);
const NATClient = createClient();
NATClient.findGateway((err, gatewayDevice) => {
if (err || gatewayDevice == null) {
console.error(
'NATClient.findGateway',
err || new Error('Invalid callback data, neither error, nor response provided'),
);
return;
}
gatewayDevice.getService(['test'], (err2, service) => {
if (err2 || service == null) {
console.error(
'gatewayDevice.getService',
err2 || new Error('Invalid callback data, neither error, nor response provided'),
);
return;
}
console.log(service.service, service.SCPDURL, service.controlURL);
});
gatewayDevice.run('action', ['arg'], (err2, res) => {
if (err2 || res == null) {
console.error(
'gatewayDevice.run',
err2 || new Error('Invalid callback data, neither error, nor response provided'),
);
return;
}
console.log(res);
});
});
NATClient.externalIp((err, ip) => {
if (err || ip == null) {
console.error(
'NATClient.externalIp',
err || new Error('Invalid callback data, neither error, nor response provided'),
);
return;
}
console.log(ip);
});
NATClient.getMappings((err, mappings) => {
if (err || mappings == null) {
console.error(
'NATClient.getMappings(cb)',
err || new Error('Invalid callback data, neither error, nor response provided'),
);
return;
}
console.log(mappings.map(m => m));
});
NATClient.getMappings({ local: true, description: /test/ }, (err, mappings) => {
if (err || mappings == null) {
console.error(
'NATClient.getMappings(opts, cb)',
err || new Error('Invalid callback data, neither error, nor response provided'),
);
return;
}
console.log(mappings.map(m => m));
});
NATClient.portMapping({
public: 9998,
private: 2221,
description: 'nat-upnp-test 1',
});
NATClient.portUnmapping({
public: 9998,
private: 2221,
});
NATClient.portMapping(
{
public: 9999,
private: 2222,
description: 'nat-upnp-test 2',
},
(err, res) => {
if (err || res == null) {
console.error(
'NATClient.portMapping',
err || new Error('Invalid callback data, neither error, nor response provided'),
);
return;
}
console.log(res);
},
);
NATClient.portUnmapping(
{
public: 9999,
private: 2222,
},
(err, res) => {
if (err || res == null) {
console.error(
'NATClient.portUnmapping',
err || new Error('Invalid callback data, neither error, nor response provided'),
);
return;
}
console.log(res);
},
);
setTimeout(() => NATClient.close(), 1000);

View File

@ -0,0 +1,23 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": [
"es6"
],
"noImplicitAny": true,
"noImplicitThis": true,
"strictFunctionTypes": true,
"strictNullChecks": true,
"baseUrl": "../",
"typeRoots": [
"../"
],
"types": [],
"noEmit": true,
"forceConsistentCasingInFileNames": true
},
"files": [
"index.d.ts",
"nat-upnp-tests.ts"
]
}

View File

@ -0,0 +1 @@
{ "extends": "dtslint/dt.json" }