Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v0.2.5 #33

Merged
merged 5 commits into from
Mar 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions addon/adapters/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { get } from '@ember/object';
import { isBlank } from '@ember/utils';
import { dasherize } from '@ember/string';
import { pluralize } from 'ember-inflector';
import { decompress as decompressJson } from 'compress-json';
import getUserOptions from '../utils/get-user-options';
import config from 'ember-get-config';

Expand Down Expand Up @@ -161,14 +162,37 @@ export default class ApplicationAdapter extends RESTAdapter {
* It then checks if the response is invalid based on the status code. If invalid, it constructs an `AdapterError` with the normalized errors and detailed message.
* For valid responses, it delegates the handling to the superclass's `handleResponse` method.
*/
handleResponse(status, headers, payload) {
async handleResponse(status, headers, payload, requestData) {
let decompressedPayload = this.decompressPayload(payload, headers);
let errors = this.normalizeErrorResponse(status, headers, payload);
let detailedMessage = this.generatedDetailedMessage(...arguments);

if (this.isInvalid(status, headers, payload)) {
return new AdapterError(errors, detailedMessage);
return new AdapterError(errors);
}

return super.handleResponse(status, headers, decompressedPayload, requestData);
}

/**
* Decompresses the response payload if it's marked as compressed in the response headers.
*
* This method checks the response headers for a specific 'x-compressed-json' flag.
* If this flag is set, indicating that the response payload is compressed, the method
* decompresses the payload. The decompressed payload is then parsed as JSON and returned.
* If the payload is not compressed, it is returned as is.
*
* @param {object} payload - The original payload of the response.
* @param {object} headers - The headers of the response, used to check if the payload is compressed.
* @return {object} The decompressed payload if it was compressed, or the original payload otherwise.
*/
async decompressPayload(payload, headers) {
// Check if the response is compressed
if (headers['x-compressed-json'] === '1' || headers['x-compressed-json'] === 1) {
// Decompress the payload
const decompressedPayload = decompressJson(payload);
// Replace payload with decompressed json payload
payload = JSON.parse(decompressedPayload);
}

return super.handleResponse(...arguments);
return payload;
}
}
11 changes: 8 additions & 3 deletions addon/initializers/load-socketcluster-client.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
export function initialize() {
const socketClusterClientScript = document.createElement('script');
socketClusterClientScript.src = '/assets/socketcluster-client.min.js';
document.body.appendChild(socketClusterClientScript);
// Check if the script already exists
// Only insert the script tag if it doesn't already exist
if (!document.querySelector('script[data-socketcluster-client]')) {
const socketClusterClientScript = document.createElement('script');
socketClusterClientScript.setAttribute('data-socketcluster-client', '1');
socketClusterClientScript.src = '/assets/socketcluster-client.min.js';
document.body.appendChild(socketClusterClientScript);
}
}

export default {
Expand Down
3 changes: 2 additions & 1 deletion addon/services/app-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ export default class AppCacheService extends Service {
@storageFor('local-cache') localCache;

get cachePrefix() {
return `${this.currentUser.id}:${this.currentUser.companyId}:`;
const userId = this.currentUser.id ?? 'anon';
return `${userId}:${this.currentUser.companyId}:`;
}

@action setEmberData(key, value, except = []) {
Expand Down
11 changes: 11 additions & 0 deletions addon/services/current-user.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import Service from '@ember/service';
import Evented from '@ember/object/evented';
import { inject as service } from '@ember/service';
import { tracked } from '@glimmer/tracking';
import { dasherize } from '@ember/string';
import { computed, get, action } from '@ember/object';
import { isBlank } from '@ember/utils';
Expand Down Expand Up @@ -43,6 +44,16 @@ export default class CurrentUserService extends Service.extend(Evented) {
*/
@service notifications;

/**
* Property to hold loaded user.
*
* @var {UserModel|Object}
* @memberof CurrentUserService
*/
@tracked user = {
id: 'anon',
};

/**
* User options in localStorage
*
Expand Down
42 changes: 26 additions & 16 deletions addon/services/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { singularize, pluralize } from 'ember-inflector';
import { task } from 'ember-concurrency';
import { storageFor } from 'ember-local-storage';
import { intervalToDuration, parseISO } from 'date-fns';
import { decompress as decompressJson } from 'compress-json';
import config from 'ember-get-config';
import corslite from '../utils/corslite';
import getMimeType from '../utils/get-mime-type';
Expand Down Expand Up @@ -226,22 +227,31 @@ export default class FetchService extends Service {
*
* @return {Promise}
*/
parseJSON(response) {
return new Promise((resolve, reject) =>
response
.json()
.then((json) =>
resolve({
statusText: response.statusText,
status: response.status,
ok: response.ok,
json,
})
)
.catch(() => {
reject(new Error('Oops! Something went wrong when handling your request.'));
})
);
async parseJSON(response) {
try {
const compressedHeader = await response.headers.get('x-compressed-json');
let json;

if (compressedHeader === '1') {
// Handle compressed json
const text = await response.text();
json = JSON.parse(text);
json = decompressJson(json);
json = JSON.parse(json);
} else {
// Handle regular json
json = await response.json();
}

return {
statusText: response.statusText,
status: response.status,
ok: response.ok,
json,
};
} catch (error) {
throw new Error('Error processing response: ' + error.message);
}
}

/**
Expand Down
2 changes: 1 addition & 1 deletion addon/services/theme.js
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export default class ThemeService extends Service {
setTheme(theme = 'light') {
document.body.classList.remove(`${this.currentTheme}-theme`);
document.body.classList.add(`${theme}-theme`);
this.currentUser.setOption(`theme`, theme);
this.currentUser.setOption('theme', theme);
this.activeTheme = theme;
}

Expand Down
49 changes: 49 additions & 0 deletions addon/services/universe.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ export default class UniverseService extends Service.extend(Evented) {
name: registryName,
menuItems: [],
menuPanels: [],
renderableComponents: [],
...options,
};

Expand Down Expand Up @@ -426,6 +427,26 @@ export default class UniverseService extends Service.extend(Evented) {
return [];
}

/**
* Retrieves renderable components from a specified registry.
* This action checks the internal registry, identified by the given registry name,
* and returns the 'renderableComponents' if they are present and are an array.
*
* @action
* @param {string} registryName - The name of the registry to retrieve components from.
* @returns {Array} An array of renderable components from the specified registry, or an empty array if none found.
*/
@action getRenderableComponentsFromRegistry(registryName) {
const internalRegistryName = this.createInternalRegistryName(registryName);
const registry = this[internalRegistryName];

if (!isBlank(registry) && isArray(registry.renderableComponents)) {
return registry.renderableComponents;
}

return [];
}

/**
* Loads a component from the specified registry based on a given slug and view.
*
Expand Down Expand Up @@ -550,6 +571,34 @@ export default class UniverseService extends Service.extend(Evented) {
});
}

/**
* Registers a renderable component or an array of components into a specified registry.
* If a single component is provided, it is registered directly.
* If an array of components is provided, each component in the array is registered individually.
* The component is also registered into the specified engine.
*
* @param {string} engineName - The name of the engine to register the component(s) into.
* @param {string} registryName - The registry name where the component(s) should be registered.
* @param {Object|Array} component - The component or array of components to register.
*/
registerRenderableComponent(engineName, registryName, component) {
if (isArray(component)) {
component.forEach((_) => this.registerRenderableComponent(registryName, _));
return;
}

// register component to engine
this.registerComponentInEngine(engineName, component);

// register to registry
const internalRegistryName = this.createInternalRegistryName(registryName);
if (isArray(this[internalRegistryName].renderableComponents)) {
this[internalRegistryName].renderableComponents.pushObject(component);
} else {
this[internalRegistryName].renderableComponents = [component];
}
}

/**
* Registers a new menu panel in a registry.
*
Expand Down
4 changes: 2 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ module.exports = {

if (app.options['ember-cli-notifications'] !== undefined) {
app.options['ember-cli-notifications'].autoClear = true;
app.options['ember-cli-notifications'].clearDuration = 1000 * 5;
app.options['ember-cli-notifications'].clearDuration = 1000 * 3.5;
} else {
app.options['ember-cli-notifications'] = {
autoClear: true,
clearDuration: 1000 * 5,
clearDuration: 1000 * 3.5,
};
}
},
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@fleetbase/ember-core",
"version": "0.2.4",
"version": "0.2.5",
"description": "Provides all the core services, decorators and utilities for building a Fleetbase extension for the Console.",
"keywords": [
"fleetbase-core",
Expand Down Expand Up @@ -35,6 +35,7 @@
},
"dependencies": {
"@babel/core": "^7.23.2",
"compress-json": "^3.0.0",
"date-fns": "^2.30.0",
"ember-auto-import": "^2.6.3",
"ember-cli-babel": "^8.2.0",
Expand Down Expand Up @@ -71,13 +72,13 @@
"ember-cli-inject-live-reload": "^2.1.0",
"ember-cli-sri": "^2.1.1",
"ember-cli-terser": "^4.0.2",
"ember-data": "^4.12.5",
"ember-file-upload": "8.4.0",
"ember-load-initializers": "^2.1.2",
"ember-page-title": "^8.0.0",
"ember-qunit": "^8.0.1",
"ember-resolver": "^11.0.1",
"ember-source": "~5.4.0",
"ember-data": "^4.12.5",
"ember-source-channel-url": "^3.0.0",
"ember-template-lint": "^5.11.2",
"ember-try": "^3.0.0",
Expand Down
Loading
Loading