Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
JWebCoder committed Jun 24, 2020
1 parent 95fee55 commit 0504d84
Show file tree
Hide file tree
Showing 3 changed files with 317 additions and 0 deletions.
7 changes: 7 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
test:
deno test --allow-net init.ts

bundle:
deno bundle worldtides.ts > bundle.js

.PHONY: test bundle
162 changes: 162 additions & 0 deletions worldtides.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
const WORLTIDES_URL = 'https://www.worldtides.info/api/v2'

type datumValues = 'LAT'
| 'MLLWS'
| 'MLWS'
| 'MHLWS'
| 'MLLW'
| 'MLW'
| 'MHLW'
| 'MLLWN'
| 'MLWN'
| 'MHLWN'
| 'MTL'
| 'MSL'
| 'MLHWN'
| 'MHWN'
| 'MHHWN'
| 'MLHW'
| 'MHW'
| 'MHHW'
| 'MLHWS'
| 'MHWS'
| 'MHHWS'
| 'HAT'

type plot = {
width?: number
height?: number
fontSize?: number
grid?: 'none' | 'course' | 'fine'
color?: string
background?: string
}

type parameters = {
key?: string
heights?: boolean,
extremes?: boolean,
date?: Date,
days?: 1|2|3|4|5|6|7,
datum?: datumValues,
datums?: boolean,
lat?: number,
lon?: number,
plot?: plot | boolean,
stationDistance?: number,
stations?: boolean,
start?: number,
length?: number,
step?: number,
}

type apiResponse = {
status: number,
error?: string,
requestDatum?: datumValues,
responseDatum?: datumValues,
requestLat: number,
requestLon: number,
responseLat: number,
responseLon: number,
callCount: number,
atlas: string,
station?: string,
copyright: string,
plot?: string,
stations?: {
id: string,
name: string,
lat: number,
lon: number,
timezone: string,
}[],
heights: {
dt: number,
date: string,
height: number,
}[],
extremes: {
dt: number,
date: string,
height: number,
type: 'Low' | 'High'
}[],
datums: {
name: string,
height: number
}[]
}

export class Worldtides {
parameters: parameters
constructor(parameters: parameters & { key: string }) {
this.parameters= parameters
}

public setParameters(parameters: parameters) {
this.parameters = {
...this.parameters,
...parameters
}
}

public async request(parameters: parameters): Promise<apiResponse> {
const url = this.buildUrl(parameters)
console.log('url', url)
const result = await fetch(
url
)
return result.json()
}

private buildUrl(parameters: parameters): string {
if (!parameters.key && !this.parameters.key) {
throw new Error('API key required')
}
if (parameters.lat === undefined && this.parameters.lat === undefined) {
throw new Error('Lat required')
}
if (!parameters.lon === undefined && !this.parameters.lon === undefined) {
throw new Error('Lon required')
}
const finalParameters = {
...this.parameters,
...parameters
}

finalParameters.lat = parameters.lat === undefined ? this.parameters.lat : parameters.lat
finalParameters.lon = parameters.lon === undefined ? this.parameters.lon : parameters.lon
let url = WORLTIDES_URL

return `${url}?${this.serializeQueryString(finalParameters)}`
}

private serializeQueryString(obj: parameters | plot): string {
return Object.entries(obj).map(
([key, value]) => {
if (value === undefined) {
return
}
if (value instanceof Date) {
let month: number | string = value.getMonth() + 1
if (month < 10) {
month = '0' + month
}
let day: number | string = value.getDate() + 1
if (day < 10) {
day = '0' + day
}
value = `${value.getFullYear()}-${month}-${day}`
}
else if (typeof value === 'object') {
return this.serializeQueryString(value)
}
if (value === true) {
return key
}
return `${key}=${encodeURIComponent(value)}`
}
).filter(e => e).join('&')
}
}
148 changes: 148 additions & 0 deletions worldtides_test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { Worldtides } from './worldtides.ts'
import {
assert,
assertEquals,
} from "https://deno.land/std@v0.57.0/testing/asserts.ts"

const apikey = Deno.env.get('apikey')

if (!apikey) {
throw new Error('missing api key')
}

const worldtides = new Worldtides({
key: apikey
})

export async function test(name: string, fn: () => void | Promise<void>) {
async function wrapped() {
if (existsSync(afterFile)) {
Deno.removeSync(afterFile)
}
try {
await fn();
if (existsSync(afterFile)) {
const decoder = new TextDecoder('utf-8')
const data = Deno.readFileSync(afterFile)
console.log(decoder.decode(data))
}
} finally {
if (existsSync(afterFile)) {
Deno.removeSync(afterFile)
}
}
}
Deno.test({ name, fn: wrapped })
}

test("server is created", async () => {
const { default: app } = await import('./app_test.ts')
assert(app, 'is not created')
})

test("simple server setup test", async () => {
const { default: app } = await import('./app_test.ts')
app.listen({ port: 8000})
const response = await fetch(
'http://localhost:8000',
{
method: 'POST',
body: JSON.stringify({
type: 'json'
}),
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}
}
)
const result = await response.json()
app.close()
assertEquals(result.test, 'json')
assertEquals(result.before, true)
})

test("urlenconded request", async () => {
const { default: app } = await import('./app_test.ts')
app.listen({ port: 8000})
const response = await fetch(
'http://localhost:8000',
{
method: 'POST',
body: 'type=urlencoded',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
)
const result = await response.json()
app.close()
assertEquals(result.test, 'urlencoded')
})

test("test request", async () => {
const { default: app } = await import('./app_test.ts')
app.listen({ port: 8000})
const response = await fetch(
'http://localhost:8000',
{
method: 'POST',
body: 'text',
headers: {
'Content-Type': 'text/plain'
}
}
)
const result = await response.json()
app.close()
assertEquals(result.test, 'text')
})

test("request with error", async () => {
const { default: app } = await import('./app_test.ts')
app.listen({ port: 8000})
const response = await fetch(
'http://localhost:8000',
{
method: 'POST',
body: JSON.stringify({
type: 'error'
}),
headers: {
'Content-Type': 'application/json'
}
}
)
const result = await response.text()
app.close()
assertEquals(result, 'error')
})

test("redirect", async () => {
const { default: app } = await import('./app_test.ts')
app.listen({ port: 8000})
const response = await fetch(
'http://localhost:8000',
{
method: 'POST',
body: JSON.stringify({
type: 'redirect'
}),
headers: {
'Content-Type': 'application/json'
},
redirect: 'manual'
}
)
app.close()
assertEquals(response.type, 'opaqueredirect')
})

test("application closes", async () => {
const { default: app } = await import('./app_test.ts')
app.listen({ port: 8000})
await app.close()
if (app.server) {
assert((await app.server?.[Symbol.asyncIterator]().next()).done)
}
})

0 comments on commit 0504d84

Please sign in to comment.