diff --git a/lib/potassium/assets/app/javascript/api/index.ts b/lib/potassium/assets/app/javascript/api/index.ts new file mode 100644 index 00000000..9f44b872 --- /dev/null +++ b/lib/potassium/assets/app/javascript/api/index.ts @@ -0,0 +1,53 @@ +import axios, { type AxiosRequestTransformer, type AxiosResponseTransformer } from 'axios'; +import convertKeys from '../utils/case-converter'; + +const api = axios.create({ + transformRequest: [ + (data: any) => convertKeys(data, 'decamelize'), + ...(axios.defaults.transformRequest as AxiosRequestTransformer[]), + ], + transformResponse: [ + ...(axios.defaults.transformResponse as AxiosResponseTransformer[]), + (data: any) => convertKeys(data, 'camelize'), + ], +}); + +/* +// Example to use the api object in the path ´app/javascript/api/users.ts´ + +import api from './index'; + +export default { + index() { + const path = '/api/internal/users'; + + return api({ + method: 'get', + url: path, + }); + }, + create(data: Partial) { + const path = '/api/internal/users'; + + return api({ + method: 'post', + url: path, + data: { + user: data, + }, + }); + }, + update(data: Partial) { + const path = `/api/internal/users/${data.id}`; + + return api({ + method: 'put', + url: path, + data: { + user: data, + }, + }); + }, +}; + +*/ diff --git a/lib/potassium/assets/app/javascript/utils/case-converter.ts b/lib/potassium/assets/app/javascript/utils/case-converter.ts new file mode 100644 index 00000000..35e655d7 --- /dev/null +++ b/lib/potassium/assets/app/javascript/utils/case-converter.ts @@ -0,0 +1,39 @@ +// From https://github.com/domchristie/humps/issues/51#issuecomment-425113505 +/* eslint-disable complexity */ +/* eslint-disable max-statements */ +import { camelize, decamelize } from 'humps'; + +type objectToConvert = File | FormData | Blob | Record | Array; + +function convertKeys( + object: objectToConvert, + conversion: 'camelize' | 'decamelize', +): objectToConvert { + const converter = { + camelize, + decamelize, + }; + if (object && !(object instanceof File) && !(object instanceof Blob)) { + if (object instanceof Array) { + return object.map((item: objectToConvert) => convertKeys(item, conversion)); + } + if (object instanceof FormData) { + const formData = new FormData(); + for (const [key, value] of object.entries()) { + formData.append(converter[conversion](key), value); + } + + return formData; + } + if (typeof object === 'object') { + return Object.keys(object).reduce((acc, next) => ({ + ...acc, + [converter[conversion](next)]: convertKeys(object[next] as objectToConvert, conversion), + }), {}); + } + } + + return object; +} + +export default convertKeys; diff --git a/lib/potassium/recipes/front_end.rb b/lib/potassium/recipes/front_end.rb index 46f2c71e..b226ff88 100644 --- a/lib/potassium/recipes/front_end.rb +++ b/lib/potassium/recipes/front_end.rb @@ -35,6 +35,7 @@ def create recipe.setup_vue if value == :vue recipe.add_responsive_meta_tag recipe.setup_tailwind + recipe.setup_api_client add_readme_header :webpack end end @@ -139,6 +140,13 @@ def setup_vue end end + def setup_api_client + run "bin/yarn add axios humps" + copy_file '../assets/app/javascript/api/index.ts', 'app/javascript/api/index.ts' + copy_file '../assets/app/javascript/utils/case-converter.ts', + 'app/javascript/utils/case-converter.ts' + end + private def frameworks(framework) diff --git a/spec/features/front_end_spec.rb b/spec/features/front_end_spec.rb index 276f925e..ce1475a3 100644 --- a/spec/features/front_end_spec.rb +++ b/spec/features/front_end_spec.rb @@ -67,5 +67,10 @@ def expect_to_have_tailwind_package_versions it 'includes correct version of vue-loader in package' do expect(node_modules_file).to include("\"vue-loader\": \"#{Potassium::VUE_LOADER_VERSION}\"") end + + it 'includes correct packages for basic api client' do + expect(node_modules_file).to include("\"axios\"") + expect(node_modules_file).to include("\"humps\"") + end end end