From 5222b6e462b812b7efda03ba8d2bd0ba3dde1442 Mon Sep 17 00:00:00 2001 From: Oksana Melnikova Date: Thu, 18 Apr 2024 00:26:24 +0900 Subject: [PATCH 01/16] (#454) updated filter for shared mappings --- .../v1/configuration_profiles_controller.rb | 24 ++-- .../api/v1/spine_terms_controller.rb | 2 +- app/javascript/components/auth/MetaTags.jsx | 28 +++++ .../components/auth/ProtectedRoute.jsx | 10 +- .../components/auth/RouteWithTitle.jsx | 11 +- .../auth/SelectConfigurationProfile.jsx | 2 +- .../property-mapping-list/PropertiesList.jsx | 2 +- .../PropertyMappingList.jsx | 16 ++- .../components/shared/AlertNotice.jsx | 14 ++- .../shared/ConfigurationProfileSelect.jsx | 4 +- .../services/fetchConfigurationProfiles.jsx | 17 ++- app/models/alignment.rb | 2 + app/models/configuration_profile.rb | 1 + .../configuration_profile_serializer.rb | 3 + .../configuration_profile_user_serializer.rb | 19 ++++ app/serializers/term_serializer.rb | 4 +- config/locales/ui/en.yml | 3 + config/routes.rb | 6 +- spec/models/domain_spec.rb | 6 +- .../api/v1/configuration_profiles_spec.rb | 106 ++++++++++++++++++ spec/support/database_cleaner.rb | 31 +---- 21 files changed, 235 insertions(+), 76 deletions(-) create mode 100644 app/javascript/components/auth/MetaTags.jsx create mode 100644 app/serializers/configuration_profile_user_serializer.rb create mode 100644 spec/requests/api/v1/configuration_profiles_spec.rb diff --git a/app/controllers/api/v1/configuration_profiles_controller.rb b/app/controllers/api/v1/configuration_profiles_controller.rb index b4713d89..1b197c26 100644 --- a/app/controllers/api/v1/configuration_profiles_controller.rb +++ b/app/controllers/api/v1/configuration_profiles_controller.rb @@ -13,19 +13,25 @@ def create end def index - fields = ["configuration_profiles.*"] - - configuration_profiles = + scope = if current_user && !current_user.super_admin? - current_user - .configuration_profile_users - .joins(:configuration_profile, :organization) - .select(*fields, :lead_mapper, "organizations.id organization_id, organizations.id AS organization") + current_user.configuration_profiles else - ConfigurationProfile.select(*fields) + ConfigurationProfile.all end - render json: configuration_profiles.order(:name) + render json: scope.order(:name) + end + + def index_shared_mappings + render json: ConfigurationProfile.active.with_shared_mappings.order(:name), with_shared_mappings: true, + shared_mappings: true + end + + def index_for_user + render json: current_user.configuration_profile_users.includes(:configuration_profile, :organization) + .where(configuration_profiles: { state: :active }) + .order("configuration_profiles.name"), with_shared_mappings: true end def destroy diff --git a/app/controllers/api/v1/spine_terms_controller.rb b/app/controllers/api/v1/spine_terms_controller.rb index dd8457b5..310c5002 100644 --- a/app/controllers/api/v1/spine_terms_controller.rb +++ b/app/controllers/api/v1/spine_terms_controller.rb @@ -15,7 +15,7 @@ def index includes += %i(organization) if params[:with_organization].present? terms = Spine.find(params[:id]).terms.includes(includes) - render json: terms, spine: params[:with_weights].present?, + render json: terms, spine: params[:with_weights].present?, spine_id: params[:id], with_organization: params[:with_organization].present? end diff --git a/app/javascript/components/auth/MetaTags.jsx b/app/javascript/components/auth/MetaTags.jsx new file mode 100644 index 00000000..7d633e7e --- /dev/null +++ b/app/javascript/components/auth/MetaTags.jsx @@ -0,0 +1,28 @@ +import { Helmet } from 'react-helmet'; +import { snakeCase } from 'lodash'; +import { i18n } from '../../utils/i18n'; + +const MetaTags = ({ pageType = 'default' }) => { + const key = snakeCase(pageType); + const title = i18n.t(`ui.pages.${key}.title`); + const description = i18n.t(`ui.pages.${key}.description`); + + return ( + + {/* Standard metadata tags */} + {title} + + {/* End standard metadata tags */} + {/* OpenGraph tags */} + + + {/* End OpenGraph tags */} + {/* Twitter tags */} + + + {/* End Twitter tags */} + + ); +}; + +export default MetaTags; diff --git a/app/javascript/components/auth/ProtectedRoute.jsx b/app/javascript/components/auth/ProtectedRoute.jsx index 4b143594..6a8053bd 100644 --- a/app/javascript/components/auth/ProtectedRoute.jsx +++ b/app/javascript/components/auth/ProtectedRoute.jsx @@ -1,9 +1,7 @@ import { Route, Redirect } from 'react-router-dom'; import { useSelector } from 'react-redux'; -import { Helmet } from 'react-helmet'; -import { snakeCase } from 'lodash'; +import MetaTags from './MetaTags'; import { showError } from '../../helpers/Messages'; -import { i18n } from '../../utils/i18n'; const ProtectedRoute = ({ component: Component, @@ -13,7 +11,6 @@ const ProtectedRoute = ({ }) => { const isLoggedIn = useSelector((state) => state.loggedIn); const user = useSelector((state) => state.user); - const key = snakeCase(pageType); return ( /// If we have a valid session @@ -29,10 +26,7 @@ const ProtectedRoute = ({ {...rest} render={(props) => ( <> - - {i18n.t(`ui.pages.${key}.title`)} - {i18n.t(`ui.pages.${key}.description`)} - + { - const key = snakeCase(pageType); - return ( <> - - {i18n.t(`ui.pages.${key}.title`)} - {i18n.t(`ui.pages.${key}.description`)} - + ); diff --git a/app/javascript/components/auth/SelectConfigurationProfile.jsx b/app/javascript/components/auth/SelectConfigurationProfile.jsx index db957e56..9436b960 100644 --- a/app/javascript/components/auth/SelectConfigurationProfile.jsx +++ b/app/javascript/components/auth/SelectConfigurationProfile.jsx @@ -6,7 +6,7 @@ const SelectConfigurationProfile = ({ history }) => ( null} />
- history.push('/')} /> + history.push('/')} />
diff --git a/app/javascript/components/property-mapping-list/PropertiesList.jsx b/app/javascript/components/property-mapping-list/PropertiesList.jsx index 9051b930..6ebcfa03 100644 --- a/app/javascript/components/property-mapping-list/PropertiesList.jsx +++ b/app/javascript/components/property-mapping-list/PropertiesList.jsx @@ -103,7 +103,7 @@ export default class PropertiesList extends Component { ) ) && /// It matches the selected spine organizations - this.selectedSpineOrganizationIds().includes(property.organizationId))) + this.selectedSpineOrganizationIds().includes(property.organization?.id))) ); return implementSpineSort(filteredProps, selectedSpineOrderOption); diff --git a/app/javascript/components/property-mapping-list/PropertyMappingList.jsx b/app/javascript/components/property-mapping-list/PropertyMappingList.jsx index e8a51d40..abcb4874 100644 --- a/app/javascript/components/property-mapping-list/PropertyMappingList.jsx +++ b/app/javascript/components/property-mapping-list/PropertyMappingList.jsx @@ -68,10 +68,20 @@ const PropertyMappingList = (props) => { ) : null}
+ {!context.currentConfigurationProfile?.withSharedMappings && ( +
+ +
+ )}
- {!context.loggedIn && } - - {context.currentConfigurationProfile && + {!context.loggedIn && ( + + )} + {context.currentConfigurationProfile?.withSharedMappings && (state.loading ? ( ) : ( diff --git a/app/javascript/components/shared/AlertNotice.jsx b/app/javascript/components/shared/AlertNotice.jsx index 2d252f09..158b3335 100644 --- a/app/javascript/components/shared/AlertNotice.jsx +++ b/app/javascript/components/shared/AlertNotice.jsx @@ -15,7 +15,7 @@ const AlertNotice = (props) => { /** * Elements from props */ - const { cssClass, title, message, onClose, withScroll = false } = props; + const { cssClass, title, message, onClose, withScroll = false, withTitle = true } = props; const myRef = useRef(null); useEffect(() => { if (withScroll && !isEmpty(message) && myRef.current) { @@ -28,14 +28,14 @@ const AlertNotice = (props) => { const renderError = () => { if (isArray(message) && message.length > 1) { return ( -
    +
      {message.map((msg, i) => (
    • {msg}
    • ))}
    ); } else { - return

    {isArray(message) ? message[0] : message}

    ; + return

    {isArray(message) ? message[0] : message}

    ; } }; @@ -44,9 +44,11 @@ const AlertNotice = (props) => { ref={myRef} className={'alert alert-dismissible ' + (cssClass ? cssClass : 'alert-danger')} > -

    - {title ? title : 'Attention!'} -

    + {withTitle && ( +

    + {title ? title : 'Attention!'} +

    + )} {renderError()} {onClose && ( + +
@@ -138,22 +186,10 @@ export default class MultipleDomainsModal extends Component {
- {domains.map((dom) => ( -
- this.handleDomainClick(dom.id)} - tabIndex={0} - type="checkbox" - /> - -
- ))} + {absentDomains.map((d) => + this.renderDomain(d, 'None', 'properties with no domain declared') + )} + {domains.map((d) => this.renderDomain(d, d.label, d.uri))}
From 7b1a37cbc41027a516eec09431e3b457ef737d44 Mon Sep 17 00:00:00 2001 From: Oksana Melnikova Date: Tue, 28 May 2024 20:14:03 +1000 Subject: [PATCH 10/16] (#533) fixed lead mapper flag passing for user --- app/serializers/configuration_profile_user_serializer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/serializers/configuration_profile_user_serializer.rb b/app/serializers/configuration_profile_user_serializer.rb index d5cb3a7f..f5054417 100644 --- a/app/serializers/configuration_profile_user_serializer.rb +++ b/app/serializers/configuration_profile_user_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class ConfigurationProfileUserSerializer < ApplicationSerializer - attributes :description, :slug, :state + attributes :description, :slug, :state, :lead_mapper delegate :id, :name, :created_at, :updated_at, to: :configuration_profile delegate :description, :slug, :state, to: :configuration_profile From 20f085e0286007f66bd9cb53e38ea2d8c6d5d2ca Mon Sep 17 00:00:00 2001 From: Alex Nizamov Date: Fri, 31 May 2024 11:43:05 +0500 Subject: [PATCH 11/16] (#342) Allow admins to impersonate mappers --- app/controllers/application_controller.rb | 8 +++++ app/controllers/impersonations_controller.rb | 36 +++++++++++++++++++ app/javascript/components/auth/AuthButton.jsx | 6 ++-- .../dashboard/agents/AgentsIndex.jsx | 4 +++ app/policies/agent_policy.rb | 7 ++++ app/views/layouts/application.html.erb | 1 + .../shared/_impersonation_status.html.erb | 8 +++++ config/routes.rb | 2 ++ 8 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 app/controllers/impersonations_controller.rb create mode 100644 app/views/shared/_impersonation_status.html.erb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 85b18550..cc671615 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base helper_method :current_configuration_profile_user helper_method :current_organization helper_method :current_user + helper_method :impersonation_mode? # We manage our own security for sessions skip_before_action :verify_authenticity_token @@ -98,4 +99,11 @@ def user_not_authorized(err) end end end + + ### + # @description: Returns `true` if an admin is impersonating an agent + ### + def impersonation_mode? + session[:impostor_id].present? + end end diff --git a/app/controllers/impersonations_controller.rb b/app/controllers/impersonations_controller.rb new file mode 100644 index 00000000..600fdc2c --- /dev/null +++ b/app/controllers/impersonations_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +### +# @description: Manages impersonation of agents by admins +### +class ImpersonationsController < ApplicationController + ### + # @description: Signs in an agent and persists the current user as the impostor + ### + def start + agent = + begin + policy_scope(User, policy_scope_class: AgentPolicy::Scope).find(params[:agent_id]) + rescue ActiveRecord::RecordNotFound + nil + end + + if agent + session[:impostor_id] = current_user.id + session[:user_id] = agent.id + end + + redirect_to root_url + end + + ### + # @description: Signs in the impostor back and redirects them to the agents page + ### + def stop + return redirect_to root_url unless impersonation_mode? + + session[:user_id] = session[:impostor_id] + session[:impostor_id] = nil + redirect_to "/dashboard/agents" + end +end diff --git a/app/javascript/components/auth/AuthButton.jsx b/app/javascript/components/auth/AuthButton.jsx index 87c2a554..eaa4a596 100644 --- a/app/javascript/components/auth/AuthButton.jsx +++ b/app/javascript/components/auth/AuthButton.jsx @@ -4,7 +4,7 @@ import { useSelector, useDispatch } from 'react-redux'; import { doLogout, unsetUser } from '../../actions/sessions'; import signOut from '../../services/signOut'; import { AppContext } from '../../contexts/AppContext'; -import { showInfo, showError } from '../../helpers/Messages'; +import { showError } from '../../helpers/Messages'; const AuthButton = () => { const { setLoggedIn, setCurrentConfigurationProfile } = useContext(AppContext); @@ -23,7 +23,9 @@ const AuthButton = () => { dispatch(unsetUser()); setCurrentConfigurationProfile(null); setLoggedIn(false); - showInfo('Signed Out'); + + // Reload the whole app + window.location = '/'; }; /// Show "Sign Out" if the user is already signed in diff --git a/app/javascript/components/dashboard/agents/AgentsIndex.jsx b/app/javascript/components/dashboard/agents/AgentsIndex.jsx index f85c9bca..edfbbc07 100644 --- a/app/javascript/components/dashboard/agents/AgentsIndex.jsx +++ b/app/javascript/components/dashboard/agents/AgentsIndex.jsx @@ -46,6 +46,9 @@ const AgentsIndex = (_props = {}) => {
    {profiles}
+ + Impersonate + ); }; @@ -68,6 +71,7 @@ const AgentsIndex = (_props = {}) => { {i18n.t('ui.dashboard.agents.table.phone')} {i18n.t('ui.dashboard.agents.table.organization')} {i18n.t('ui.dashboard.agents.table.configuration_profile')} + {state.agents.map(buildTableRow)} diff --git a/app/policies/agent_policy.rb b/app/policies/agent_policy.rb index 746fc062..a5e5d2fd 100644 --- a/app/policies/agent_policy.rb +++ b/app/policies/agent_policy.rb @@ -5,4 +5,11 @@ # agents records. ### class AgentPolicy < AdminAccessPolicy + class Scope < ApplicationPolicy::Scope + def resolve + return scope.none unless user.user.super_admin? + + User.joins(:roles).where(roles: { name: Desm::MAPPER_ROLE_NAME }).distinct + end + end end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 3bfabcc8..44250e0c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -17,6 +17,7 @@ data-logged-in="<%= current_user.present? %>" data-organization="<%= current_organization&.to_json %>" > + <%= render "shared/impersonation_status" %> <%= yield %> diff --git a/app/views/shared/_impersonation_status.html.erb b/app/views/shared/_impersonation_status.html.erb new file mode 100644 index 00000000..02c5ea1b --- /dev/null +++ b/app/views/shared/_impersonation_status.html.erb @@ -0,0 +1,8 @@ +<% if impersonation_mode? %> +
+ + 🎭 Impersonating <%= current_user.fullname %> <%= current_organization ? "@ #{current_organization.name}" : "" %> <<%= current_user.email %>> + + <%= link_to 'Stop Impersonating', stop_impersonating_path, class: 'btn btn-danger btn-sm' %> +
+<% end %> diff --git a/config/routes.rb b/config/routes.rb index dc09a1db..2301ac3b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -132,6 +132,8 @@ resources :registrations, only: [:create] delete :logout, to: 'sessions#logout' get :session_status, to: 'sessions#session_status' + get 'agents/:agent_id/impersonate', to: 'impersonations#start' + get :stop_impersonating, to: 'impersonations#stop' post 'password/forgot', to: 'passwords#forgot' post 'password/reset', to: 'passwords#reset' From 1c7473523410e438e7cff5ce7aee4c727c71277f Mon Sep 17 00:00:00 2001 From: Alex Nizamov Date: Fri, 31 May 2024 11:43:05 +0500 Subject: [PATCH 12/16] (#342) Allow admins to impersonate mappers --- app/controllers/application_controller.rb | 8 +++++ app/controllers/impersonations_controller.rb | 36 +++++++++++++++++++ app/javascript/components/auth/AuthButton.jsx | 6 ++-- .../dashboard/agents/AgentsIndex.jsx | 4 +++ app/policies/agent_policy.rb | 7 ++++ app/views/layouts/application.html.erb | 1 + .../shared/_impersonation_status.html.erb | 8 +++++ config/routes.rb | 2 ++ 8 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 app/controllers/impersonations_controller.rb create mode 100644 app/views/shared/_impersonation_status.html.erb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 85b18550..cc671615 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -11,6 +11,7 @@ class ApplicationController < ActionController::Base helper_method :current_configuration_profile_user helper_method :current_organization helper_method :current_user + helper_method :impersonation_mode? # We manage our own security for sessions skip_before_action :verify_authenticity_token @@ -98,4 +99,11 @@ def user_not_authorized(err) end end end + + ### + # @description: Returns `true` if an admin is impersonating an agent + ### + def impersonation_mode? + session[:impostor_id].present? + end end diff --git a/app/controllers/impersonations_controller.rb b/app/controllers/impersonations_controller.rb new file mode 100644 index 00000000..600fdc2c --- /dev/null +++ b/app/controllers/impersonations_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +### +# @description: Manages impersonation of agents by admins +### +class ImpersonationsController < ApplicationController + ### + # @description: Signs in an agent and persists the current user as the impostor + ### + def start + agent = + begin + policy_scope(User, policy_scope_class: AgentPolicy::Scope).find(params[:agent_id]) + rescue ActiveRecord::RecordNotFound + nil + end + + if agent + session[:impostor_id] = current_user.id + session[:user_id] = agent.id + end + + redirect_to root_url + end + + ### + # @description: Signs in the impostor back and redirects them to the agents page + ### + def stop + return redirect_to root_url unless impersonation_mode? + + session[:user_id] = session[:impostor_id] + session[:impostor_id] = nil + redirect_to "/dashboard/agents" + end +end diff --git a/app/javascript/components/auth/AuthButton.jsx b/app/javascript/components/auth/AuthButton.jsx index 87c2a554..eaa4a596 100644 --- a/app/javascript/components/auth/AuthButton.jsx +++ b/app/javascript/components/auth/AuthButton.jsx @@ -4,7 +4,7 @@ import { useSelector, useDispatch } from 'react-redux'; import { doLogout, unsetUser } from '../../actions/sessions'; import signOut from '../../services/signOut'; import { AppContext } from '../../contexts/AppContext'; -import { showInfo, showError } from '../../helpers/Messages'; +import { showError } from '../../helpers/Messages'; const AuthButton = () => { const { setLoggedIn, setCurrentConfigurationProfile } = useContext(AppContext); @@ -23,7 +23,9 @@ const AuthButton = () => { dispatch(unsetUser()); setCurrentConfigurationProfile(null); setLoggedIn(false); - showInfo('Signed Out'); + + // Reload the whole app + window.location = '/'; }; /// Show "Sign Out" if the user is already signed in diff --git a/app/javascript/components/dashboard/agents/AgentsIndex.jsx b/app/javascript/components/dashboard/agents/AgentsIndex.jsx index f85c9bca..edfbbc07 100644 --- a/app/javascript/components/dashboard/agents/AgentsIndex.jsx +++ b/app/javascript/components/dashboard/agents/AgentsIndex.jsx @@ -46,6 +46,9 @@ const AgentsIndex = (_props = {}) => {
    {profiles}
+ + Impersonate + ); }; @@ -68,6 +71,7 @@ const AgentsIndex = (_props = {}) => { {i18n.t('ui.dashboard.agents.table.phone')} {i18n.t('ui.dashboard.agents.table.organization')} {i18n.t('ui.dashboard.agents.table.configuration_profile')} + {state.agents.map(buildTableRow)} diff --git a/app/policies/agent_policy.rb b/app/policies/agent_policy.rb index 746fc062..a5e5d2fd 100644 --- a/app/policies/agent_policy.rb +++ b/app/policies/agent_policy.rb @@ -5,4 +5,11 @@ # agents records. ### class AgentPolicy < AdminAccessPolicy + class Scope < ApplicationPolicy::Scope + def resolve + return scope.none unless user.user.super_admin? + + User.joins(:roles).where(roles: { name: Desm::MAPPER_ROLE_NAME }).distinct + end + end end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 3bfabcc8..44250e0c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -17,6 +17,7 @@ data-logged-in="<%= current_user.present? %>" data-organization="<%= current_organization&.to_json %>" > + <%= render "shared/impersonation_status" %> <%= yield %> diff --git a/app/views/shared/_impersonation_status.html.erb b/app/views/shared/_impersonation_status.html.erb new file mode 100644 index 00000000..02c5ea1b --- /dev/null +++ b/app/views/shared/_impersonation_status.html.erb @@ -0,0 +1,8 @@ +<% if impersonation_mode? %> +
+ + 🎭 Impersonating <%= current_user.fullname %> <%= current_organization ? "@ #{current_organization.name}" : "" %> <<%= current_user.email %>> + + <%= link_to 'Stop Impersonating', stop_impersonating_path, class: 'btn btn-danger btn-sm' %> +
+<% end %> diff --git a/config/routes.rb b/config/routes.rb index dc09a1db..2301ac3b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -132,6 +132,8 @@ resources :registrations, only: [:create] delete :logout, to: 'sessions#logout' get :session_status, to: 'sessions#session_status' + get 'agents/:agent_id/impersonate', to: 'impersonations#start' + get :stop_impersonating, to: 'impersonations#stop' post 'password/forgot', to: 'passwords#forgot' post 'password/reset', to: 'passwords#reset' From 4b8428cfef0a58ad00844e4f7556398c33b8f0c8 Mon Sep 17 00:00:00 2001 From: Alex Nizamov Date: Sun, 26 May 2024 13:09:33 +0500 Subject: [PATCH 13/16] (#444) Display compact domain from spec as term's class/type --- .../PropertyAlignments.jsx | 8 +++----- .../property-mapping-list/PropertyCard.jsx | 5 ++--- .../stores/propertyMappingListStore.js | 19 +++++++------------ 3 files changed, 12 insertions(+), 20 deletions(-) diff --git a/app/javascript/components/property-mapping-list/PropertyAlignments.jsx b/app/javascript/components/property-mapping-list/PropertyAlignments.jsx index 5854b167..a3c654b9 100644 --- a/app/javascript/components/property-mapping-list/PropertyAlignments.jsx +++ b/app/javascript/components/property-mapping-list/PropertyAlignments.jsx @@ -1,7 +1,7 @@ import { useMemo, useState } from 'react'; import { compact, flatMap } from 'lodash'; import { implementAlignmentSort, implementAlignmentTermsSort } from './SortOptions'; -import { propertyClassesForAlignmentTerm } from './stores/propertyMappingListStore'; +import { propertyClassForSpineTerm } from './stores/propertyMappingListStore'; /** * @description A list of alignments with information like predicate, comment, and more. @@ -47,7 +47,7 @@ const PropertyAlignments = (props) => { ? { ...mTerm, alignment, - selectedClasses: propertyClassesForAlignmentTerm(alignment, mTerm), + selectedClass: propertyClassForSpineTerm(mTerm), } : null ) @@ -80,8 +80,6 @@ const PropertyAlignments = (props) => { const AlignmentCard = ({ alignment, term, isLast = false }) => { const [showingAlignmentComment, setShowingAlignmentComment] = useState(false); - const alignmentTermClasses = term.selectedClasses.map((c) =>
  • {c}
  • ); - return (
    @@ -98,7 +96,7 @@ const AlignmentCard = ({ alignment, term, isLast = false }) => {

    {term.name}

    Class/Type -
      {alignmentTermClasses}
    +

    {term.selectedClass}

    Definition diff --git a/app/javascript/components/property-mapping-list/PropertyCard.jsx b/app/javascript/components/property-mapping-list/PropertyCard.jsx index 39372da0..060919df 100644 --- a/app/javascript/components/property-mapping-list/PropertyCard.jsx +++ b/app/javascript/components/property-mapping-list/PropertyCard.jsx @@ -1,12 +1,11 @@ import ProgressReportBar from '../shared/ProgressReportBar'; -import { propertyClassesForSpineTerm } from './stores/propertyMappingListStore'; +import { propertyClassForSpineTerm } from './stores/propertyMappingListStore'; /** * Props: * @param {Object} term */ const PropertyCard = ({ term }) => { - const propertyClasses = propertyClassesForSpineTerm(term).map((c) =>
  • {c}
  • ); return (
    @@ -14,7 +13,7 @@ const PropertyCard = ({ term }) => {

    {term.name}

    Class/Type -
      {propertyClasses}
    +

    {propertyClassForSpineTerm(term)}

    Definition

    {term.property.comment}

    diff --git a/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js b/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js index 2b0315fd..fe5e4dcc 100644 --- a/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js +++ b/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js @@ -1,4 +1,3 @@ -import { flatMap, intersection, uniq } from 'lodash'; import { baseModel } from '../../stores/baseModel'; import { easyStateSetters } from '../../stores/easyState'; import { alignmentSortOptions, spineSortOptions } from '../SortOptions'; @@ -44,18 +43,14 @@ export const defaultState = { propertiesInputValue: '', }; -export const propertyClassesForSpineTerm = (term) => { - const selectedDomains = uniq( - flatMap(term.alignments, (alignment) => alignment.selectedDomains || []) - ); - const termClasses = term.property.domain || []; - return intersection(selectedDomains, termClasses); -}; +export const propertyClassForSpineTerm = (term) => { + const { selectedDomain } = term.property; + + if ((selectedDomain ?? '').startsWith('http')) { + return null; + } -export const propertyClassesForAlignmentTerm = (alignment, term) => { - const selectedDomains = alignment.selectedDomains || []; - const termClasses = term.property.domain || []; - return intersection(selectedDomains, termClasses); + return selectedDomain; }; export const propertyMappingListStore = (initialData = {}) => ({ From 628ef303caf3b4bb06d815f3e2e51b7305e927fa Mon Sep 17 00:00:00 2001 From: Alex Nizamov Date: Sun, 26 May 2024 17:20:16 +0500 Subject: [PATCH 14/16] (#444) Display all compact domains in property card --- .../property-mapping-list/PropertyAlignments.jsx | 8 +++++--- .../components/property-mapping-list/PropertyCard.jsx | 5 +++-- .../stores/propertyMappingListStore.js | 11 ++--------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/app/javascript/components/property-mapping-list/PropertyAlignments.jsx b/app/javascript/components/property-mapping-list/PropertyAlignments.jsx index a3c654b9..69e8c9da 100644 --- a/app/javascript/components/property-mapping-list/PropertyAlignments.jsx +++ b/app/javascript/components/property-mapping-list/PropertyAlignments.jsx @@ -1,7 +1,7 @@ import { useMemo, useState } from 'react'; import { compact, flatMap } from 'lodash'; import { implementAlignmentSort, implementAlignmentTermsSort } from './SortOptions'; -import { propertyClassForSpineTerm } from './stores/propertyMappingListStore'; +import { propertyClassesForSpineTerm } from './stores/propertyMappingListStore'; /** * @description A list of alignments with information like predicate, comment, and more. @@ -47,7 +47,7 @@ const PropertyAlignments = (props) => { ? { ...mTerm, alignment, - selectedClass: propertyClassForSpineTerm(mTerm), + selectedClasses: propertyClassesForSpineTerm(mTerm), } : null ) @@ -80,6 +80,8 @@ const PropertyAlignments = (props) => { const AlignmentCard = ({ alignment, term, isLast = false }) => { const [showingAlignmentComment, setShowingAlignmentComment] = useState(false); + const alignmentTermClasses = term.selectedClasses.map((c) =>
  • {c}
  • ); + return (
    @@ -96,7 +98,7 @@ const AlignmentCard = ({ alignment, term, isLast = false }) => {

    {term.name}

    Class/Type -

    {term.selectedClass}

    +
      {alignmentTermClasses}
    Definition diff --git a/app/javascript/components/property-mapping-list/PropertyCard.jsx b/app/javascript/components/property-mapping-list/PropertyCard.jsx index 060919df..39372da0 100644 --- a/app/javascript/components/property-mapping-list/PropertyCard.jsx +++ b/app/javascript/components/property-mapping-list/PropertyCard.jsx @@ -1,11 +1,12 @@ import ProgressReportBar from '../shared/ProgressReportBar'; -import { propertyClassForSpineTerm } from './stores/propertyMappingListStore'; +import { propertyClassesForSpineTerm } from './stores/propertyMappingListStore'; /** * Props: * @param {Object} term */ const PropertyCard = ({ term }) => { + const propertyClasses = propertyClassesForSpineTerm(term).map((c) =>
  • {c}
  • ); return (
    @@ -13,7 +14,7 @@ const PropertyCard = ({ term }) => {

    {term.name}

    Class/Type -

    {propertyClassForSpineTerm(term)}

    +
      {propertyClasses}
    Definition

    {term.property.comment}

    diff --git a/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js b/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js index fe5e4dcc..90f253ca 100644 --- a/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js +++ b/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js @@ -43,15 +43,8 @@ export const defaultState = { propertiesInputValue: '', }; -export const propertyClassForSpineTerm = (term) => { - const { selectedDomain } = term.property; - - if ((selectedDomain ?? '').startsWith('http')) { - return null; - } - - return selectedDomain; -}; +export const propertyClassesForSpineTerm = (term) => + (term.property.domain ?? []).filter((u) => !u.startsWith('http')); export const propertyMappingListStore = (initialData = {}) => ({ ...baseModel(initialData), From 423a43c6b84d18f2c2f2e45f45ddce707ee14610 Mon Sep 17 00:00:00 2001 From: Alex Nizamov Date: Tue, 28 May 2024 21:17:07 +0500 Subject: [PATCH 15/16] (#444) Build custom mechanism for compacting URIs --- .../PropertyAlignments.jsx | 3 +- .../property-mapping-list/PropertyCard.jsx | 4 +- .../stores/propertyMappingListStore.js | 3 -- app/lib/utils.rb | 25 ++++++++++ app/models/property.rb | 4 ++ app/serializers/property_serializer.rb | 5 ++ app/services/converters/base.rb | 4 +- app/services/exporters/mapping.rb | 49 +------------------ config/application.rb | 1 + config/initializers/desm_constants.rb | 48 ++++++++++++++++++ spec/lib/utils_spec.rb | 40 +++++++++++++++ 11 files changed, 128 insertions(+), 58 deletions(-) create mode 100644 app/lib/utils.rb create mode 100644 app/serializers/property_serializer.rb create mode 100644 spec/lib/utils_spec.rb diff --git a/app/javascript/components/property-mapping-list/PropertyAlignments.jsx b/app/javascript/components/property-mapping-list/PropertyAlignments.jsx index 69e8c9da..c7eeb2cf 100644 --- a/app/javascript/components/property-mapping-list/PropertyAlignments.jsx +++ b/app/javascript/components/property-mapping-list/PropertyAlignments.jsx @@ -1,7 +1,6 @@ import { useMemo, useState } from 'react'; import { compact, flatMap } from 'lodash'; import { implementAlignmentSort, implementAlignmentTermsSort } from './SortOptions'; -import { propertyClassesForSpineTerm } from './stores/propertyMappingListStore'; /** * @description A list of alignments with information like predicate, comment, and more. @@ -47,7 +46,7 @@ const PropertyAlignments = (props) => { ? { ...mTerm, alignment, - selectedClasses: propertyClassesForSpineTerm(mTerm), + selectedClasses: mTerm.property.compactDomains, } : null ) diff --git a/app/javascript/components/property-mapping-list/PropertyCard.jsx b/app/javascript/components/property-mapping-list/PropertyCard.jsx index 39372da0..73acbe4b 100644 --- a/app/javascript/components/property-mapping-list/PropertyCard.jsx +++ b/app/javascript/components/property-mapping-list/PropertyCard.jsx @@ -1,12 +1,12 @@ import ProgressReportBar from '../shared/ProgressReportBar'; -import { propertyClassesForSpineTerm } from './stores/propertyMappingListStore'; /** * Props: * @param {Object} term */ const PropertyCard = ({ term }) => { - const propertyClasses = propertyClassesForSpineTerm(term).map((c) =>
  • {c}
  • ); + const propertyClasses = term.property.compactDomains.map((c) =>
  • {c}
  • ); + return (
    diff --git a/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js b/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js index 90f253ca..6d2a82fa 100644 --- a/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js +++ b/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js @@ -43,9 +43,6 @@ export const defaultState = { propertiesInputValue: '', }; -export const propertyClassesForSpineTerm = (term) => - (term.property.domain ?? []).filter((u) => !u.startsWith('http')); - export const propertyMappingListStore = (initialData = {}) => ({ ...baseModel(initialData), ...easyStateSetters(defaultState, initialData), diff --git a/app/lib/utils.rb b/app/lib/utils.rb new file mode 100644 index 00000000..a08b0758 --- /dev/null +++ b/app/lib/utils.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class Utils + ## + # Compacts a full URI. + # + # @param uri [String] + # @param context [Hash] + # @return [String|nil] + # If the `uri` is a compact URI, returns it as is. + # If the `uri` is a DESM URI, returns the value from which it was generated. + # If the `uri` belongs to a namespace from the `context`, returns its compact version. + # Otherwise, returns `nil`. + def self.compact_uri(uri, context: Desm::CONTEXT) + return uri unless uri.start_with?("http") + return URI(uri).path.split("/").last if uri.start_with?(Desm::DESM_NAMESPACE.to_s) + + context.each do |prefix, namespace| + next unless namespace.is_a?(String) + return uri.sub(namespace, "#{prefix}:") if uri.start_with?(namespace) + end + + nil + end +end diff --git a/app/models/property.rb b/app/models/property.rb index 72a51c8c..048082e6 100644 --- a/app/models/property.rb +++ b/app/models/property.rb @@ -36,4 +36,8 @@ ### class Property < ApplicationRecord belongs_to :term + + def compact_domains + @compact_domains ||= Array.wrap(domain).map { Utils.compact_uri(_1) }.compact + end end diff --git a/app/serializers/property_serializer.rb b/app/serializers/property_serializer.rb new file mode 100644 index 00000000..4aecdccd --- /dev/null +++ b/app/serializers/property_serializer.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class PropertySerializer < ActiveModel::Serializer + attribute :compact_domains +end diff --git a/app/services/converters/base.rb b/app/services/converters/base.rb index 7f141dd8..ec9fd23f 100644 --- a/app/services/converters/base.rb +++ b/app/services/converters/base.rb @@ -12,8 +12,6 @@ class Base skos: "http://www.w3.org/2004/02/skos/core#" }.freeze - DESM_NAMESPACE = URI("http://desmsolutions.org/ns/") - attr_reader :concept_scheme_cache, :domain_class_cache, :resources, :spec_id ## @@ -54,7 +52,7 @@ def build_domain_class(*args) # @return [String] def build_desm_uri(value) normalized_value = value.squish.gsub(/\W+/, "_") - (DESM_NAMESPACE + "#{spec_id}/#{normalized_value}").to_s + (Desm::DESM_NAMESPACE + "#{spec_id}/#{normalized_value}").to_s end ## diff --git a/app/services/exporters/mapping.rb b/app/services/exporters/mapping.rb index acb0baeb..1b66744e 100644 --- a/app/services/exporters/mapping.rb +++ b/app/services/exporters/mapping.rb @@ -9,53 +9,6 @@ class Mapping # CONSTANTS ### - ### - # @description: These are for established specs used in the mapping. This block will be the same for all mapping, - # the prefixes an URIs are pre-existing constants. - ### - CONTEXT = { - ceds: "http://desmsolutions.org/ns/ceds/", - credReg: "http://desmsolutions.org/ns/credReg/", - dct: "http://purl.org/dc/terms/", - dcterms: "http://purl.org/dc/terms/", - desm: "http://desmsolutions.org/ns/", - rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", - rdfs: "http://www.w3.org/2000/01/rdf-schema#", - sdo: "http://schema.org/", - xsd: "http://www.w3.org/2001/XMLSchema#", - skos: "http://www.w3.org/2004/02/skos/core#", - "desm:inTermMapping": { - "@type": "@id" - }, - "desm:mapper": { - "@type": "@id" - }, - "desm:isClassMappingOf": { - "@type": "@id" - }, - "desm:mappingPredicateType": { - "@type": "@id" - }, - "skos:inScheme": { - "@type": "@id" - }, - "dct:isPartOf": { - "@type": "@id" - }, - "desm:AbstractClass": { - "@type": "@id" - }, - "desm:hasProperty": { - "@type": "@id" - }, - "dct:creator": { - "@type": "@id" - }, - "desm:homepage": { - "@type": "@id" - } - }.freeze - ### # @description: Initializes this class with the instance to export. ### @@ -68,7 +21,7 @@ def initialize(instance) ### def export { - "@context": CONTEXT, + "@context": Desm::CONTEXT, "@graph": @instance.alignments.map do |alignment| term_nodes(alignment) end.flatten.unshift(main_node) diff --git a/config/application.rb b/config/application.rb index 0d066103..1f3cd0d0 100644 --- a/config/application.rb +++ b/config/application.rb @@ -37,6 +37,7 @@ class Application < Rails::Application config.autoload_paths += [ Rails.root.join("app", "interacotrs", "concerns"), + Rails.root.join("app", "lib"), Rails.root.join("lib", "utils"), ] diff --git a/config/initializers/desm_constants.rb b/config/initializers/desm_constants.rb index 143cb41b..65a41611 100644 --- a/config/initializers/desm_constants.rb +++ b/config/initializers/desm_constants.rb @@ -7,4 +7,52 @@ module Desm PHONE_RE = /\A\+?[0-9 -]{6,18}\z/i PRIVATE_KEY = ENV['PRIVATE_KEY'] || 'BAE4QavZnymiL^c584&nBV*dxEGFzas4KXiHTz!a26##!zsHnS' MIN_PASSWORD_LENGTH = ENV['MIN_PASSWORD_LENGTH'] || 8 + DESM_NAMESPACE = URI("http://desmsolutions.org/ns/") + + ### + # @description: These are for established specs used in the mapping. This block will be the same for all mapping, + # the prefixes an URIs are pre-existing constants. + ### + CONTEXT = { + ceds: "http://desmsolutions.org/ns/ceds/", + credReg: "http://desmsolutions.org/ns/credReg/", + dct: "http://purl.org/dc/terms/", + dcterms: "http://purl.org/dc/terms/", + desm: "http://desmsolutions.org/ns/", + rdf: "http://www.w3.org/1999/02/22-rdf-syntax-ns#", + rdfs: "http://www.w3.org/2000/01/rdf-schema#", + sdo: "http://schema.org/", + xsd: "http://www.w3.org/2001/XMLSchema#", + skos: "http://www.w3.org/2004/02/skos/core#", + "desm:inTermMapping": { + "@type": "@id" + }, + "desm:mapper": { + "@type": "@id" + }, + "desm:isClassMappingOf": { + "@type": "@id" + }, + "desm:mappingPredicateType": { + "@type": "@id" + }, + "skos:inScheme": { + "@type": "@id" + }, + "dct:isPartOf": { + "@type": "@id" + }, + "desm:AbstractClass": { + "@type": "@id" + }, + "desm:hasProperty": { + "@type": "@id" + }, + "dct:creator": { + "@type": "@id" + }, + "desm:homepage": { + "@type": "@id" + } + }.freeze end diff --git a/spec/lib/utils_spec.rb b/spec/lib/utils_spec.rb new file mode 100644 index 00000000..3a22eb70 --- /dev/null +++ b/spec/lib/utils_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "rails_helper" + +RSpec.describe Utils do + describe ".compact_uri" do + context "compact URI" do + it "does nothing" do + expect(Utils.compact_uri("sdo:Organization")).to eq("sdo:Organization") + end + end + + context "full DESM URI" do + it "returns original value" do + expect(Utils.compact_uri("http://desmsolutions.org/ns/f401/Program")).to eq("Program") + end + end + + context "full URI" do + context "from context" do + it "returns compact URI" do + expect(Utils.compact_uri("http://www.w3.org/2001/XMLSchema#anyURI")).to eq("xsd:anyURI") + end + end + + context "with custom context" do + it "returns compact URI" do + expect(Utils.compact_uri("http://xmlns.com/foaf/0.1/Person", + context: { foaf: "http://xmlns.com/foaf/0.1/" })).to eq("foaf:Person") + end + end + + context "not from context" do + it "returns nothing" do + expect(Utils.compact_uri("http://xmlns.com/foaf/0.1/Person")).to eq(nil) + end + end + end + end +end From 63722ffeb7457d4760835951f7adeb1eb8b399ec Mon Sep 17 00:00:00 2001 From: Alex Nizamov Date: Sat, 1 Jun 2024 13:23:09 +0500 Subject: [PATCH 16/16] (#444) Display relevant domains only in property cards --- app/controllers/api/v1/alignments_controller.rb | 7 ++++++- .../property-mapping-list/PropertyAlignments.jsx | 3 ++- .../components/property-mapping-list/PropertyCard.jsx | 4 ++-- .../stores/propertyMappingListStore.js | 10 ++++++++++ app/models/alignment.rb | 4 ++++ app/models/property.rb | 3 +++ app/models/specification.rb | 7 +++++++ app/models/term.rb | 2 ++ app/serializers/alignment_serializer.rb | 7 ++++--- app/serializers/property_serializer.rb | 5 ----- app/serializers/term_serializer.rb | 2 +- 11 files changed, 41 insertions(+), 13 deletions(-) delete mode 100644 app/serializers/property_serializer.rb diff --git a/app/controllers/api/v1/alignments_controller.rb b/app/controllers/api/v1/alignments_controller.rb index 06a1c00a..b2035757 100644 --- a/app/controllers/api/v1/alignments_controller.rb +++ b/app/controllers/api/v1/alignments_controller.rb @@ -15,7 +15,12 @@ class AlignmentsController < BaseController ### def index terms = current_configuration_profile.alignments - .includes(:predicate, mapping: :specification, mapped_terms: %i(organization property vocabularies)) + .includes( + :predicate, + :specification, + mapping: %i(configuration_profile_user organization), + mapped_terms: %i(organization property vocabularies) + ) .where( mappings: { spine_id: params[:spine_id], status: :mapped } ) diff --git a/app/javascript/components/property-mapping-list/PropertyAlignments.jsx b/app/javascript/components/property-mapping-list/PropertyAlignments.jsx index c7eeb2cf..5854b167 100644 --- a/app/javascript/components/property-mapping-list/PropertyAlignments.jsx +++ b/app/javascript/components/property-mapping-list/PropertyAlignments.jsx @@ -1,6 +1,7 @@ import { useMemo, useState } from 'react'; import { compact, flatMap } from 'lodash'; import { implementAlignmentSort, implementAlignmentTermsSort } from './SortOptions'; +import { propertyClassesForAlignmentTerm } from './stores/propertyMappingListStore'; /** * @description A list of alignments with information like predicate, comment, and more. @@ -46,7 +47,7 @@ const PropertyAlignments = (props) => { ? { ...mTerm, alignment, - selectedClasses: mTerm.property.compactDomains, + selectedClasses: propertyClassesForAlignmentTerm(alignment, mTerm), } : null ) diff --git a/app/javascript/components/property-mapping-list/PropertyCard.jsx b/app/javascript/components/property-mapping-list/PropertyCard.jsx index 73acbe4b..39372da0 100644 --- a/app/javascript/components/property-mapping-list/PropertyCard.jsx +++ b/app/javascript/components/property-mapping-list/PropertyCard.jsx @@ -1,12 +1,12 @@ import ProgressReportBar from '../shared/ProgressReportBar'; +import { propertyClassesForSpineTerm } from './stores/propertyMappingListStore'; /** * Props: * @param {Object} term */ const PropertyCard = ({ term }) => { - const propertyClasses = term.property.compactDomains.map((c) =>
  • {c}
  • ); - + const propertyClasses = propertyClassesForSpineTerm(term).map((c) =>
  • {c}
  • ); return (
    diff --git a/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js b/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js index 6d2a82fa..a34242d9 100644 --- a/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js +++ b/app/javascript/components/property-mapping-list/stores/propertyMappingListStore.js @@ -1,3 +1,4 @@ +import { flatMap, intersection } from 'lodash'; import { baseModel } from '../../stores/baseModel'; import { easyStateSetters } from '../../stores/easyState'; import { alignmentSortOptions, spineSortOptions } from '../SortOptions'; @@ -43,6 +44,15 @@ export const defaultState = { propertiesInputValue: '', }; +export const propertyClassesForSpineTerm = (term) => + intersection( + term.compactDomains, + flatMap(term.alignments, (a) => a.compactDomains) + ); + +export const propertyClassesForAlignmentTerm = (alignment, term) => + intersection(term.compactDomains, alignment.compactDomains); + export const propertyMappingListStore = (initialData = {}) => ({ ...baseModel(initialData), ...easyStateSetters(defaultState, initialData), diff --git a/app/models/alignment.rb b/app/models/alignment.rb index c8aa063e..238bf8d7 100644 --- a/app/models/alignment.rb +++ b/app/models/alignment.rb @@ -63,6 +63,8 @@ class Alignment < ApplicationRecord ### has_and_belongs_to_many :mapped_terms, join_table: :alignment_mapped_terms, class_name: :Term + has_one :specification, through: :mapping + has_one :spine, through: :mapping ### @@ -101,6 +103,8 @@ class Alignment < ApplicationRecord scope :mapped_for_spine, ->(spine_id) { joins(:mapping).where(mappings: { status: :mapped, spine_id: }) } + delegate :compact_domains, to: :specification + ### # METHODS ### diff --git a/app/models/property.rb b/app/models/property.rb index 048082e6..2e859ae8 100644 --- a/app/models/property.rb +++ b/app/models/property.rb @@ -37,6 +37,9 @@ class Property < ApplicationRecord belongs_to :term + ### + # @description: Returns the property's compact domains + ### def compact_domains @compact_domains ||= Array.wrap(domain).map { Utils.compact_uri(_1) }.compact end diff --git a/app/models/specification.rb b/app/models/specification.rb index f4c66f96..6ddde6e0 100644 --- a/app/models/specification.rb +++ b/app/models/specification.rb @@ -96,5 +96,12 @@ def to_json_ld } end + ### + # @description: Returns the specification's compact domains + ### + def compact_domains + @compact_domains ||= Array.wrap(selected_domains_from_file).map { Utils.compact_uri(_1) }.compact + end + scope :for_dso, ->(dso) { joins(:user).where(users: { id: dso.users }) } end diff --git a/app/models/term.rb b/app/models/term.rb index 1af59c9e..5b2cac2c 100644 --- a/app/models/term.rb +++ b/app/models/term.rb @@ -68,6 +68,8 @@ class Term < ApplicationRecord before_destroy :check_if_alignments_exist + delegate :compact_domains, to: :property + ### # @description: Include additional information about the specification in # json responses. This overrides the ApplicationRecord as_json method. diff --git a/app/serializers/alignment_serializer.rb b/app/serializers/alignment_serializer.rb index c0522321..43f80ace 100644 --- a/app/serializers/alignment_serializer.rb +++ b/app/serializers/alignment_serializer.rb @@ -1,7 +1,8 @@ # frozen_string_literal: true class AlignmentSerializer < ApplicationSerializer - attributes :comment, :mapping_id, :origin, :predicate_id, :spine_term_id, :synthetic, :uri, :vocabulary_id + attributes :comment, :compact_domains, :mapping_id, :origin, :predicate_id, :spine_term_id, :synthetic, :uri, + :vocabulary_id attributes :mapped_terms, :predicate attribute :mapping, if: -> { params[:with_schema_name] } do { id: object.mapping.id, title: object.mapping.title, description: object.mapping.description, @@ -12,11 +13,11 @@ class AlignmentSerializer < ApplicationSerializer object.uri end attribute :schema_name, if: -> { params[:with_schema_name] } do - schema = object.mapping.specification + schema = object.specification "#{schema.name}#{schema.version.present? ? " (#{schema.version})" : ''}" end attribute :selected_domains, if: -> { params[:with_schema_name] } do - object.mapping.specification.selected_domains_from_file + object.specification.selected_domains_from_file end # pass the params to the serializer diff --git a/app/serializers/property_serializer.rb b/app/serializers/property_serializer.rb deleted file mode 100644 index 4aecdccd..00000000 --- a/app/serializers/property_serializer.rb +++ /dev/null @@ -1,5 +0,0 @@ -# frozen_string_literal: true - -class PropertySerializer < ActiveModel::Serializer - attribute :compact_domains -end diff --git a/app/serializers/term_serializer.rb b/app/serializers/term_serializer.rb index 4d72f6fd..db21d314 100644 --- a/app/serializers/term_serializer.rb +++ b/app/serializers/term_serializer.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class TermSerializer < ApplicationSerializer - attributes :raw, :source_uri, :slug, :uri + attributes :compact_domains, :raw, :source_uri, :slug, :uri has_one :property has_many :vocabularies, serializer: PreviewSerializer has_one :organization, if: -> { params[:spine] || params[:with_organization] }, serializer: PreviewSerializer