Changelog
O que há de novo.
Cada melhoria para a plataforma, como ele envia.
· VOIPService
SmsKeywordService — o único escritor para palavras-chave de entrada (MS1 Task 3)
Fecha a tarefa TODO 2 deixada para trás. Até que este commit um STOP de entrada verificado chegou BulkTextInboundController, foi reconhecido, foi registrado, e foi então DROPPED — o endpoint aceitou opt-outs e não suprimiu nada. SmsKeywordService é o único escritor: ele adiciona o consentimento do WORMRecord e atualiza o AgenteContatoEstado linha o portão de saída já lê, em um operação (plano Decisão M2 — livro contabilístico mais projecção derivada, não duas fontes de verdade; MS2 faz a porta ler o consentimentoRecord diretamente, após o que o projeção torna-se um cache). Ordem: normalizar ou recusar; interpretar; inserir o recibo de indemnidade; então Pare, comece, ajude. UNKNOWN retorna imediatamente e não escreve nada, então um a mensagem comum passa para a manipulação normal. As decisões valem a pena discutir com mais tarde. * Escreve AgenteContactState diretamente em vez de chamar AIService. Já está exposto em E ainda é inutilizável daqui para três razões independentes: VOIPService não tem cliente para AIService; AIService autentica essa superfície com um segredo diferente **************** não interna.auth.secret); e o ponto final Não aceita nenhum E.164, apenas **************** número é exatamente o que uma entrada STOP é. * Chave de contato é "tel:<e164>". AGENT CONTACT STATE é único em (ORGANIZAÇÃO ID, CONTACT KEY) e não tem coluna de telefone, então não há lugar nenhum Caso contrário, um número nu pode ir. CONSEQUÊNCIA, NÃO FECHADO POR MS1: esta linha não merge com uma linha posterior de "leader:<uid>" para o mesmo humano, por isso uma consulta no portal a linha com chave de chumbo não vê nenhum opt-out. MS2 fecha fazendo o portão ler ConsentimentoRecord, que é chave número. * Uma STOP nua define todos os quatro canais. Não diz "somente SMS", e lê-lo Assim deixa o inquilino a enviar um e-mail a alguém que lhes disse para pararem. INÍCIO re-concede SMS SOMENTE — um START enviado para um número de campanha é evidência sobre SMS E nada mais. * Uma org nula registra o recibo e nada mais. Nenhuma linha de consentimento, não projection: a consentRecord with a null organization is the global-supression postura C1 proíbe, e ********************** é nullable = false de qualquer forma. As provas de que chegou uma STOP ainda batem o silêncio de hoje. * consentidoPrefere a hora do evento da transportadora e os registros que foram usados. Twilio's inbound webhook carrega NO timestamp em tudo, então a entidade javadoc "app-set from the event, never now()" não é satisfatório para isso. Flowroute faz enviar `timestamp`, assim InboundSms ganhou um `carrierEventAt` componente e o O analisador de rota de fluxo lê- o (Instant / OffsetDateTime / local desnudo, normalizado para UTC; qualquer coisa ilegível é nula, nunca um palpite). A prova da linha de consentimentoBlob carrega "consentidoAtSource": "carregador" "recebedor". A hora de recebimento é defensável e um fabricado não é; os dois sendo indistinguíveis depois é o que não sobreviveria à revisão. * handle () toma um registro, não os seis parâmetros posicionais do plano. O plano é... a assinatura não pôde carregar o tempo de recebimento, a hora do evento do transportador ou o bruto carga útil prova, e quatro dos seus parâmetros foram adjacentes Strings - transposição 'de' e 'para' compila silenciosamente e suprime o próprio número do inquilino. Ganhos de aplicação do VOIPService **************************** **************************** não necessita de entrada: basePackages corresponde sub-pacotes e com.kamo.z.shared.comms já estão listados. VoipRepositoryScanTest drives Spring Data's JpaRepositóriosRegistrar sobre a mesma lista de pacotes e afirma que os três repositórios SmsKeywordService injects recebem definições de feijão. Uma inicialização completa do @SpringBootTest NÃO está disponível neste serviço — nenhuma base de dados de testes, sem recursos de teste, sem teste de contexto existente – então isso exerce o registro que realmente decide o resultado, sem um DataSource. TDD — falhas observadas, verbatim. Vermelho antes de implementar: [ERROR] SmsKeywordServiceTest.java não consegue encontrar o símbolo [ERROR] símbolo: classe SmsKeywordService [ERROR] símbolo: classe InboundKeywordEvent Localização: classe. Falha no edifício [ERROR] **************************** não foi possível encontrar o símbolo [ERROR] symbol: method carrierEventAt() Verde depois: SmsKeywordServiceTest 17/17; suite completa 314/314. Mutação 1 — retirou a ordem do consentimentoRecord escrever: [ERROR] Testes executados: 17, Falhas: 2, Erros: 0 < < < Falha! esperado: 100L mas foi: nulo [ERROR] **************************** [uma linha de consentimento com um org nulo é supressão global – a postura C1 proíbe] [ERROR] **************************** Mutação 2 — retirou o suplemento de recibo: [ERROR] Testes executados: 17, Falhas: 3, Erros: 0 < < < Falha! Procurados, mas não invocados: [ERROR] **************************** [ERROR] **************************** [ERROR] **************************** NOTA: o plano diz que esta mutação deve tornar o teste RETRY vermelho. Não. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Ele faz curto-circuitos antes da inserção e permanece GREEN sob esta mutação. A O plano nomeou o teste errado. Mutação 2b abaixo é o que cobre a repetição caminho, e sem executá-lo a reivindicação de imunidade M1 mutação que prova outra coisa. Mutação 2b — removido o pre- check existent(): [ERROR] Testes executados: 17, Falhas: 2, Erros: 0 < < < Falha! **************************** Nunca quis estar aqui. [ERROR] **************************** [ERROR] **************************** Mutação 3 — optou por SMS apenas em vez dos quatro canais: [ERROR] Testes executados: 17, Falhas: 1, Erros: 0 < < < Falha! Esperando que o valor fosse verdadeiro, mas era falso [ERROR] **************************** Mutação 4 — caiu do scanner. pacotes, ou seja, o próprio CrashLoop: [ERROR] Testes executados: 3, Falhas: 2, Erros: 0 < < < Falha! [ERROR] **************************** [ConsentRecordRepository não tem definição de feijão - ******************* não é coberto por @EnableJpaRepositórios. Isto não é um período de execução nulo: o O contexto RECUSA PARA COMEÇAR e o pod CrashLoops.] [ERROR] **************************** [a sonda deste teste foi desviada da anotação real da VOIPServiceApplication] Todos revertidos. Suíte completa após reversão: Testes executados: 314, Falhas: 0, Erros: 0. A DDL não aterrou. Isso depende da adição de biblioteca compartilhada com kamo Comms sms inbound receipt e ********** nenhum dos quais ainda existe na base de dados. Construído localmente contra um jar de lib em um PRIVATE -Dmaven.repo.local; ~/.m2 depois (md5 **********************
· kamo-internal
Desanexar o navegador de chat da janela que maximiza
O navegador de conversa era um adereço infantil do ToolWindowShell, renderizado dentro da caixa de conteúdo da janela maximizada. Clicando em uma linha nela abre que conversação maximizada, que desmotiva a janela segurando - assim o navegador desmontado e montado novamente em cada uso. O membro navegado por destruindo o que eles estavam navegando com: balde, posição de rolagem e texto de busca todo reset no instante em que clicaram. Agora é um irmão de nível da doca. ToolDock monta exatamente um ChatNavigatorPanel, fixou a borda esquerda e a janela maximizada parou Em vez de ser puxado por baixo, a menos que seja ('leftInset'). Através de um swap o painel nunca desmonta, então do ponto de vista do React nada acontece com Ele. A semente do balde está congelada no primeiro toolId que viu, então a lista não refiltra de baixo de um membro que apenas clicou uma linha. Moção é chumbo-e-trail: maximizando, o navegador salta para fora da esquerda borda e a janela se expande para o que é deixado para trás 70ms mais tarde; restaurando execuções o reverso, janela primeiro, navegador seguindo por 90ms. Nenhum dos componentes armazena uma direcção — ambos os atrasos resultam de «máximo», Então a bandeira é a direcção. Tempos compartilhados vivem em maximizeMotion.ts porque as duas metades já não conseguem ver o estado um do outro. Um clique de linha é coreografado separadamente (useMaximizedSwap): o painel congela e as duas conversas dissolvem-se dentro dela, em vez de uma janela voando para a doca enquanto outro voa para fora dela ao lado de um navegador em pé perfeitamente imóvel. A janela rebaixada mantém a geometria do painel enquanto desaparece, Em seguida, obter um ajuste batida em seu slot dock com transições ainda fora, por isso ele reaparece desaparecendo em vez de voar de volta através do viewport. Opacidade e transformar movimento de CSS para framer-motion: uma transição CSS não pode expressar "iniciar a partir de 8px mais baixo" para um elemento já montado, que é o que uma janela que desvanece no painel tem de fazer. A geometria permanece no CSS — o framer re-medeia o calc(100vw - Npx) cada frame. prefere o movimento reduzido Deixa tudo para uma troca de opacidade. Recolher elevadores de estado para usarChatNavigatorCollapse: ToolDock precisa do largura do navegador para dimensionar a janela, e um componente possuindo sua própria largura não podia dizer. Sem novas chaves I18n. Verificado: tsc limpo, 144 testes passam através de ferramentas / e conversas/, todos os scripts de guarda passam. Os dois comportamentos de carga (semente de balde congelado, painel mantido através da dissolução) foram testadas mutações. Não verificado visualmente — este repositório não tem navegador sem cabeça.
· kamo-internal
Telefone da empresa (main/toll free) na assinatura de um membro, com sua extensão
A página de conteúdo de ************* ganha dois interruptores: Telefone da empresa (Main) e Telefone da empresa (Toll Free). Activados, imprimem o a linha publicada da organização seguida da extensão do membro que envia — "(555) 555-5555 x81234" — assim, uma resposta por telefone chega à pessoa que escreveu, Não a central telefónica. Os números vêm de informações de contato sobre **************************** A extensão é a que o membro é realmente atribuído: MemberVoipConfig, leitura do VOIPService e apenas quando o seu VOIP está habilitado, uma vez que um membro com não responde a essa extensão. EquipeMembro.phoneWorkExtension é o backback, e é tudo uma organização que executa nenhum servidor de telefone através O Kamo alguma vez o terá feito. Nenhuma extensão significa que o número imprime nu em vez de com um "x" pendurado. Mesma preferência QuoteAgentContactResolver já se aplica do lado do servidor. empresaPhone torna-se empresaPhoneMain + empresaPhoneTollFree, que fixa dois Coisas que estava a esconder: - aplicarFieldConfig fechado ambos os números atrás de um campo com `se (!local &&!tollFree)`, então uma organização que permitiu apenas sua portagem livre A linha ainda tinha o seu local impresso. - CampaignEditor estava escrevendo uma chave da empresaPhone em SignatureContent; campo ido o compilador encontra-lo, mas o spread tinha deixado passar silenciosamente, então uma prévia da campanha teria deixado cair a linha. Todos os 29 modelos de assinatura agora renderizam ambas as linhas. Três fizeram antes, então o Os serviços equivalentes e os serviços da empresa eram em grande medida inertes — permitindo que se nada nos outros 26 layouts e lido como uma configuração quebrada. O departamento e as opções da empresa mantêm as suas chaves armazenadas e são remarcadas para a mesma redacção, Então os três escopos já não chamam o mesmo número duas coisas diferentes. Quatro caminhos independentes constroem conteúdo de assinatura — a pré-visualização das configurações, o compositor, campanhas e convites para reuniões — e todos os quatro são atualizados. VOIPService não é acessível através do forwardToApi (rotas APIService no prefixo /api/voip), para que a rota de convite obtenha o helper base-URL o catch-all Já tinha, levantado em ********************** Precisa de EmailService e da migração KamoInitializerService para ter desembarcado: o duas alternâncias são persistentes org-wide.
· EmailService
Persista nos dois telefones da empresa
Lê e escreve usuárioEmpresaPhoneMain / usuárioEmpresaPhoneTollFree na assinatura config do campo, para que os novos switches da aba Conteúdo sobrevivam a uma gravação. saveFieldConfig copia campo por campo na linha existente em vez de substituir ele, então uma alternância omitida lá é silenciosamente jogado em cada salvo após o primeiro - ambos os novos campos são adicionados a essa cópia, bem como ao mapper request e resposta. Precisa das colunas correspondentes: KamoInitializerService's Deve ter corrido antes destas naves.
· kamo-shared-library
Atribuir extensões gratuitas ao servidor onde estão ligados
Uma org que executa um servidor FreePBX ocupado ao lado de um servidor KamoPBX novo e vazio viu "21 extensões billable — 63.00 / mês" enquanto olhando para o KamoPBX um. Ambos os números eram verdadeiros da organização e nenhum era verdadeiro do servidor em tela, e não havia nenhuma maneira de uma tela para dizer a diferença: a quebra instância transportadaId por extensão billable mas colapsou os livres em um Int de organização única. não atribuídoThirdPartyByInstance substitui esse int. não atribuídoThirdPartyCount() é mantido como uma soma derivada em vez de transportado ao lado, de modo que o total e as partes não pode se afastar, e cada chamador e asserção existente não é afetado. Adiciona faturabilidadeCountFor( instance), countFor( instance, class) e O suficiente para uma linha por servidor que respeite as regras de classe, que diferem exatamente da maneira que importa aqui: extensão é billable se alguém é atribuído ou não, um terceiro apenas Quando alguém está. Então, um servidor KamoPBX vazio é $0 e um servidor KamoPBX com cinco extensões não atribuídas não são. Versão deliberadamente inalterada em 1.6.0. Construído e testado em um isolado worktree off origin/main — outra sessão tem sete arquivos não relacionados abertos aqui, e instalar a partir desta árvore teria publicado seu trabalho em andamento. 2305 testes passam.
· InitializerService
Pouse as colunas do telefone da empresa antes que o EmailService as leia
ADD COLUNN SE NÃO EXISTE user company phone main e user company phone toll free. A atualização ddl-auto deste serviço adicionaria ambos da entidade, mas todos os serviços que os lê roda ddl-auto: nenhum, e o próprio ALTER de Hibernate não carrega DEFAULT. Estabelecê-lo aqui aterra-los FALSE para organizações existentes em vez de NULL, que é o que "desligar por padrão" é suposto significar. Ordem 0: a entidade mapeia ambas as colunas a partir do momento em que esta compilação começa, então a runner ordenado após um que carrega através de JPA nunca executaria — o mais cedo um morreria em "coluna não existe" e levar toda a corrida com ele.
· kamo-shared-library
O telefone da empresa alterna uma assinatura individual pode transportar
Duas bandeiras de org-wide no email assinature field config: user company phone main e user company phone toll free. Eles colocaram a linha publicada da organização em um assinatura do próprio membro portador da extensão desse membro - "(555) 555-5555 x81234" — que é a forma como um destinatário chega à pessoa que lhe escreveu em vez de Telefonista. Desligado por omissão. Uma organização que não publica nenhum número principal, ou cujos membros manter linhas diretas, não deve encontrar uma linha extra que apareça sob cada nome após uma Preparar.
· kamo-shared-library
O recibo da palavra-chave e o consentimento da chave por campanha (Tarefa MS1 3)
Duas coisas que o livro de registros de consentimento precisa antes de uma entrada STOP pode ser registrado Inquilinos vigiados e exactamente uma vez. «SmsInboundReceipt» (comms sms inbound receipt) é a âncora de idempotência de Plano Decisão M1. Ambos os trilhos de entrada já dedupe e nenhum dedupe pode cobrir uma palavra-chave: *************Dedupes, perguntando se um `VoipMessage` existe para o ID da mensagem da operadora, mas uma mensagem de palavra-chave é interceptado e retorna ANTES que a linha é escrita — assim a segunda entrega de um O retry do portador não encontra nenhuma linha e é processado novamente. Sem esta tabela uma transportadora tentar novamente STOP três vezes adiciona três registros de revogação a um livro de registro WORM que nunca pode ser limpo. Único (ORG ID, CARRIER MESSAGE ID); WORM bloco total guardas copiados do consentimentoRecord, deliberadamente não de AgentOutboundTouch Só é uma convenção sem guarda. ORG ID é nulável, então uma STOP não atribuível ainda é registrada como evidência – e Restrições exclusivas SQL não restringem NULLs, então o repositório carrega uma separado **************************** Um nulo passado para o comum o localizador derivado renderiza como `= NULL`, que nunca é verdadeiro, então o reteste sem org O cheque teria passado silenciosamente todas as vezes. Este buraco está documentado no entidade; fecha quando ADD5/MS3 torna o org resolvível. `ConsentRecord` ganha UMA coluna nula, `campaign id', e UM índice, (organization id, raw number, escopo). Nada mais se mexeu: party-XOR-rawNumber `assertIdentified()` que o DB CHECK mlos consent one identity chk depende dos três índices pré-existentes e da nullable organization id estão todos inalterados, e `ConsentRecordCampaignKeyTest' é uma cerca de regressão em torno de cada um deles. Os novos localizadores org-scoped sentam-se ao lado do org-AGNOSTIC pré-existente **************************** Essa é correcta para o MLOS global-STOP leitura e errado para MS1, onde um proprietário que para Bob A canalização deve permanecer acessível pelo AVAC do Dave. Ambos estão documentados no declaração para escolher o errado é visível no local de chamada em vez de sendo uma supressão silenciosa de agentes cruzados. TDD — falhas observadas, verbatim. Vermelho antes de implementar (mvn -o -Dmaven.repo.local=<private> test): [ERROR] **************************** não foi possível encontrar o símbolo [ERROR] símbolo: classe SmsInboundReceipt Localização: classe. (9 ocorrências) [ERROR] **************************** não foi possível encontrar o símbolo [ERROR] **************************** não foi possível encontrar o símbolo [ERROR] **************************** não foi possível encontrar o símbolo Falha no edifício Verde depois: Testes executados: 19, Falhas: 0, Erros: 0 (com NotificationConsentTest). Mutação A — largada `única = true` do índice do recibo: Testes executados: 5, Falhas: 1, Erros: 0 < < < Falha! -- in **************************** java.lang. AsserçãoError: comms sms inbound receipt precisa de um índice UNIQUE — ele é a única coisa que impede um operador de tentar novamente anexando uma segunda revogação registro [ERROR] **************************** Mutação B — removeu o índice (organization id, raw number, escopo): [ERROR] Testes executados: 6, Falhas: 1, Erros: 0 < < < Falha! -- in **************************** [ERROR] **************************** [um índice (organization id, raw number, escopo) — o opt-out vigiado por inquilino lido] Ambos reverteram. Suíte completa após a reversão: Testes executados: 2295, Falhas: 0, Erros: 0 — incluindo SharedLibBeanSafetyTest, que as duas novas classes passam porque nem carrega um estereótipo de Primavera. A DDL não aterrou. comms sms inbound receipt não existe e mlos consent record não tem coluna de campanha id. Isto não deve ser enviado antes. O KamoInitializerService aplica-se tanto quanto REVOKES UPDATE/DELETE na nova tabela (plan Task 5) — callbacks de entidade não disparam para JPQL em massa ou SQL nativo.
· kamo-shared-library
O diário de sincronização e uma execução de terminal que está realmente congelada
BL1 Tarefa 5, metade bibliotecária compartilhada. Cinco enums de diário, três entidades e três Repositórios abaixo de... Sem estereótipos: BillingWormGuard é uma classe final simples com um construtor privado, e SharedLibBeanSafety O teste está verde. Duas melhorias deliberadas sobre a fonte da folha de pagamento: 1. OS DOCUMENTOS DE PAGAMENTO TERMINAL-RESIDENTE E NÃO TEM (defeito 5). PayrollO javadoc do SyncRun afirma que "uma execução de terminal nunca é reescrita" com Nada por trás disso. BillingSyncRun impõe-o. O plano especifica o guarda como uma leitura nua do campo atual: if (state != null && state != BillingSyncRunState.RUNNING) {jogar ... } Essa versão está errada e teria quebrado a produção. @ Pré- Actualizar incêndios APÓS o chamador ter mutado a entidade, então ele vê o estado terminal BillingSyncJournal.complete() acaba de atribuir e se recusa a escrever o row existe para receber — complete() jogaria em cada execução e closeStaleRuns () em cada varredura. Portanto, a guarda compara-se com o estado como LOADED, capturado por um instantâneo @PostLoad em um campo @Transient. A linha nunca carregada (um novo `novo') é julgado em seu estado atual, que mantém O próprio teste do plano é significativo. 2. dbNow () (gap marcado de Auto-Revisão). Payroll lê o relógio do banco de dados de TimecardPunchRepository; O serviço de faturamento não tem tabela de socos. Adicionado ********************** @Query( value = "SELECT CAST( now() AS timestamp)", nativeQuery = true) — com o CAST padrão SQL, porque o elenco é silenciosamente mutilado: Hibernate lê `:` como prefixo de parâmetro-nomeado e vira now():timestamp into :timestamp. BillingSyncJournal é o seu único interlocutor e o único escritor real de occidentAt; os fallbacks @PrePersist LocalDateTime.now() das entidades são mantidos como guardas de último recurso e documentados como tal (defeito 6). Ambas as entidades WORM são servidas por repositórios NARROW que estendem Repositório, não JpaRepository, então não há exclusão de lote para passar @PreRemove. Provas TDD — Primeiro RED (compilação, antes da existência de qualquer das classes): [ERROR] **************************** não foi possível encontrar o símbolo [ERROR] símbolo: classe BillingSyncIssue Localização: classe. ... 20 erros de "não consigo encontrar o símbolo" GREEN após a implementação: Testes executados: 7, Falhas: 0, Erros: 0, Ignorado: 0 Mutações, cada execução de uma cópia isolada `git arquivo HEAD' com o seu próprio target/ e seu próprio -Dmaven.repo.local (este checkout é compartilhado com outros sessões e uma suite vermelha aqui normalmente é a fase vermelha de outra pessoa): MUTATION D — executionTerminal A imutabilidade é substituída por «retorno» (os nomes do passo 6 do plano): Testes executados: 7, Falhas: 2, Erros: 0, Pular: 0 < < < Falha! **************************** < < < Falha! java.lang. AsserçãoError: Esperando código para levantar um jogável. - Não. **************************** < < < Falha! java.lang. AsserçãoError: Esperando código para levantar um jogável. - Não. MUTATION E — o guarda reescrito exatamente como o plano especifica, ou seja, um nu ler de `state' sem @PostLoad instantâneo. Esta é a evidência de que o A versão do plano está quebrada: Testes executados: 7, Falhas: 1, Erros: 0, Pular: 0 < < < Falha! **************************** < < < Falha! java.lang. AsserçãoError: Esperando código para não levantar um jogável, mas capturado **************************** BillingSyncRun null is SUCEEDED and is evidência sobre uma execução completa; não pode ser reescrita. - Não. - Não. MUTAÇÃO F — BillingWormGuard.rejeitaram: Testes executados: 7, Falhas: 4, Erros: 0, Pular: 0 < < < Falha! **************************** - O quê? Esperando código para levantar um jogável. **************************** - O quê? Esperando código para levantar um jogável. **************************** - O quê? Esperando código para levantar um jogável. **************************** - O quê? Esperando código para levantar um jogável. Os três reverteram. Suíte de biblioteca completa depois: Testes executados: 2288, Falhas: 0, Erros: 0, Ignorado: 0 SUCESSO À CONSTRUÇÃO <versão> intocada em 1.6.0. Não pressionado — Tarefa 9 aterra primeiro DDL.
· VOIPService
Webhook SMS voltado para o portador (tarefa MS1 2)
* ******************************* -- form-codificado ou JSON, guardado pelo A própria assinatura do Carrier. Porquê um novo objectivo. /api/voip/webhook não pode receber um STOP e falha silenciosamente: readTree() é o corpo sem restrição de 'consumo', cada suporte de texto posts **************************** o parse lança, a exceção é engolido e o portador é respondido 200 OK -- então ele nunca repete. Confirmado na fonte atual antes de escrever isto. Por que este caminho. ************* ************************************************************************************************************************************************************************************* e combina com startsWith, então qualquer coisa abaixo dele exige um X-Internal-Auth cabeçalho que uma operadora não pode enviar. /api/bulktext/inbound é um SIBLING de /send, is adicionado a ************* e é deliberadamente NÃO registrado em InternalAuthFilter. Assinatura Twilio: HMAC-SHA1 sobre o URL completo (texto de pesquisa incluído) mais o Parâmetros POST anexados nome-então-valor na ordem ordenada caso-sensível, base64, comparado ao X-Twilio-Assinatura com MensagemDigest.isEqual. Verificado contra A documentação de segurança de Twilio, então fixada ao vetor publicado de Twilio's proprio twilio-java RequestValidatorTest (token 12345 -> RSOYDt4T1cUTdK1PDd93/VVr8B8=), então o teste não pode passar por um algoritmo errado. Modelado no MicrosoftTeamsProvider (segredo em branco -> log + false; exceção -> falso; comparação de tempo constante); NÃO no RingCentralProvider `return true; // TODO`, e RingCentralSignnatureUtil é igual a IgnoreCase não é reutilizado. A URL assinada é reconstruída a partir do X-Forwarded-Proto/Host, não getRequestURL(). A edge termina TLS, então getRequestURL () retorna o próprio esquema do contêiner e host, que não é o que Twilio assinou -- cada assinatura seria falha e o O fracasso seria exactamente uma falsificação. O flowerroute é fechado e continua a recusar todos os trabalhos. Flowroute publica no assinatura webhook: seus documentos de mensagem de entrada especificam o URL de retorno de chamada, o aplicativo/vnd.api+json tipo de conteúdo e a forma de carga útil, e nenhuma assinatura cabeçalho, esquema HMAC ou segredo compartilhado. Não há nada para verificar, por isso mantém a falha-fechada padrão em vez de obter um esquema inventado -- um inventado um quer rejeitar todo o tráfego genuíno ou ser escrito para retornar verdadeiro, que é a superfície falsificada que esta interface existe para prevenir. Flowroute também não post form-codificado, então `consumis` aceita vnd.api+json também. Sinch, Infobip e Vonage não são implementados em ambos os métodos e registrar um aviso nomeando-se. Provas TDD ------------- Vermelho (classe ausente): [ERROR] **************************** não foi possível encontrar o símbolo símbolo: classe BulkTextInboundController Localização: classe **************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************************** Verde: Testes executados: 27, Falhas: 0, Erros: 0, Pular: 0 Mutação 1 -- aquele que o plano nomeia, e não prova nada. Plan Task 2 Passo 8 diz: make validateWebhookAssinatura padrão para `true` e aMissingWebhookSecretRejects deve ficar vermelho. Observado: Testes executados: 24, Falhas: 0, Erros: 0, Pular: 0 CONSTRUÇÃO DE SUCESSO Nada ficou vermelho. Cada test drives Twilio, que OVERRIDES o padrão, assim o falha-fechada padrão foi exercida por nenhum teste em tudo e poderia ter sido silenciosamente invertida a qualquer momento -- transformando quatro portadores de "recusa tudo" em "aceita qualquer coisa" num objectivo em que aceitar qualquer coisa permite a um estranho revogar o consentimento de qualquer consumidor. Fechado adicionando **************** que fixa o padrão em um nu implementação e sobre as quatro transportadoras reais que dependem dele. Repetir o mesmo mutação contra ela: **************************** **************************** [uma transportadora não implementada nunca deve aceitar um webhook inverificável] Esperando que o valor fosse falso, mas era verdadeiro **************************** **************************** [SinchTextProvider deve falhar fechado] Esperando que o valor fosse falso, mas era verdadeiro Mutação 2 -- aquele que realmente tem como alvo o teste chamado do plano: Em branco-authToken guarda mudou de `retorno falso` para `retorno verdadeiro'. Observado: **************************** **************************** esperado: 403 mas foi: 200 **************************** **************************** Esperando que o valor fosse falso, mas era verdadeiro Mutação 3 -- o parâmetro classificar invertido (ordem natural TreeMap -> Comparator.reverseOrder(), para mostrar que o teste do algoritmo não é vago: **************************** **************************** Esperando que o valor fosse verdadeiro, mas era falso **************************** **************************** esperado: 200 mas foi: 403 As três mutações reverteram. Conjunto completo após a reversão: Testes executados: 290, Falhas: 0, Erros: 0, Ignorado: 0 / SUCESSO CONSTRUIDO Gaps conhecidos, deliberados e registrados em tempo de execução **************************** * Uma mensagem de entrada verificada é reconhecida e LIGADA MAS NÃO RECORDADA. A hand-off para SmsKeywordService é uma TODO marcada porque essa classe chega Tarefa 3. Este endpoint não suprime nada ainda. * Resolução Org verifica instâncias ativas e coincide com o número para Contacte o Canon.fone. Não há inventário de números -- o único registro de propriedade é o único nullable FROM PHONE NUMBER no BulkTextProviderInstance, sem localizador e sem índice. Nenhuma correspondência, ou uma correspondência entre dois orgs, atribui o mensagem para ninguém e logs; supondo que um inquilino iria gravar uma revogação contra o errado, o que é pior do que não gravar. Precisa de ADD5/MS3. * InboundSms não carrega timestamp transportadora: Twilio entrada SMS webhook não Manda um. A tarefa 3 "consentedAt vem do evento portador, nunca now()" é Por conseguinte, não é satisfatório para o Twilio. Refs: **************************** Tarefa 2
· kamo-shared-library
E.164 normalizador e intérprete de palavras-chave SMS (MS1 Task 1)
Duas aulas puras sem E/S e sem estereótipo de Primavera, por isso todos os serviços podem carregá-los e SharedLibBeanSafety O teste continua verde. E164.normalize(raw, defaultRegion) -> Optional<String>. Deliberadamente estreitos e NANP-only: libphonenumber NÃO é uma dependência deste módulo e não foi adicionado para isso, porque adicionar uma dependência transitiva a uma biblioteca todos os 28 serviços load não é uma decisão que um analisador opt-out pode fazer. Aceita-se um '+' principal as-is; 10 dígitos nus com uma região NANP, ou 11 liderados por '1', tornam-se +1; qualquer coisa O outro está vazio. Recusar é o ponto -- um palpite que cai em um real, mas número diferente suprime um estranho E deixa a pessoa que enviou STOP Ainda a receber mensagens. NOT ContactCanon.phone(), que é uma chave de comparação só para os EUA (últimos 10 dígitos, levando 1 caiu) que a correspondência de linha-a-chamada da plataforma depende. Inalterado e não reutilizado aqui. **************************** -> Palavra-chave {SOP, HELP, START, DESCONHECIDO}. Vocabulário CTIA mais ARRET/AIDE. Correspondência é EXACT definir a adesão após * **************** nunca contém(). Provas TDD ------------- Vermelho (classes ausentes): [ERROR] **************************** não foi possível encontrar o símbolo [ERROR] símbolo: variável SmsKeyword Interpretador Localização: classe. Verde: Testes executados: 10, Falhas: 0, Erros: 0, Ignorado: 0 Mutação 1 -- correspondência exata alterada para **************************** (e o mesmo para Help/START). Vermelho observado: **************************** **************************** esperado: DESCONHECIDO Mas foi: **************************** **************************** [UNSTOP] esperado: INÍCIO Mas foi: A segunda falha não foi prevista pelo plano e vale a pena registrar: "UNSTOP" CONTÉM "STOP", então uma implementação de substrings lê o consumidor re-opt-in como um opt-out. contiver() não é meramente impreciso aqui, ele inverte o significado da única mensagem que desfaz uma supressão. Mutação 2 -- A versão final do E164 `return Optional.empty()` mudou para «retorno Opcional.de ("+1" + dígitos)». Vermelho observado: **************************** java.lang. AsserçãoError: Esperando um opcional vazio, mas estava contendo valor: "+112345" Ambas as mutações reverteram. Conjunto completo após a reversão: Testes executados: 2284, Falhas: 0, Erros: 0, Pular: 0 / SUCESSO CONSTRUIDO Refs: **************************** Tarefa 1
· kamo-shared-library
A linha de conexão do provedor, uma por org
BL1 Tarefa 1. Uma transliteração quase verbal do provedor de folha de pagamento enums e PayrollConectar-se com Enums, uma entidade e um repositório — sem estereótipos, então SharedLibBeanSafetyTest permanece verde e o próprio serviço (Serviço de faturamento) declara cada feijão. Novo relativo à folha de pagamento: WEBHOOK SECRET CIPHERTEXT, um per-org rotatável webhook secret na sua própria coluna (B2). Um segredo de plataforma significaria rotação após qualquer vazamento de uma única org quebra o pagamento de qualquer outra org ingestão no mesmo instante. PlatformOAuthProviderType não tem constante de QUICKBOOKS, XERO ou SQUARE — ele termina em ADP WFN(17), seus ids são persistidos e são documentados somente apêndices. Por Tarefa do plano 1 Etapa 3 instrução, todos os três navios PER ORG CREDENTIAIS com um registro nulo em vez de crescer um compartilhado Ordinal enum nesta tarefa. Isso faz do plano **************************** vago na sua usesPlatformOAuth () branch, então um quinto teste declara a consequência diretamente: nenhum tipo pode reivindicar PLATFORM OAUTH sem registro para apontar. Provas de TDD. RED (teste escrito, sem implementação): [ERROR] **************************** não foi possível encontrar o símbolo [ERROR] símbolo: variável BillingProviderType Localização: classe. [ERROR] **************************** não foi possível encontrar o símbolo [ERROR] símbolo: variável BillingAuthKind - Sim. [INFO] Testes executados: 5, Falhas: 0, Erros: 0, Pular: 0 -- in **************************** MUTATION (KAMO NAtive authKinnd NONE -> PER ORG CREDENTIAIS) — exatamente um Ensaio vermelho, que é o ponto: [ERROR] Testes executados: 5, Falhas: 1, Erros: 0, Pular: 0 < < < Falha! [ERROR] **************************** < < < Falha! **************************** esperado: NENHUMA mas foi: PER ORG CREDENCIAIS - Não. Revertida. SUÍTE FULL: Testes executados: 2271, Falhas: 0, Erros: 0, Ignorado: 0 / SUCESSO CONSTRUIDO. Executar em uma cópia isolada `git archive HEAD' com seu próprio alvo/ e seu próprio -Dmaven.repo.local, NÃO no checkout compartilhado: outra sessão foi no meio do TDD Não, não. implementação ainda), então `mvn test` há vermelho por razões não relacionadas com Esta mudança. ~/.m2 não foi escrito e a instalação do mvn não foi executada. Não empurrado — Tarefa 9 aterra o DDL primeiro.
· KlusterServices
Dê a cada extensão de kamocrm WebRTC, e porta de saída chamando
Todos os 21 membros da equipe kamocrm agora possuem uma extensão 8XXX no K1m1 PBX (Chris renumerou 7255 a 8134). Duas mudanças aqui atrás. Os blocos de endpoint WebRTC agora cobrem cada extensão de pessoal em vez do Três pessoas repararam que estavam partidas. O softphone kamo é um navegador, assim DTLS/ICE não é um realce — uma extensão sem bloco registra sobre WS e, em seguida, não tem áudio em qualquer chamada. 8888 já tinham não listados durante meses. A chamada de saída está agora encerrada: apenas as extensões listadas em Chega ao porta-bagagens. Todos mantêm a extensão para... marcação de extensão e entrada a partir do PSTN, ambos os quais funcionam através caminhos que isto não toca. A porta vive em [de-interno-costumo] em vez de na saída rota porque de-interno-xfer inclui esse contexto à frente de de-interno-adicional, então ele vê um número discado antes de 1 Sim. A própria coluna match cid da rota era a alternativa e seria precisa de uma linha de padrão duplicada por extensão permitida e nega com O "não pode ser concluído como discado" do FreePBX — uma mensagem que envia pessoal À procura de um erro de digitação num número que estava bem. A adesão é testada com DIALPLAN EXISTS contra um contexto de uma linha por pessoa, então nada meio-correspondências: 80800 e um identificador de chamada vazio ambos negar, que é a direção segura para falhar. Os três padrões de intercepção são um espelho de rota de saída PrimaryRoiute padrões de discagem e tem que Fica um. Verificado no k1m1: todos os 21 pontos de avaliação reportam ice support=true, media encription=dtls, o transporte WS e direct media=false; todos os 21 carrega o dispositivo AMPUSER e as teclas de dicas direito; o caminho permitido ainda resolve outrt-1 para ambos os padrões domésticos e 011.
· kamo-internal
ServiceJobApi.createJob, e o construtor back-link
Restaura a metade cliente da SW6c Task 2, que pousou em 157fa714 e foi largada por uma reescrita do histórico em uma sessão concorrente; a tela de criação em si sobreviveu dentro do bddd3617 e vem importando ambos desde então. `createJob' posts `CreateJobInput` no caminho base e respostas com o detalhe Projecção, para que um chamador possa adoptar o trabalho devolvido inteiro. Sete campos e nenhum coluna que o servidor possui: sem status, sem número de trabalho, sem totais, sem assinatura. SW6b removido exatamente aqueles cinco de `JobInput` após encontrar `PUT /{uid}` aceito eles verbatim, e `CreateJobInput` junta os pares de entrada do teste de contrato para um «Estatuto» adicionado apenas deste lado falha em vez de ser serializado e silenciosamente ignorada. `commerceBackHref` está documentado em sua definição: `/commerce?tab=service` nunca nomeado uma aba real, e cada chamador hoje não passa no mercado porque nenhum serviço O registo tem um.
· kamo-internal
A grade serviço-pedido e cliente ler os endpoints reais
Restaura o resto da tarefa SW6c 4, que pousou como d58a6aba e foi deixado por um histórico reescrever em uma sessão concorrente; a página de detalhes sobreviveu dentro do bddd3617 e vem importando `serviceQuoteWorkOrder` desde então. `PipelineServiceRequestsTab` chamadas `GET /service-quotes` em vez de «GerateMockServiceRequests». O solicitante, departamento, categoria, prioridade, SLA, colunas técnicas e de equipe se foram: nenhum tem uma fonte no fio, e uma coluna que torna um em dash para sempre lido como "nada gravado" em vez de "isso nunca foi real" — como 'serviceName', 'serviceId' e 'engajamento Eu tinha sobrevivido a um O ecrã inteiro é vitalício antes da SW6a. Status são os cinco valores do 'IntentStatus', não o equipamento é seis, e os botões de estado foram-se com ele: eles moveram um local array e levantou um brinde de sucesso para uma escrita que nunca deixou o navegador. Criar envia exatamente uma oferta e diz isso. `CreateServiceQuoteRequest` anuncia o serviço ‘List<Long>OfferingUids’ e o serviço mantém apenas `.get(0)’, Em silêncio. O botão New costumava apontar para... não existe; abre uma janela. `marketId` não pode filtrar esta lista, então o `data.filter(() => true)` que se vestiu como um filtro se foi e uma legenda diz que cada mercado mostra as mesmas linhas. `posApi.ServiceQuote` declarou `serviceId' e `serviceName` contra um envio de DTO «serviceOfferingUid» e «serviceOfferingName» — o mesmo defeito no irmão tipo — e agora espelhos «ServiceQuoteDTO». Os membros fantasma do WorkOrder são corrigido também, porque um deles acaba por ser de carga: «AceiteServiceQuote» responde com o «FulfillmentOrder», cujo uid não é o 'ServiceJob's, e `engagmentUid' é o único fio atravessado. `serviceQuoteWorkOrder.ts` segue-o, e seus pinos de teste a parte que funcionaria numa base de dados sem sementes e parada na produção — «JsSafeLongSerializer» cita uma 'Long' por valor, então o mesmo campo chega como uma string em um id de 19 dígitos e um número num pequeno.
· kamo-internal
Capturas de tela E vídeos em um relatório de erros
Repórteres estavam descrevendo gravações de tela em prosa e, em seguida, postando o arquivo em um bate-papo, porque o formulário do relatório só tomou stils. Agora leva MP4, MOV e WebM ao lado dos quatro formatos de imagem, até 100 MB cada, dentro do mesmo arquivo de dez Cap. A zona de largada já não implementa as suas próprias regras. Dá o lote à `ingestFiles` contra um novo SYSTEM BUG ATTACHMENT POLICY, o mesmo caminho que um chat mensagem e um e-mail passar, que é o que recebe por classe limites de tamanho, um pasta abandonada reconhecida como uma pasta, uma gota duplicada ignorada e uma cópia intervalo de células de planilha deixadas como texto em vez de silenciosamente se tornar um bitmap. Um arquivo recusado não mais descarta o resto do lote com ele. Os URLs dos objetos se movem para 'useDraftThumbnails', então nada aqui tem que se lembrar de revogar um. O vídeo faz o que já faz em qualquer outro lugar do produto: um cartaz frame desenhado com o truque `#t=0.1` e um emblema de jogo no azulejo, controles do navegador na caixa de luz. As miniaturas são apenas metadados, por isso uma gaveta cheia de peças não puxa clipes inteiros para baixo — que é o que o suporte Range acrescentou MediaService é para, e por que este proxy passa Range e 206 através em ambos Direcções em vez de as desmoronar. A rota de upload pára de processar o corpo com formData(). Que manteve o arquivo inteiro neste processo, é possível sobreviver para uma imagem de 10 MB e não para uma imagem de 100 MB gravação; agora canaliza através do nó:http como o upload do chat faz, para o mesmo razão do tempo limite. O botão conta os ficheiros enquanto funciona — obter relatórios não Carregar o progresso, e "Carregar..." sentado ali durante minutos, parece um enforcamento. Enviar apenas para AI lista imagens. Uma sessão lê uma imagem e não consegue ver um vídeo, assim oferecer alguém estaria oferecendo para falhar; o diálogo diz quantos estão ficando atrás, e SecurityService recusa um pelo nome se um console velho o enviar.
· MediaService
Anexar um vídeo a um relatório de erros, não apenas uma captura de tela
Um ainda não pode mostrar os três cliques antes que a coisa quebrou, então os repórteres foram descrever gravações em prosa e postar o arquivo em um chat. A Endpoint anexo agora leva MP4, MOV e WebM ao lado dos quatro formatos de imagem. - O cheiro mágico cresceu metade do vídeo. Uma caixa ftyp só diz "base ISO mídia", então a marca é compatível com uma lista de allowlist: HEIC, AVIF e transporte 3GP a mesma caixa, e um ainda armazenado como vídeo/mp4 atinge um elemento <video> que mostra um jogador morto. Matroska é aceita apenas quando seu EBML DocType diz "webm" — uma simples .mkv joga em nenhum navegador onde o console é executado. - Vídeos têm seu próprio teto de 100 MB; imagens manter 10 MB. A tampa de dez arquivos Os dois tipos. Ordem dos controlos é por custo: contagem, em seguida, o teto externo, em seguida, o formato, então o próprio teto do tipo — snilling primeiro leria um arquivo que era Será sempre recusado. - Um upload sem nome tem o nome dos bytes. O objeto armazenado é chaveado por essa extensão, então chamando uma gravação colada screenshot.png colocaria .png em um MP4 e entregue ao navegador um <vídeo> que não vai começar. - O endpoint do conteúdo responde Pedidos de intervalo, que não é opcional uma vez vídeos são anexáveis: um <video> não pode procurar sem eles, Safari abre cada vídeo com "bytes=0-1" e lê um 200 como "este servidor não faz vídeo", e um A miniatura da galeria é desenhada a partir de uma obtenção apenas de metadados em vez da totalidade Clip. Espelhos de análise ImagingProxyController. O RangeStream está agora fechado quando a resposta termina; nada estava fechando antes.
· kamo-internal
Restaurar a página de Citações de tubulação e conversão de citações de fio
`POST /quotes/{uid}/convert` enviado em SW4 e nada lhe chamou: `quotesApi` tinha dez métodos e `convert` não estava entre eles, então o ponto final estava vivo e inatingível. Isso coloca uma sub-tab Citações de volta no SERVIÇO pipeline e ofertas Convert em uma citação ACCEPTED. A razão pela qual isso precisava de um teste antes de precisar de código: `subTabLabels` é um `Record<PipelineSubTabId, string>`, então um rótulo faltando é um erro de compilação, mas `renderSubContent` decidiu o que desenhar com quatro `se (id") retornando X; devolver Y; ` cadeias, e que segue `retorno Y` responde a todos os id ninguém Pensei nisso. Adicionando um ID, por isso, coloque uma nova aba na tela que silenciosamente desenhou a lista de compromissos, em todas as quatro verticais, e verificação de tipo. Este é o defeito `pipelineSubTabs.ts` próprio cabeçalho narra já ter enviado uma vez. Afirma, contra a fonte, que A entrada 'SUB TAB IDS' é nomeada em cada vertical. Estava vermelho antes de qualquer ramo alterado — em «compromissos», em todas as quatro verticais, porque o Nunca o nomeie. Excluindo um 'caso' do código acabado deixa `tsc --noEmit` silencioso e vira o teste vermelho, que é todo o ponto dele existente. As correntes são agora interruptores sem 'padrão'. Uma superfície vertical sem citações ainda escreve `case "quotes":` além de seu caso de compromissos, então o próximo id adicionado faz alguém escolher quatro vezes em vez de herdar uma resposta. Apenas as listas de SERVIÇO «quotas» em «SUB TABS BY VERTICAL», por isso só o SERVIÇO mostra o tab — uma página de Citações no ALUGAMENTO que renderizou Reservas seria o mesmo silêncio erro em um novo lugar. A ordem de retirada ainda vem de `SUB TAB IDS` sozinho: o o novo mapa é um conjunto, e `Tab value' permanece o índice `SUB TAB IDS` para omitir uma aba não pode renumerar os outros. `subIndexForVertical` braça uma ligação `?sub=quotes` que cai em uma vertical sem a aba, que MUI de outra forma renderizaria como um seleção vazia sobre o conteúdo de retrocesso. §quotesApi.convert` mantém 409 e 404 separados, porque eles dizem coisas diferentes: 409 é "o estado desta citação está errado, e a razão do servidor é a metade útil", 404 é "desapareceu desta orgia". Contando a alguém um registro que eles estão olhando não foi encontrado quando a verdade é "não aceita ainda" envia-os à caça de dados problema que não existe, então apenas 404 força uma recarga. 'QuoteConversion.orderUid' é uma 'string'. «Ordem extende BaseEntity», cujo ID é um `INT8 DEFAULT unique rowid()`; o servidor já declara o campo como um String, e o proxy seguinte reparsa o corpo em Node um hop upstream do navegador, então um int64 nu seria redondo antes de qualquer código de cliente poderia vê-lo. `total` é digitado `string número' e lido através de `Number(): é um `BigDecimal`, e `HttpWireJacksonConfig` só se diz `Long`. Converter é oferecido em ACCEPTED e nada mais; aceitar e decodificador ficar no página tokenized do cliente, onde a aceitação de uma ordem repousa em é realmente dado. Produção detém 15 citações, todas DRAFT, então o botão está atualmente visível em nenhum deles.
· kamo-marketing
Nosso preço vem do catálogo, e não pode ser digitado novamente
As páginas de comparação codificaram KamoCRM em $29 Starter e $64 Business enquanto o catálogo vendeu-os em $34 e $72. Nove arquivos, quarenta e dois menções, e o a construção era verde o tempo todo — um preço em prosa é apenas prosa. Eles carregam os tokens {starterMédia} / {businessMédia} agora, resolvidos na renderização a partir do mesmo ponto final do catálogo a página de preços lê, compartilhando sua entrada de cache através de um URL idêntico e revalidate. Reprimir um plano no catálogo é o Apenas edite o que alguém faz. O formulário multiplicador cobre os totais que a cópia calcula em voz alta — {negócios mensais*10} para uma equipa de dez lugares, {negócios mensais*50*12} para o Comparação da Salesforce a três anos. Aqueles foram os piores dos codificados números: citado contra o projeto de lei de um concorrente, lido como autoritário, e errado pelo facto de o preço unitário por trás deles estar errado. Substituição corre após a localização, então páginas traduzidas obter números ao vivo também, e é aplicado sempre que os dados do concorrente são lidos em vez de apenas onde que, actualmente, tem de ser — um domínio que começa a mencionar as verbas mais tarde deve Não tenho que me lembrar de opt-in. check-price-tokens guardas ambas as direções: nossa figura digitado como um literal, e um símbolo que não iria resolver. Uma nave de digitação como visível lixo, o que é pior que um número velho. Os preços dos concorrentes permanecem literais, que é o ponto de uma comparação — o guarda diz-lhes separadamente pela proximidade para uma menção de Kamo e por se a quantidade é um ponto final de um intervalo. FALLBACK KAMO PRICES é o que o CI renderiza, já que a chave API está ausente E estas páginas pré-render. É o valor do catálogo ao vivo, deliberadamente: Um retrocesso seria o mesmo insecto num lugar novo.
· KlusterServices
Aviso de chamada em chamadas de extensão para extensão
Completa o terceiro caminho de chamada. Chamadas de funcionários nunca tocam no atendente — vão de-interno -> ext-local, onde o mostrador pertence ao FreePBX - assim [de-interno-custom] agora intercepta extensões reais e as encaminha através [kamo-internal-connect], que possui seu Dial e pode carregar aA(). A intercepção só é segura por causa de duas coisas verificadas primeiro, não presumidas: - do noxfer interno, que procede a consultas internas ANTES do xfer interno, não resolve extensões. Se tivesse feito isso, de-interno-costume nunca iria Foi alcançado e isso não teria feito nada silenciosamente. - a partir de-interno-costumo é incluído por a partir de-interno-xfer antes de de-interno-adicional, então ele vê extensões primeiro. Qualquer coisa que não é uma extensão real é entregue de volta para o FreePBX, então nada mais no plano interno muda. DIALPLAN EXISTS é testado em vez do que presumiu, porque um Goto nu para uma extensão ausente desliga na chamada em vez de jogar "não em serviço". A regressão testou os três casos internos: - 8912 (real, offline) -> kamo-internal-connect, Dial PJSIP/8912,15,aA(...), Userfield DS:CHANUNAVAIL/VM:default, cai para o Voicemail - 8080 (real, online) -> Discar PJSIP/8080,15,aA(...), telefone tocou - 7777 (ext-test) -> ainda atinge o ext-test e volta através do pstn - 3000 (número ruim) -> ainda atinge número ruim. Ele não escreve nenhuma linha CDR, que não é uma falha: o número errado faz o ResetCDR() e CDR PROP( disable)=verdadeiro por design. Ambas as partes ouvem a gravação do AGENT em chamadas internas: não há cliente na chamada, então o texto voltado para o cliente seria endereçado a alguém que é não presente. Cada lado é o seu próprio global, então isso é uma mudança de uma linha. No-resposta interna joga vm-nobodyavail quando a extensão não tem caixa de correio — deliberadamente NÃO a fila do cliente, que é onde os chamados de entrada caem para trás.
· SecurityService
POST /service-jobs, fechado em GESTÃO SERVICE WORK
O verbo criar a superfície de ordem de trabalho nunca teve. app/commerce/service/new é um Dezassete linhas de substituição porque não havia nada para chamar. Gated on MANAGEM SERVICE WORK como qualquer outro escrever aqui. É uma CRIANÇA de VER SERVICE WORK, então aceitar vista ainda iria olhar fechado enquanto admitindo qualquer um que pode simplesmente ver o tabuleiro — ServiceJobRightsGateTest conduz o novo manipulador com uma sessão somente de visualização e afirma que o serviço nunca é alcançado. O inquilino sai da sessão e chega ao serviço como seu primeiro argumento, onde ela se torna um predicado dentro da consulta; customerUid nome nada sobre uma organização. Um mercado ou uma conta pertencente a outro inquilino é "não encontrado" e responde 404, indistinguível de um que Nunca existiu. Os componentes do CreateJobInput são asseverados: nenhum estado, nenhum trabalhoNúmero, nenhum total, nenhum Assinado. A asserção verbo-set agora espera quatro POSTs, então o proxy Próximo para esta superfície ainda tem que exportar os cinco verbos.
· kamo-shared-library
CreateJob, para que uma ordem de trabalho possa ser levantada sem uma ordem
Faltava o verbo a superfície serviço-trabalho. Até agora a única coisa ligada a plataforma que poderia produzir uma linha ServiceJob foi * ************* por uma ordem paga — é por isso Cada mesa de serviço-trabalho tem zero linhas. CreateJobInput não carrega nenhuma coluna do servidor: nenhum estado, nenhum trabalhoNúmero, nenhum total, sem assinaturaOff/signoff Na SW6b levou exatamente aqueles cinco fora JobInput depois descobrindo PUT /{uid} aceitou-os literalmente; um caminho de criação que os levou seria A mesma porta com um nome diferente. O compromisso de origem permanece nulo, deliberadamente. processOrderPaid abre com `if (order.getCommitment() != null) return;`, então um compromisso inventado aqui para preencher uma coluna nulável iria desativar toda a ordem paga a jusante se esta tarefa Mais tarde juntou-se a uma ordem. Um ServiceVisit, sequência n.o 1, SCHEDULED, nenhum evento de calendário e nenhum cessionário — os mesmos quatro fatos criamDeCommitment escreve, então um trabalho parece o mesmo no entanto Foi criada. O mercado e o cliente são resolvidos com o predicado org na consulta (findByIdAndOrganizaçãoId / findByUidAndOrganização), o par SW4 usado em CitaçãoService.create. O mercado não pode ser armazenado — não existe uma coluna de mercado sobre ServiceJob e nenhum no Engajamento — então seu único efeito durável é o engajamento Tipo, através de uma citação.
· kamo-internal
VER/MANAGEM ORDERS e VER/MANAGEMENT PRICING
A metade do cliente a fechar o buraco de mutação da ordem. Serviço de Segurança agora portões 39 ordem, preços e objectivos fiscais sobre quatro novos direitos; estes são os dois espelhos do Java enum que tem que mover com ele, ou os direitos existem sobre o servidor e são ungrantable da UI. direitosHierarquia.ts ganha quatro arestas — VER ORDERS e VER PRICAR sob ACESS COMMERCE, GESTÃO ORDERS sob o primeiro e GESTÃO PRICAÇÃO sob o segundo — que é o que faz o editor cascata concordar com o servidor normalização. RoleRightType.ts ganha as quatro constantes E suas quatro valores entradas; uma constante deixada fora desse array é invisível para tudo que itera o enum, e os editores de permissão iteram. Nenhuma mudança de seção de permissão foi necessária: todos os quatro carregam ServiceType. POS e Linha de comércio já existe em ambas as permissõesAppSections.ts e sua linha em linha duplicado em SecurityRoleManager.tsx, para que eles sejam concedidos na chegada. Ambos comentários são corrigidos para dizer isso, em vez de deixar a contagem para trás. Nenhuma mudança de dicionário também — etiquetas certas são a string Java `display` verbatim espelhado, não uma chave i18n. Service Work's cinco não acrescentou nada a Kamo-tradução-dicionário pela mesma razão. Paridade total 189 -> 193. Verificado contra a fonte Java por DireitosHierarquiaParidade e papelDireitoMirrorParidade, ambos lidos kamo-shared-library diretamente; suite completa 278 arquivos / 3603 testes, 0 falhas.
· kamo-shared-library
VER/MANAGEM ORDERS e VER/MANAGEME PRICING, IDs 273-276
Nenhum direito de mutação de ordem existia em qualquer lugar. Endpoints de ordem do POSController e todos os preços e manipulador de impostos no CommerceMarketController ler o org fora do sessão e não verificou mais nada, então qualquer membro autenticado de qualquer papel poderia criar, editar, reembolsar e re-status pedidos — e reescrever listas de preços, impostos classes, zonas fiscais, taxas de imposto e níveis de clientes — dentro das suas próprias org. Nunca cross-tenant; papel simplesmente não significou nada nessas superfícies. Quatro direitos em vez de três: GERIR pendura fora VER em ambos os pares de modo que mudar uma ordem ou um preço não pode ser concedido sem a capacidade de ler o que está sendo alterado, e de modo que um endpoint de escrita aceitando o direito Detectável degradar em vez de invisível. VER PRICAR não é decoração — ler o que as taxas de imposto e as listas de preços SÃO um comum necessidade de financiamento e auditoria que não devem ter o poder de os alterar. Max id foi novamente verificado contra origem / main imediatamente antes de escrever: 272. A O id duplicado é uma falha do Collectors.toMap em um inicializador estático, ou seja, cada serviço que carrega este enum pára de arrancar. Contagem de pinos movimento 263/189/35/39 -> 267/193/35/39: todas as quatro são crianças e ACESS COMMERCE já era uma raiz-com-crianças, então apenas dois números se movem. Nota: este commit também carrega uma remoção de comentários de cinco linhas em RoleRightType.java feito por uma sessão concorrente nesta saída. `git commit -- only` toma o estado da árvore de trabalho do caminho; a mudança é deles, não minha, e é preservada aqui em vez de descartado.
· KlusterServices
Aviso de chamada gravado em extensões dializadas diretas também
Um chamador que disca uma extensão sobre a saudação agora ouve o aviso como Bem. Esse caminho passou de-did-direct, onde o Dial pertence a O ext-local do FreePBX, então A() não pôde ser anexado a ele — o chamador alcançado Um agente sem aviso prévio. Mostradores diretos agora roteiam através de [kamo-direct-connect], que possui seu próprio mostrador e, portanto, carrega aA(agent:client) exatamente como o caminho da fila. O custo é reimplementando as duas coisas que o ext-local deu de graça, ambas preservadas aqui: - temporizador de anel: AMPUSER/<ext>/temporizador de anel por extensão, onde definido, caso contrário FreePBX's RINGTIMER DEFAULT (15s), assim que uma extensão anéis por tanto tempo como costumava - sem resposta: a caixa de correio da extensão se tiver uma, caso contrário, de volta ao fila para uma pessoa viva, em vez de acabar com a chamada. A fila Tempo de 10 minutos ainda os leva para a caixa de correio principal eventualmente. Mantido separado de [kamo-agent-connect] deliberadamente. Esse contexto é alcançado por membros da fila, onde uma não-resposta deve voltar para app queue para que possa tentar o próximo agente; adicionar um retorno do voicemail lá seria soltar um chamador QUEUED na caixa de correio pessoal de um agente. Verificado ao vivo: 7255 (sem caixa de correio, offline) tocaram 15s depois voltaram para a fila, que ligou para ambos os agentes e ligou-se em 8080. A selecção dos ramos está agora visível no CDR via userfield — a execução 8912 gravada DS:CHANUNAVAIL/VM:default, Ou seja, caiu corretamente para o Voicemail. Asterisco então registrado "Nenhum áudio disponível no Local/...;2", que é o canal de teste sem mídia, não o dialplan: um canal de `canal originar ... aplicativo Wait` não tem mídia caminho para reprodução, a mesma razão pela qual o MixMonitor devolve um ficheiro vazio nele.
· KlusterServices
Aviso de chamada gravada para ambas as partes ao mesmo tempo, em seguida, um tom de ponte
Quando um agente responde a uma chamada em fila de espera, o chamador ouve o aviso do cliente e o agente ouve o agente notar ao mesmo tempo, em seguida, uma marca de tom compartilhada 1000Hz No momento em que estão ligados. Os membros da fila agora apontam para canais locais em [kamo-agent-connect] em vez de reta em PJSIP/<ext>, então a perna final é um Dial() — Dial's A(x:y) é o A única coisa em Asterisco que reproduz áudio diferente para cada parte SIMULTANEOUSMENTE e depois pontes. A própria locução da Fila não é um substituto: é tocada para Só o agente, e antes de ligar enquanto o ouvinte ainda está em espera. O 'a' minúsculo antes de A() é o linchpin e não é opcional. Sem ele O mostrador deixa o canal sem resposta até que o anúncio termine. app queue nunca pontes e o arquivo do lado do chamador toca em um canal ninguém é Estou a ouvir. fila log tornou isso mensurável: CONNECT em 7s (após o áudio) sem «a», conecte-se a zero com ela. Provado por gravar a perna do chamador com hold música silenciada e cruz- correlacionando o áudio capturado: vs arquivo cliente 0. 833 vs arquivo agente 0.068 vs hold music 0.044 e o tom de 1000Hz detectado no próprio áudio do chamador em t=5,75s. Ambos os arquivos são acolchoados a exatamente 7.53s (a fala do cliente é 0.32s mais curta, então ele carrega a diferença como silêncio) e termina com o mesmo tom, então os tons disparam juntos e nenhuma das partes é liberada cedo. O nível de tonalidade corresponde à fala (RMS 3082 vs ~3200) — o filtro seno do ffmpeg está muito abaixo da escala completa, por isso é necessário calibrando em vez de adivinhar. Limitação conhecida: isto cobre o caminho QUEUE. Um chamador que disca uma extensão diretamente sobre a saudação ainda chega de-did-direto e não ouve nenhum aviso; roteando que através deste contexto iria trocar o anel por extensão do FreePBX temporizador e retorno do voicemail.
· InitializerService
4 GB por assento em cada plano, e assentos em vez de usuários
O armazenamento deixa de ser um diferenciador de níveis. A escada 2/4/5 coloca um gigabyte entre Starter e Business em uma página que teve que explicar o que "conjuntou" significa, então agora é 4 GB por assento em todos os lugares: declarado uma vez no Free, herdado pelas cartas acima. Nenhum código de exibição precisava de uma caixa especial — as cartas renderizar o delta de um nível sobre o de baixo, então filas iguais caem por conta própria. Enterprise perde "150 GB para a organização". ArmazenamentoQuotaService somente lê numeric value e multiplica-o por banco count, de modo que a linha foi faturada como 150 GB PE SEAT enquanto a página prometia uma piscina plana. A prosa e o código Discordou e o código ganhou silenciosamente. O recurso USERS é rotulado como "Seats" com unidade "seats". O CÓDIGO da funcionalidade é Propositadamente inalterado — corresponde ao nome em toda a frota. Também repara o plano interno do proprietário, encontrado ao verificar ninguém foi empurrado sobre o novo teto: ambas as linhas medidas transportadas NULL com numeric is unlimited = false, que planStorage lê como um zero duro. O plano cuja descrição lê "Covers every feature" tinha direito a nenhum armazenamento e Um lugar e o kamoclub segura-o em 99 lugares.
· kamo-internal
As seis páginas de ordem de trabalho persistem
SW6b Tarefa 4. Trabalho, partes, tempo, fornecedores, assinatura e notas foram Reagir 'useState' em 'commerce/service/[id]' e perdeu tudo em recarga. Eles agora escrever para os terminais SW6b Tarefa 3 desembarcado. O cliente reflete as três mudanças de quebra em `ServiceJobApi.java`: * «JobInput» perde «trabalhoTotal», «materialTotal», «total», «assinado» e "SignoffAt". Os totais são recomputados do lado do servidor das linhas do trabalho cada linha escreve, e o sinal-off passa por `PATCH /{uid}/signoff`, que carimba o seu próprio relógio. Deixá-los na atualização geral era uma segunda porta em cinco colunas o servidor possui, e a segunda porta é a única ninguém Verificações. * "JobView" ganha "linhas", "notas" e "vendores". ** Null significa "não carregado", vazio significa "nenhum"** — 'listJobs' deixa todos os três nulos para que a grade não execute três consultas por linha, e cada aba indica os dois separados em vez de desenhar "sem linhas" sobre um trabalho que tem doze. * O proxy exporta todos os cinco verbos. POST e DELETE estavam deliberadamente ausentes enquanto um trabalho não teve filhos; uma linha e um engajamento do vendedor são criados e removido aqui, então ambos estão mapeados agora. **************************** afirma o conjunto em ambas as direções e foi ampliado com eles. Cada mutação responde com todo o detalhe `JobView' e cada mão manipuladora que vai directamente para o 'adopto'. Nada funde o efeito de uma escrita à mão: os três os totais são do servidor e nenhuma aritmética do cliente os reproduz. «adopt» NÃO re-semeia deliberadamente o rascunho de assinatura — a página da SW6a re-semeia todos os campo local de cada resposta, assim adicionando uma linha silenciosamente descartado o que o engenheiro datilografou o write-up de conclusão. Apenas a carga e um Gravar com sucesso a nova semente. A aba notas é portada a partir da marcação salva fora da ordem de trabalho morta diálogo, reutilizando suas cinco chaves existentes. Duas partidas, ambas registadas no código: o autor da nota é um ID e a aba diz "Você" para o membro atual e omite a linha do autor para qualquer outra pessoa, porque nenhum endpoint resolve uma lote de IDs de membros para nomes e um número nu de 19 dígitos sob uma rubrica que diz "autor" não ajuda ninguém; e o '.reverse()' do salvamento é abandonado, porque o servidor já responde o mais novo e invertendo que coloca o mais antigo Top. `commerceVendorDirectory.ts` é novo e existe porque `posApi.getVendors()` não pode ser usado para o seletor de fornecedores: `POSController.getVendors' respostas com a entidade `Vendor`, cujo `uid` é um `unique rowid()` de ~19 dígitos escrito como um nu número JSON, por isso `JSON.parse ` roda-lo antes de qualquer componente vê-lo e «posApi.Vendedor» declara devidamente «uid: número». Ligar um vendedor com um arredondado id nomeia uma linha que não existe. Este módulo lê a resposta como texto e cita valores longos de `"uid"` antes de analisar. A correção estreita é um DTO que envia o id como uma string, como 'ServiceJobApi' faz; até que exista este é o Uma leitura honesta. O dicionário pousou primeiro: kamo-tradução-dicionário 0eaab740.
· KlusterServices
Passe o IP público anunciado para o pod de mídia
· SecurityService
Oito endpoints de ordem de trabalho para linhas, notas, fornecedores, desligamento e o relógio
SW6b Tarefa 3, controlador metade. POST/PUT/DELETE /{jobUid}/lines/..., POST /{jobUid}/notas, POST/DELETE /{jobUid}/vendores/..., PATCH /{jobUid}/signoff e PATCH ************************* Lê ficar em VER SERVICE WORK; cada um dos oito é MANAGEM SERVICE WORK. MANAGEM SERVICE WORK é uma CRIANÇA DE VER SERVICE WORK, então uma mutação clonada de o manipulador de leitura ao lado aceita ver em uma escrita e ainda parece fechado em revisão. ServiceJobRightsGateTest drives todos os doze manipuladores com uma view-only sessão e afirma que o serviço nunca é alcançado -- um manipulador que atua e em seguida, checks retorna 403 tendo já escrito a linha. Ambos os IDs atingem o serviço em cada ponto final da criança, e o serviço resolve a criança com o emprego uid e o org como predicados em uma consulta. Sem a pai um, DELETE /{jobUid}/lines/{lineUid} atinge qualquer linha no org através de uma URL que parece inteiramente correta. O autor de uma nota é memberId(req) e nunca o corpo: NoteInput carrega um corpo e nada mais, em um registro sem caminho de edição precisamente para que o que ele diz Fica o que foi dito. Sair de uma visita que ninguém marcou é um 409 nomeando o estado da visita, Não um 400 e não um 500 -- o pedido é bem formado ea linha existe, e O que está errado é o estado. Engaging o mesmo vendedor duas vezes é um 409 também. O teste agora lê os nomes do manipulador mapeado fora do controlador e afirma isso classe conduz cada um deles, então um nono ponto final adicionado e esquecido aqui falha em vez de ser coberto por nada. A asserção verbo-set é ampliada para todos os cinco verbos: o proxy seguinte para esta superfície deve exportar GET, POST, PUT, PATCH e DELETE ou todas as respostas de salvamento 405 do Next sem nada no Registos de servidores. Teste de mutação, 19 mutações, cada uma distinta falha chamada: a verificação certa suprimido em cada um dos oito, MANAGEM trocado por VER em cada um dos oito, o verificação movida após a chamada de serviço, a sessão org substituída por uma literal, e um verbo mudou.

Seu universo está esperando.
Comece gratuitamente em minutos.Adicione produtos sempre que estiver pronto — sem migração, sem novos logins.
Gratuito para sempre — não é necessário cartão de crédito