Skip to content

Commit

Permalink
feat: support action queries with prefixed resource names (#19)
Browse files Browse the repository at this point in the history
* feat: support action queries with prefixed resource names

* chore: remove test export

* chore: use path.join and update tests

* fix: use joinPath and update babel deps
  • Loading branch information
amcgee authored Jul 9, 2019
1 parent 2bb7aac commit c5c5dc7
Show file tree
Hide file tree
Showing 10 changed files with 4,694 additions and 472 deletions.
4 changes: 2 additions & 2 deletions runtime/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"build/**"
],
"devDependencies": {
"@babel/core": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"@babel/core": "^7.5.0",
"@babel/preset-env": "^7.5.2",
"@dhis2/app-service-data": "1.2.0",
"loop": "^3.1.3",
"rollup": "^1.9.0",
Expand Down
2 changes: 1 addition & 1 deletion services/data/babel.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module.exports = function(api) {
api.cache.forever()
api.cache.never()

const presets = ['@babel/preset-react', ['@babel/preset-typescript']]

Expand Down
16 changes: 8 additions & 8 deletions services/data/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@
"build/**"
],
"devDependencies": {
"@babel/cli": "^7.2.3",
"@babel/core": "^7.4.0",
"@babel/plugin-proposal-class-properties": "^7.4.0",
"@babel/plugin-proposal-object-rest-spread": "^7.4.0",
"@babel/plugin-transform-runtime": "^7.4.0",
"@babel/preset-env": "^7.4.1",
"@babel/cli": "^7.5.0",
"@babel/core": "^7.5.0",
"@babel/plugin-proposal-class-properties": "^7.5.0",
"@babel/plugin-proposal-object-rest-spread": "^7.5.2",
"@babel/plugin-transform-runtime": "^7.5.0",
"@babel/preset-env": "^7.5.2",
"@babel/preset-react": "^7.0.0",
"@babel/preset-typescript": "^7.3.3",
"@babel/runtime": "^7.4.0",
"@babel/runtime": "^7.5.2",
"@testing-library/react": "^8.0.4",
"@types/jest": "^24.0.11",
"@types/node": "^12.0.2",
"@types/react": "^16.8.8",
Expand All @@ -29,7 +30,6 @@
"jest-dom": "^3.1.3",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-testing-library": "^8.0.1",
"rimraf": "^2.6.3",
"ts-jest": "^24.0.0",
"typescript": "^3.3.4000"
Expand Down
2 changes: 1 addition & 1 deletion services/data/src/__tests__/integration.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import { useQuery } from '..'
import { render, waitForElement } from 'react-testing-library'
import { render, waitForElement } from '@testing-library/react'
import { CustomProvider } from '../components/CustomProvider'
import { Query } from '../components/Query'
import { QueryRenderInput } from '../types/Query'
Expand Down
8 changes: 5 additions & 3 deletions services/data/src/context/makeContext.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { fetchData } from '../utils/networkFetch'
import { joinPath } from '../utils/path'
import { ContextType, ContextInput } from '../types/Context'
import { queryToResourcePath } from '../utils/queryToResourcePath'
import { queryToResourceUrl } from '../utils/queryToResourceUrl'

export const makeContext = ({
baseUrl,
apiVersion,
}: ContextInput): ContextType => {
const apiUrl = joinPath(baseUrl, 'api', String(apiVersion))
return {
const context: ContextType = {
baseUrl,
apiVersion,
apiUrl,
fetch: (query, options) =>
fetchData(joinPath(apiUrl, queryToResourcePath(query)), options),
fetchData(joinPath(queryToResourceUrl(query, context)), options),
}

return context
}
2 changes: 1 addition & 1 deletion services/data/src/setupRTL.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'jest-dom/extend-expect'
import 'react-testing-library/cleanup-after-each'
import '@testing-library/react/cleanup-after-each'

process.on('unhandledRejection', err => {
throw err
Expand Down
72 changes: 0 additions & 72 deletions services/data/src/utils/queryToResourcePath.test.ts

This file was deleted.

115 changes: 115 additions & 0 deletions services/data/src/utils/queryToResourceUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { queryToResourceUrl } from './queryToResourceUrl'
import { QueryDefinition } from '../types/Query'
import { ContextType } from '../types/Context'

const baseUrl = '<base>'
const apiVersion = 42
const apiUrl = '<api>'
const context: ContextType = {
baseUrl,
apiVersion,
apiUrl,
fetch: async () => ({}),
}

const actionPrefix = `${baseUrl}/dhis-web-commons/`
const actionPostfix = '.action'

describe('queryToResourceUrl', () => {
describe('action', () => {
it('should return action URL with no querystring if not query parameters are passed', () => {
const query: QueryDefinition = {
resource: 'action::test',
}
expect(queryToResourceUrl(query, context)).toBe(
`${actionPrefix}test${actionPostfix}`
)
})
it('should return action URL with a simple querystring if query parameters are passed', () => {
const query: QueryDefinition = {
resource: 'action::test',
key: 'value',
}
expect(queryToResourceUrl(query, context)).toBe(
`${actionPrefix}test${actionPostfix}?key=value`
)
})
})
it('should return resource url with no querystring if not query parameters are passed', () => {
const query: QueryDefinition = {
resource: 'test',
}
expect(queryToResourceUrl(query, context)).toBe(`${apiUrl}/test`)
})
it('should return resource url and singular parameter separated by ?', () => {
const query: QueryDefinition = {
resource: 'test',
key: 'value',
}
expect(queryToResourceUrl(query, context)).toBe(
`${apiUrl}/test?key=value`
)
})
it('should return resource url and multiple parameters separated by ? and &', () => {
const query: QueryDefinition = {
resource: 'test',
key: 'value',
param: 'value2',
}
expect(queryToResourceUrl(query, context)).toBe(
`${apiUrl}/test?key=value&param=value2`
)
})
it('should url encode special characters in query keys', () => {
const query: QueryDefinition = {
resource: 'test',
'key=42&val': 'value',
}
expect(queryToResourceUrl(query, context)).toBe(
`${apiUrl}/test?key%3D42%26val=value`
)
})
it('should url encode special characters in string parameters', () => {
const query: QueryDefinition = {
resource: 'test',
key: 'value?=42',
param: 'value2&& 53',
}
expect(queryToResourceUrl(query, context)).toBe(
`${apiUrl}/test?key=value%3F%3D42&param=value2%26%26%2053`
)
})
it('should support numeric (integer and float) parameters', () => {
const query: QueryDefinition = {
resource: 'test',
key: 42,
param: 193.75,
}
expect(queryToResourceUrl(query, context)).toBe(
`${apiUrl}/test?key=42&param=193.75`
)
})
it('should join array parameters with commas', () => {
const query: QueryDefinition = {
resource: 'test',
key: ['asdf', 123],
}
expect(queryToResourceUrl(query, context)).toBe(
`${apiUrl}/test?key=asdf,123`
)
})
it('should NOT YET support name-aliased parameters', () => {
const query: QueryDefinition = {
resource: 'test',
key: { asdf: 'fdsa' },
}
expect(() => queryToResourceUrl(query, context)).toThrow()
})
it('should throw if passed something crazy like a function', () => {
const query: QueryDefinition = {
resource: 'test',
key: ((a: any) => a) as any,
}
expect(() => queryToResourceUrl(query, context)).toThrow()
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import {
QueryParameters,
QueryParameterValue,
} from '../types/Query'
import { ContextInput, ContextType } from '../types/Context'
import { joinPath } from './path'

const encodeQueryParameter = (param: QueryParameterValue): string => {
if (Array.isArray(param)) {
Expand Down Expand Up @@ -30,12 +32,26 @@ const queryParametersToQueryString = (params: QueryParameters) =>
)
.join('&')

export const queryToResourcePath = ({
resource,
...params
}: QueryDefinition): string => {
const actionPrefix = 'action::'

const isAction = (resource: string) => resource.startsWith(actionPrefix)
const makeActionURL = (baseUrl: string, resource: string) =>
joinPath(
baseUrl,
'dhis-web-commons',
`${resource.substr(actionPrefix.length)}.action`
)

export const queryToResourceUrl = (
{ resource, ...params }: QueryDefinition,
{ baseUrl, apiUrl }: ContextType
): string => {
const base = isAction(resource)
? makeActionURL(baseUrl, resource)
: joinPath(apiUrl, resource)

if (Object.keys(params).length) {
return `${resource}?${queryParametersToQueryString(params)}`
return `${base}?${queryParametersToQueryString(params)}`
}
return resource
return base
}
Loading

0 comments on commit c5c5dc7

Please sign in to comment.